Bug 21683: Remove accountlines.accountno
[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 => 6;
23 use Test::MockModule;
24 use t::lib::Mocks;
25 use t::lib::TestBuilder;
26
27 use Koha::AuthUtils;
28
29 BEGIN {
30     use_ok('C4::ILSDI::Services');
31 }
32
33 my $schema  = Koha::Database->schema;
34 my $dbh     = C4::Context->dbh;
35 my $builder = t::lib::TestBuilder->new;
36
37 subtest 'AuthenticatePatron test' => sub {
38
39     plan tests => 14;
40
41     $schema->storage->txn_begin;
42
43     my $plain_password = 'tomasito';
44
45     $builder->build({
46         source => 'Borrower',
47         value => {
48             cardnumber => undef,
49         }
50     });
51
52     my $borrower = $builder->build({
53         source => 'Borrower',
54         value  => {
55             cardnumber => undef,
56             password => Koha::AuthUtils::hash_password( $plain_password )
57         }
58     });
59
60     my $query = new CGI;
61     $query->param( 'username', $borrower->{userid});
62     $query->param( 'password', $plain_password);
63
64     my $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
65     is( $reply->{id}, $borrower->{borrowernumber}, "userid and password - Patron authenticated" );
66     is( $reply->{code}, undef, "Error code undef");
67
68     $query->param('password','ilsdi-passworD');
69     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
70     is( $reply->{code}, 'PatronNotFound', "userid and wrong password - PatronNotFound" );
71     is( $reply->{id}, undef, "id undef");
72
73     $query->param( 'password', $plain_password );
74     $query->param( 'username', 'wrong-ilsdi-useriD' );
75     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
76     is( $reply->{code}, 'PatronNotFound', "non-existing userid - PatronNotFound" );
77     is( $reply->{id}, undef, "id undef");
78
79     $query->param( 'username', uc( $borrower->{userid} ));
80     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
81     is( $reply->{id}, $borrower->{borrowernumber}, "userid is not case sensitive - Patron authenticated" );
82     is( $reply->{code}, undef, "Error code undef");
83
84     $query->param( 'username', $borrower->{cardnumber} );
85     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
86     is( $reply->{id}, $borrower->{borrowernumber}, "cardnumber and password - Patron authenticated" );
87     is( $reply->{code}, undef, "Error code undef" );
88
89     $query->param( 'password', 'ilsdi-passworD' );
90     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
91     is( $reply->{code}, 'PatronNotFound', "cardnumber and wrong password - PatronNotFount" );
92     is( $reply->{id}, undef, "id undef" );
93
94     $query->param( 'username', 'randomcardnumber1234' );
95     $query->param( 'password', $plain_password );
96     $reply = C4::ILSDI::Services::AuthenticatePatron($query);
97     is( $reply->{code}, 'PatronNotFound', "non-existing cardnumer/userid - PatronNotFound" );
98     is( $reply->{id}, undef, "id undef");
99
100     $schema->storage->txn_rollback;
101 };
102
103
104 subtest 'GetPatronInfo/GetBorrowerAttributes test for extended patron attributes' => sub {
105
106     plan tests => 5;
107
108     $schema->storage->txn_begin;
109
110     $schema->resultset( 'Issue' )->delete_all;
111     $schema->resultset( 'Borrower' )->delete_all;
112     $schema->resultset( 'BorrowerAttribute' )->delete_all;
113     $schema->resultset( 'BorrowerAttributeType' )->delete_all;
114     $schema->resultset( 'Category' )->delete_all;
115     $schema->resultset( 'Item' )->delete_all; # 'Branch' deps. on this
116     $schema->resultset( 'Club' )->delete_all;
117     $schema->resultset( 'Branch' )->delete_all;
118
119     # Configure Koha to enable ILS-DI server and extended attributes:
120     t::lib::Mocks::mock_preference( 'ILS-DI', 1 );
121     t::lib::Mocks::mock_preference( 'ExtendedPatronAttributes', 1 );
122
123     # Set up a library/branch for our user to belong to:
124     my $lib = $builder->build( {
125         source => 'Branch',
126         value => {
127             branchcode => 'T_ILSDI',
128         }
129     } );
130
131     # Create a new category for user to belong to:
132     my $cat = $builder->build( {
133         source => 'Category',
134         value  => {
135             category_type                 => 'A',
136             BlockExpiredPatronOpacActions => -1,
137         }
138     } );
139
140     # Create a new attribute type:
141     my $attr_type = $builder->build( {
142         source => 'BorrowerAttributeType',
143         value  => {
144             code                      => 'HIDEME',
145             opac_display              => 0,
146             authorised_value_category => '',
147             class                     => '',
148         }
149     } );
150     my $attr_type_visible = $builder->build( {
151         source => 'BorrowerAttributeType',
152         value  => {
153             code                      => 'SHOWME',
154             opac_display              => 1,
155             authorised_value_category => '',
156             class                     => '',
157         }
158     } );
159
160     # Create a new user:
161     my $brwr = $builder->build( {
162         source => 'Borrower',
163         value  => {
164             categorycode => $cat->{'categorycode'},
165             branchcode   => $lib->{'branchcode'},
166         }
167     } );
168
169     # Authorised value:
170     my $auth = $builder->build( {
171         source => 'AuthorisedValue',
172         value  => {
173             category => $cat->{'categorycode'}
174         }
175     } );
176
177     # Set the new attribute for our user:
178     my $attr_hidden = $builder->build( {
179         source => 'BorrowerAttribute',
180         value  => {
181             borrowernumber => $brwr->{'borrowernumber'},
182             code           => $attr_type->{'code'},
183             attribute      => '1337 hidden',
184         }
185     } );
186     my $attr_shown = $builder->build( {
187         source => 'BorrowerAttribute',
188         value  => {
189             borrowernumber => $brwr->{'borrowernumber'},
190             code           => $attr_type_visible->{'code'},
191             attribute      => '1337 shown',
192         }
193     } );
194
195     my $fine = $builder->build(
196         {
197             source => 'Accountline',
198             value  => {
199                 borrowernumber    => $brwr->{borrowernumber},
200                 accounttype       => 'xxx',
201                 amountoutstanding => 10
202             }
203         }
204     );
205
206     # Prepare and send web request for IL-SDI server:
207     my $query = new CGI;
208     $query->param( 'service', 'GetPatronInfo' );
209     $query->param( 'patron_id', $brwr->{'borrowernumber'} );
210     $query->param( 'show_attributes', '1' );
211     $query->param( 'show_fines', '1' );
212
213     my $reply = C4::ILSDI::Services::GetPatronInfo( $query );
214
215     # Build a structure for comparison:
216     my $cmp = {
217         category_code     => $attr_type_visible->{'category_code'},
218         class             => $attr_type_visible->{'class'},
219         code              => $attr_shown->{'code'},
220         description       => $attr_type_visible->{'description'},
221         display_checkout  => $attr_type_visible->{'display_checkout'},
222         value             => $attr_shown->{'attribute'},
223         value_description => undef,
224     };
225
226     is( $reply->{'charges'}, '10.00',
227         'The \'charges\' attribute should be correctly filled (bug 17836)' );
228
229     is( scalar( @{$reply->{fines}->{fine}}), 1, 'There should be only 1 account line');
230     is(
231         $reply->{fines}->{fine}->[0]->{accountlines_id},
232         $fine->{accountlines_id},
233         "The accountline should be the correct one"
234     );
235
236     # Check results:
237     is_deeply( $reply->{'attributes'}, [ $cmp ], 'Test GetPatronInfo - show_attributes parameter' );
238
239     ok( exists $reply->{is_expired}, 'There should be the is_expired information');
240
241     # Cleanup
242     $schema->storage->txn_rollback;
243 };
244
245
246 subtest 'LookupPatron test' => sub {
247
248     plan tests => 9;
249
250     $schema->storage->txn_begin;
251
252     $schema->resultset( 'Issue' )->delete_all;
253     $schema->resultset( 'Borrower' )->delete_all;
254     $schema->resultset( 'BorrowerAttribute' )->delete_all;
255     $schema->resultset( 'BorrowerAttributeType' )->delete_all;
256     $schema->resultset( 'Category' )->delete_all;
257     $schema->resultset( 'Item' )->delete_all; # 'Branch' deps. on this
258     $schema->resultset( 'Branch' )->delete_all;
259
260     my $borrower = $builder->build({
261         source => 'Borrower',
262     });
263
264     my $query = CGI->new();
265     my $bad_result = C4::ILSDI::Services::LookupPatron($query);
266     is( $bad_result->{message}, 'PatronNotFound', 'No parameters' );
267
268     $query->delete_all();
269     $query->param( 'id', $borrower->{firstname} );
270     my $optional_result = C4::ILSDI::Services::LookupPatron($query);
271     is(
272         $optional_result->{id},
273         $borrower->{borrowernumber},
274         'Valid Firstname only'
275     );
276
277     $query->delete_all();
278     $query->param( 'id', 'ThereIsNoWayThatThisCouldPossiblyBeValid' );
279     my $bad_optional_result = C4::ILSDI::Services::LookupPatron($query);
280     is( $bad_optional_result->{message}, 'PatronNotFound', 'Invalid ID' );
281
282     foreach my $id_type (
283         'cardnumber',
284         'userid',
285         'email',
286         'borrowernumber',
287         'surname',
288         'firstname'
289     ) {
290         $query->delete_all();
291         $query->param( 'id_type', $id_type );
292         $query->param( 'id', $borrower->{$id_type} );
293         my $result = C4::ILSDI::Services::LookupPatron($query);
294         is( $result->{'id'}, $borrower->{borrowernumber}, "Checking $id_type" );
295     }
296
297     # Cleanup
298     $schema->storage->txn_rollback;
299 };
300
301 subtest 'Holds test' => sub {
302
303     plan tests => 5;
304
305     $schema->storage->txn_begin;
306
307     t::lib::Mocks::mock_preference( 'AllowHoldsOnDamagedItems', 0 );
308
309     my $patron = $builder->build({
310         source => 'Borrower',
311     });
312
313     my $biblio = $builder->build({
314         source => 'Biblio',
315     });
316
317     my $biblioitems = $builder->build({
318         source => 'Biblioitem',
319         value => {
320             biblionumber => $biblio->{biblionumber},
321         }
322     });
323
324     my $item = $builder->build({
325         source => 'Item',
326         value => {
327             biblionumber => $biblio->{biblionumber},
328             damaged => 1
329         }
330     });
331
332     my $query = new CGI;
333     $query->param( 'patron_id', $patron->{borrowernumber});
334     $query->param( 'bib_id', $biblio->{biblionumber});
335
336     my $reply = C4::ILSDI::Services::HoldTitle( $query );
337     is( $reply->{code}, 'damaged', "Item damaged" );
338
339     my $item_o = Koha::Items->find($item->{itemnumber});
340     $item_o->damaged(0)->store;
341
342     my $hold = $builder->build({
343         source => 'Reserve',
344         value => {
345             borrowernumber => $patron->{borrowernumber},
346             biblionumber => $biblio->{biblionumber},
347             itemnumber => $item->{itemnumber}
348         }
349     });
350
351     $reply = C4::ILSDI::Services::HoldTitle( $query );
352     is( $reply->{code}, 'itemAlreadyOnHold', "Item already on hold" );
353
354     my $biblio_with_no_item = $builder->build({
355         source => 'Biblio',
356     });
357
358     $query = new CGI;
359     $query->param( 'patron_id', $patron->{borrowernumber});
360     $query->param( 'bib_id', $biblio_with_no_item->{biblionumber});
361
362     $reply = C4::ILSDI::Services::HoldTitle( $query );
363     is( $reply->{code}, 'NoItems', 'Biblio has no item' );
364
365     my $biblio2 = $builder->build({
366         source => 'Biblio',
367     });
368
369     my $biblioitems2 = $builder->build({
370         source => 'Biblioitem',
371         value => {
372             biblionumber => $biblio2->{biblionumber},
373         }
374     });
375
376     my $item2 = $builder->build({
377         source => 'Item',
378         value => {
379             biblionumber => $biblio2->{biblionumber},
380             damaged => 0
381         }
382     });
383
384     t::lib::Mocks::mock_preference( 'ReservesControlBranch', 'PatronLibrary' );
385     my $issuingrule = $builder->build({
386         source => 'Issuingrule',
387         value => {
388             categorycode => $patron->{categorycode},
389             itemtype => $item2->{itype},
390             branchcode => $patron->{branchcode},
391             reservesallowed => 0,
392         }
393     });
394
395     $query = new CGI;
396     $query->param( 'patron_id', $patron->{borrowernumber});
397     $query->param( 'bib_id', $biblio2->{biblionumber});
398     $query->param( 'item_id', $item2->{itemnumber});
399
400     $reply = C4::ILSDI::Services::HoldItem( $query );
401     is( $reply->{code}, 'tooManyReserves', "Too many reserves" );
402
403     my $biblio3 = $builder->build({
404         source => 'Biblio',
405     });
406
407     my $biblioitems3 = $builder->build({
408         source => 'Biblioitem',
409         value => {
410             biblionumber => $biblio3->{biblionumber},
411         }
412     });
413
414     # Adding a holdable item to biblio 3.
415     my $item3 = $builder->build({
416         source => 'Item',
417         value => {
418             biblionumber => $biblio3->{biblionumber},
419             damaged => 0,
420         }
421     });
422
423     my $item4 = $builder->build({
424         source => 'Item',
425         value => {
426             biblionumber => $biblio3->{biblionumber},
427             damaged => 1,
428         }
429     });
430
431     my $issuingrule2 = $builder->build({
432         source => 'Issuingrule',
433         value => {
434             categorycode => $patron->{categorycode},
435             itemtype => $item3->{itype},
436             branchcode => $patron->{branchcode},
437             reservesallowed => 10,
438         }
439     });
440
441     $query = new CGI;
442     $query->param( 'patron_id', $patron->{borrowernumber});
443     $query->param( 'bib_id', $biblio3->{biblionumber});
444     $query->param( 'item_id', $item4->{itemnumber});
445
446     $reply = C4::ILSDI::Services::HoldItem( $query );
447     is( $reply->{code}, 'damaged', "Item is damaged" );
448
449     $schema->storage->txn_rollback;
450 };
451
452 subtest 'Holds test for branch transfer limits' => sub {
453
454     plan tests => 4;
455
456     $schema->storage->txn_begin;
457
458     # Test enforement of branch transfer limits
459     t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '1' );
460     t::lib::Mocks::mock_preference( 'BranchTransferLimitsType', 'itemtype' );
461
462     my $patron = $builder->build({
463         source => 'Borrower',
464     });
465
466     my $origin_branch = $builder->build(
467         {
468             source => 'Branch',
469             value  => {
470                 pickup_location => 1,
471             }
472         }
473     );
474     my $pickup_branch = $builder->build(
475         {
476             source => 'Branch',
477             value  => {
478                 pickup_location => 1,
479             }
480         }
481     );
482
483     my $biblio = $builder->build({
484         source => 'Biblio',
485     });
486     my $biblioitem = $builder->build({
487         source => 'Biblioitem',
488         value => {
489             biblionumber => $biblio->{biblionumber},
490         }
491     });
492     my $item = $builder->build({
493         source => 'Item',
494         value => {
495             homebranch => $origin_branch->{branchcode},
496             holdingbranch => $origin_branch->{branchcode},
497             biblionumber => $biblio->{biblionumber},
498             damaged => 0,
499             itemlost => 0,
500         }
501     });
502
503     Koha::IssuingRules->search()->delete();
504     my $issuingrule = $builder->build({
505         source => 'Issuingrule',
506         value => {
507             categorycode => '*',
508             itemtype => '*',
509             branchcode => '*',
510             reservesallowed => 99,
511         }
512     });
513
514     my $limit = Koha::Item::Transfer::Limit->new({
515         toBranch => $pickup_branch->{branchcode},
516         fromBranch => $item->{holdingbranch},
517         itemtype => $item->{itype},
518     })->store();
519
520     my $query = new CGI;
521     $query->param( 'pickup_location', $pickup_branch->{branchcode} );
522     $query->param( 'patron_id', $patron->{borrowernumber});
523     $query->param( 'bib_id', $biblio->{biblionumber});
524     $query->param( 'item_id', $item->{itemnumber});
525
526     my $reply = C4::ILSDI::Services::HoldItem( $query );
527     is( $reply->{code}, 'cannotBeTransferred', "Item hold, Item cannot be transferred" );
528
529     $reply = C4::ILSDI::Services::HoldTitle( $query );
530     is( $reply->{code}, 'cannotBeTransferred', "Record hold, Item cannot be transferred" );
531
532     t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '0' );
533
534     $reply = C4::ILSDI::Services::HoldItem( $query );
535     is( $reply->{code}, undef, "Item hold, Item can be transferred" );
536
537     Koha::Holds->search()->delete();
538
539     $reply = C4::ILSDI::Services::HoldTitle( $query );
540     is( $reply->{code}, undef, "Record hold, Item con be transferred" );
541
542     $schema->storage->txn_rollback;
543 }