Bug 17578: GetMemberDetails - Remove GetMemberDetails
[koha.git] / C4 / ILSDI / Services.pm
1 package C4::ILSDI::Services;
2
3 # Copyright 2009 SARL Biblibre
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use strict;
21 use warnings;
22
23 use C4::Members;
24 use C4::Items;
25 use C4::Circulation;
26 use C4::Accounts;
27 use C4::Biblio;
28 use C4::Reserves qw(AddReserve GetReservesFromBiblionumber GetReservesFromBorrowernumber CanBookBeReserved CanItemBeReserved IsAvailableForItemLevelRequest);
29 use C4::Context;
30 use C4::AuthoritiesMarc;
31 use XML::Simple;
32 use HTML::Entities;
33 use CGI qw ( -utf8 );
34 use DateTime;
35 use C4::Auth;
36 use C4::Members::Attributes qw(GetBorrowerAttributes);
37
38 use Koha::Libraries;
39
40 =head1 NAME
41
42 C4::ILS-DI::Services - ILS-DI Services
43
44 =head1 DESCRIPTION
45
46 Each function in this module represents an ILS-DI service.
47 They all takes a CGI instance as argument and most of them return a 
48 hashref that will be printed by XML::Simple in opac/ilsdi.pl
49
50 =head1 SYNOPSIS
51
52         use C4::ILSDI::Services;
53         use XML::Simple;
54         use CGI qw ( -utf8 );
55
56         my $cgi = new CGI;
57
58         $out = LookupPatron($cgi);
59
60         print CGI::header('text/xml');
61         print XMLout($out,
62                 noattr => 1, 
63                 noescape => 1,
64                 nosort => 1,
65                 xmldecl => '<?xml version="1.0" encoding="UTF-8" ?>',
66                 RootName => 'LookupPatron', 
67                 SuppressEmpty => 1);
68
69 =cut
70
71 =head1 FUNCTIONS
72
73 =head2 GetAvailability
74
75 Given a set of biblionumbers or itemnumbers, returns a list with 
76 availability of the items associated with the identifiers.
77
78 Parameters:
79
80 =head3 id (Required)
81
82 list of either biblionumbers or itemnumbers
83
84 =head3 id_type (Required)
85
86 defines the type of record identifier being used in the request, 
87 possible values:
88
89   - bib
90   - item
91
92 =head3 return_type (Optional)
93
94 requests a particular level of detail in reporting availability, 
95 possible values:
96
97   - bib
98   - item
99
100 =head3 return_fmt (Optional)
101
102 requests a particular format or set of formats in reporting 
103 availability 
104
105 =cut
106
107 sub GetAvailability {
108     my ($cgi) = @_;
109
110     my $out = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
111     $out .= "<dlf:collection\n";
112     $out .= "  xmlns:dlf=\"http://diglib.org/ilsdi/1.1\"\n";
113     $out .= "  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
114     $out .= "  xsi:schemaLocation=\"http://diglib.org/ilsdi/1.1\n";
115     $out .= "    http://diglib.org/architectures/ilsdi/schemas/1.1/dlfexpanded.xsd\">\n";
116
117     foreach my $id ( split( / /, $cgi->param('id') ) ) {
118         if ( $cgi->param('id_type') eq "item" ) {
119             my ( $biblionumber, $status, $msg, $location ) = _availability($id);
120
121             $out .= "  <dlf:record>\n";
122             $out .= "    <dlf:bibliographic id=\"" . ( $biblionumber || $id ) . "\" />\n";
123             $out .= "    <dlf:items>\n";
124             $out .= "      <dlf:item id=\"" . $id . "\">\n";
125             $out .= "        <dlf:simpleavailability>\n";
126             $out .= "          <dlf:identifier>" . $id . "</dlf:identifier>\n";
127             $out .= "          <dlf:availabilitystatus>" . $status . "</dlf:availabilitystatus>\n";
128             if ($msg)      { $out .= "          <dlf:availabilitymsg>" . $msg . "</dlf:availabilitymsg>\n"; }
129             if ($location) { $out .= "          <dlf:location>" . $location . "</dlf:location>\n"; }
130             $out .= "        </dlf:simpleavailability>\n";
131             $out .= "      </dlf:item>\n";
132             $out .= "    </dlf:items>\n";
133             $out .= "  </dlf:record>\n";
134         } else {
135             my $status;
136             my $msg;
137             my $biblioitem = ( GetBiblioItemByBiblioNumber( $id, undef ) )[0];
138             if ($biblioitem) {
139
140             } else {
141                 $status = "unknown";
142                 $msg    = "Error: could not retrieve availability for this ID";
143             }
144             $out .= "  <dlf:record>\n";
145             $out .= "    <dlf:bibliographic id=\"" . $id . "\" />\n";
146             $out .= "    <dlf:simpleavailability>\n";
147             $out .= "      <dlf:identifier>" . $id . "</dlf:identifier>\n";
148             $out .= "      <dlf:availabilitystatus>" . $status . "</dlf:availabilitystatus>\n";
149             $out .= "      <dlf:availabilitymsg>" . $msg . "</dlf:availabilitymsg>\n";
150             $out .= "    </dlf:simpleavailability>\n";
151             $out .= "  </dlf:record>\n";
152         }
153     }
154     $out .= "</dlf:collection>\n";
155
156     return $out;
157 }
158
159 =head2 GetRecords
160
161 Given a list of biblionumbers, returns a list of record objects that 
162 contain bibliographic information, as well as associated holdings and item
163 information. The caller may request a specific metadata schema for the 
164 record objects to be returned.
165
166 This function behaves similarly to HarvestBibliographicRecords and 
167 HarvestExpandedRecords in Data Aggregation, but allows quick, real time 
168 lookup by bibliographic identifier.
169
170 You can use OAI-PMH ListRecords instead of this service.
171
172 Parameters:
173
174   - id (Required)
175         list of system record identifiers
176   - id_type (Optional)
177         Defines the metadata schema in which the records are returned, 
178         possible values:
179           - MARCXML
180
181 =cut
182
183 sub GetRecords {
184     my ($cgi) = @_;
185
186     # Check if the schema is supported. For now, GetRecords only supports MARCXML
187     if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
188         return { code => 'UnsupportedSchema' };
189     }
190
191     my @records;
192
193     # Loop over biblionumbers
194     foreach my $biblionumber ( split( / /, $cgi->param('id') ) ) {
195
196         # Get the biblioitem from the biblionumber
197         my $biblioitem = ( GetBiblioItemByBiblioNumber( $biblionumber, undef ) )[0];
198         if ( not $biblioitem->{'biblionumber'} ) {
199             $biblioitem->{code} = "RecordNotFound";
200         }
201
202         my $embed_items = 1;
203         my $record = GetMarcBiblio($biblionumber, $embed_items);
204         if ($record) {
205             $biblioitem->{marcxml} = $record->as_xml_record();
206         }
207
208         # Get most of the needed data
209         my $biblioitemnumber = $biblioitem->{'biblioitemnumber'};
210         my $reserves         = GetReservesFromBiblionumber({ biblionumber => $biblionumber });
211         my $issues           = GetBiblioIssues($biblionumber);
212         my $items            = GetItemsByBiblioitemnumber($biblioitemnumber);
213
214         # We loop over the items to clean them
215         foreach my $item (@$items) {
216
217             # This hides additionnal XML subfields, we don't need these info
218             delete $item->{'more_subfields_xml'};
219
220             # Display branch names instead of branch codes
221             my $home_library    = Koha::Libraries->find( $item->{homebranch} );
222             my $holding_library = Koha::Libraries->find( $item->{holdingbranch} );
223             $item->{'homebranchname'}    = $home_library    ? $home_library->branchname    : '';
224             $item->{'holdingbranchname'} = $holding_library ? $holding_library->branchname : '';
225         }
226
227         # Hashref building...
228         $biblioitem->{'items'}->{'item'}       = $items;
229         $biblioitem->{'reserves'}->{'reserve'} = $reserves;
230         $biblioitem->{'issues'}->{'issue'}     = $issues;
231
232         push @records, $biblioitem;
233     }
234
235     return { record => \@records };
236 }
237
238 =head2 GetAuthorityRecords
239
240 Given a list of authority record identifiers, returns a list of record 
241 objects that contain the authority records. The function user may request 
242 a specific metadata schema for the record objects.
243
244 Parameters:
245
246   - id (Required)
247     list of authority record identifiers
248   - schema (Optional)
249     specifies the metadata schema of records to be returned, possible values:
250       - MARCXML
251
252 =cut
253
254 sub GetAuthorityRecords {
255     my ($cgi) = @_;
256
257     # If the user asks for an unsupported schema, return an error code
258     if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
259         return { code => 'UnsupportedSchema' };
260     }
261
262     my @records;
263
264     # Let's loop over the authority IDs
265     foreach my $authid ( split( / /, $cgi->param('id') ) ) {
266
267         # Get the record as XML string, or error code
268         push @records, GetAuthorityXML($authid) || { code => 'RecordNotFound' };
269     }
270
271     return { record => \@records };
272 }
273
274 =head2 LookupPatron
275
276 Looks up a patron in the ILS by an identifier, and returns the borrowernumber.
277
278 Parameters:
279
280   - id (Required)
281         an identifier used to look up the patron in Koha
282   - id_type (Optional)
283         the type of the identifier, possible values:
284         - cardnumber
285         - firstname
286         - userid
287         - borrowernumber
288
289 =cut
290
291 sub LookupPatron {
292     my ($cgi) = @_;
293
294     # Get the borrower...
295     my $borrower = GetMember($cgi->param('id_type') => $cgi->param('id'));
296     if ( not $borrower->{'borrowernumber'} ) {
297         return { message => 'PatronNotFound' };
298     }
299
300     # Build the hashref
301     my $patron->{'id'} = $borrower->{'borrowernumber'};
302     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
303
304     # ...and return his ID
305     return $patron;
306 }
307
308 =head2 AuthenticatePatron
309
310 Authenticates a user's login credentials and returns the identifier for 
311 the patron.
312
313 Parameters:
314
315   - username (Required)
316     user's login identifier (userid or cardnumber)
317   - password (Required)
318     user's password
319
320 =cut
321
322 sub AuthenticatePatron {
323     my ($cgi) = @_;
324     my $username = $cgi->param('username');
325     my $password = $cgi->param('password');
326     my ($status, $cardnumber, $userid) = C4::Auth::checkpw( C4::Context->dbh, $username, $password );
327     if ( $status ) {
328         # Get the borrower
329         my $borrower = GetMember( cardnumber => $cardnumber );
330         my $patron->{'id'} = $borrower->{'borrowernumber'};
331         return $patron;
332     }
333     else {
334         return { code => 'PatronNotFound' };
335     }
336 }
337
338 =head2 GetPatronInfo
339
340 Returns specified information about the patron, based on options in the 
341 request. This function can optionally return patron's contact information, 
342 fine information, hold request information, and loan information.
343
344 Parameters:
345
346   - patron_id (Required)
347         the borrowernumber
348   - show_contact (Optional, default 1)
349         whether or not to return patron's contact information in the response
350   - show_fines (Optional, default 0)
351         whether or not to return fine information in the response
352   - show_holds (Optional, default 0)
353         whether or not to return hold request information in the response
354   - show_loans (Optional, default 0)
355         whether or not to return loan information request information in the response 
356
357 =cut
358
359 sub GetPatronInfo {
360     my ($cgi) = @_;
361
362     # Get Member details
363     my $borrowernumber = $cgi->param('patron_id');
364     my $borrower = GetMember( borrowernumber => $borrowernumber );
365     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
366
367     # Cleaning the borrower hashref
368     my $flags = C4::Members::patronflags( $borrower );
369     $borrower->{'charges'}    = $flags>{'CHARGES'}->{'amount'};
370     my $library = Koha::Libraries->find( $borrower->{branchcode} );
371     $borrower->{'branchname'} = $library ? $library->branchname : '';
372     delete $borrower->{'userid'};
373     delete $borrower->{'password'};
374
375     # Contact fields management
376     if ( defined $cgi->param('show_contact') && $cgi->param('show_contact') eq "0" ) {
377
378         # Define contact fields
379         my @contactfields = (
380             'email',              'emailpro',           'fax',                 'mobile',          'phone',             'phonepro',
381             'streetnumber',       'zipcode',            'city',                'streettype',      'B_address',         'B_city',
382             'B_email',            'B_phone',            'B_zipcode',           'address',         'address2',          'altcontactaddress1',
383             'altcontactaddress2', 'altcontactaddress3', 'altcontactfirstname', 'altcontactphone', 'altcontactsurname', 'altcontactzipcode'
384         );
385
386         # and delete them
387         foreach my $field (@contactfields) {
388             delete $borrower->{$field};
389         }
390     }
391
392     # Fines management
393     if ( $cgi->param('show_fines') && $cgi->param('show_fines') eq "1" ) {
394         my @charges;
395         for ( my $i = 1 ; my @charge = getcharges( $borrowernumber, undef, $i ) ; $i++ ) {
396             push( @charges, @charge );
397         }
398         $borrower->{'fines'}->{'fine'} = \@charges;
399     }
400
401     # Reserves management
402     if ( $cgi->param('show_holds') && $cgi->param('show_holds') eq "1" ) {
403
404         # Get borrower's reserves
405         my @reserves = GetReservesFromBorrowernumber( $borrowernumber, undef );
406         foreach my $reserve (@reserves) {
407
408             # Get additional informations
409             my $item = GetBiblioFromItemNumber( $reserve->{'itemnumber'}, undef );
410             my $library = Koha::Libraries->find( $reserve->{branchcode} );
411             my $branchname = $library ? $library->branchname : '';
412
413             # Remove unwanted fields
414             delete $item->{'marcxml'};
415             delete $item->{'more_subfields_xml'};
416
417             # Add additional fields
418             $reserve->{'item'}       = $item;
419             $reserve->{'branchname'} = $branchname;
420             $reserve->{'title'}      = GetBiblio( $reserve->{'biblionumber'} )->{'title'};
421         }
422         $borrower->{'holds'}->{'hold'} = \@reserves;
423     }
424
425     # Issues management
426     if ( $cgi->param('show_loans') && $cgi->param('show_loans') eq "1" ) {
427         my $issues = GetPendingIssues($borrowernumber);
428         foreach my $issue ( @$issues ){
429             $issue->{'issuedate'} = $issue->{'issuedate'}->strftime('%Y-%m-%d %H:%M');
430             $issue->{'date_due'} = $issue->{'date_due'}->strftime('%Y-%m-%d %H:%M');
431         }
432         $borrower->{'loans'}->{'loan'} = $issues;
433     }
434
435     if ( $cgi->param('show_attributes') eq "1" ) {
436         my $attrs = GetBorrowerAttributes( $borrowernumber, 0, 1 );
437         $borrower->{'attributes'} = $attrs;
438     }
439
440     return $borrower;
441 }
442
443 =head2 GetPatronStatus
444
445 Returns a patron's status information.
446
447 Parameters:
448
449   - patron_id (Required)
450         the borrower ID
451
452 =cut
453
454 sub GetPatronStatus {
455     my ($cgi) = @_;
456
457     # Get Member details
458     my $borrowernumber = $cgi->param('patron_id');
459     my $borrower = GetMember( borrowernumber => $borrowernumber );
460     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
461
462     # Return the results
463     return {
464         type   => $$borrower{categorycode},
465         status => 0, # TODO
466         expiry => $$borrower{dateexpiry},
467     };
468 }
469
470 =head2 GetServices
471
472 Returns information about the services available on a particular item for 
473 a particular patron.
474
475 Parameters:
476
477   - patron_id (Required)
478         a borrowernumber
479   - item_id (Required)
480         an itemnumber
481 =cut
482
483 sub GetServices {
484     my ($cgi) = @_;
485
486     # Get the member, or return an error code if not found
487     my $borrowernumber = $cgi->param('patron_id');
488     my $borrower = GetMember( borrowernumber => $borrowernumber );
489     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
490
491     # Get the item, or return an error code if not found
492     my $itemnumber = $cgi->param('item_id');
493     my $item = GetItem( $itemnumber );
494     return { code => 'RecordNotFound' } unless $$item{itemnumber};
495
496     my @availablefor;
497
498     # Reserve level management
499     my $biblionumber = $item->{'biblionumber'};
500     my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
501     if ($canbookbereserved eq 'OK') {
502         push @availablefor, 'title level hold';
503         my $canitembereserved = IsAvailableForItemLevelRequest($item, $borrower);
504         if ($canitembereserved) {
505             push @availablefor, 'item level hold';
506         }
507     }
508
509     # Reserve cancellation management
510     my @reserves = GetReservesFromBorrowernumber( $borrowernumber, undef );
511     my @reserveditems;
512     foreach my $reserve (@reserves) {
513         push @reserveditems, $reserve->{'itemnumber'};
514     }
515     if ( grep { $itemnumber eq $_ } @reserveditems ) {
516         push @availablefor, 'hold cancellation';
517     }
518
519     # Renewal management
520     my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
521     if ( $renewal[0] ) {
522         push @availablefor, 'loan renewal';
523     }
524
525     # Issuing management
526     my $barcode = $item->{'barcode'} || '';
527     $barcode = barcodedecode($barcode) if ( $barcode && C4::Context->preference('itemBarcodeInputFilter') );
528     if ($barcode) {
529         my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $borrower, $barcode );
530
531         # TODO push @availablefor, 'loan';
532     }
533
534     my $out;
535     $out->{'AvailableFor'} = \@availablefor;
536
537     return $out;
538 }
539
540 =head2 RenewLoan
541
542 Extends the due date for a borrower's existing issue.
543
544 Parameters:
545
546   - patron_id (Required)
547         a borrowernumber
548   - item_id (Required)
549         an itemnumber
550   - desired_due_date (Required)
551         the date the patron would like the item returned by 
552
553 =cut
554
555 sub RenewLoan {
556     my ($cgi) = @_;
557
558     # Get borrower infos or return an error code
559     my $borrowernumber = $cgi->param('patron_id');
560     my $borrower = GetMember( borrowernumber => $borrowernumber );
561     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
562
563     # Get the item, or return an error code
564     my $itemnumber = $cgi->param('item_id');
565     my $item = GetItem( $itemnumber );
566     return { code => 'RecordNotFound' } unless $$item{itemnumber};
567
568     # Add renewal if possible
569     my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
570     if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber ); }
571
572     my $issue = GetItemIssue($itemnumber);
573
574     # Hashref building
575     my $out;
576     $out->{'renewals'} = $issue->{'renewals'};
577     $out->{date_due}   = $issue->{date_due}->strftime('%Y-%m-%d %H:%S');
578     $out->{'success'}  = $renewal[0];
579     $out->{'error'}    = $renewal[1];
580
581     return $out;
582 }
583
584 =head2 HoldTitle
585
586 Creates, for a borrower, a biblio-level hold reserve.
587
588 Parameters:
589
590   - patron_id (Required)
591         a borrowernumber
592   - bib_id (Required)
593         a biblionumber
594   - request_location (Required)
595         IP address where the end user request is being placed
596   - pickup_location (Optional)
597         a branch code indicating the location to which to deliver the item for pickup
598   - needed_before_date (Optional)
599         date after which hold request is no longer needed
600   - pickup_expiry_date (Optional)
601         date after which item returned to shelf if item is not picked up 
602
603 =cut
604
605 sub HoldTitle {
606     my ($cgi) = @_;
607
608     # Get the borrower or return an error code
609     my $borrowernumber = $cgi->param('patron_id');
610     my $borrower = GetMember( borrowernumber => $borrowernumber );
611     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
612
613     # Get the biblio record, or return an error code
614     my $biblionumber = $cgi->param('bib_id');
615     my $biblio = GetBiblio( $biblionumber );
616     return { code => 'RecordNotFound' } unless $$biblio{biblionumber};
617     
618     my $title = $$biblio{title};
619
620     # Check if the biblio can be reserved
621     return { code => 'NotHoldable' } unless CanBookBeReserved( $borrowernumber, $biblionumber ) eq 'OK';
622
623     my $branch;
624
625     # Pickup branch management
626     if ( $cgi->param('pickup_location') ) {
627         $branch = $cgi->param('pickup_location');
628         return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
629     } else { # if the request provide no branch, use the borrower's branch
630         $branch = $$borrower{branchcode};
631     }
632
633     # Add the reserve
634     #    $branch,    $borrowernumber, $biblionumber,
635     #    $constraint, $bibitems,  $priority, $resdate, $expdate, $notes,
636     #    $title,      $checkitem, $found
637     my $priority= C4::Reserves::CalculatePriority( $biblionumber );
638     AddReserve( $branch, $borrowernumber, $biblionumber, undef, $priority, undef, undef, undef, $title, undef, undef );
639
640     # Hashref building
641     my $out;
642     $out->{'title'}           = $title;
643     my $library = Koha::Libraries->find( $branch );
644     $out->{'pickup_location'} = $library ? $library->branchname : '';
645
646     # TODO $out->{'date_available'}  = '';
647
648     return $out;
649 }
650
651 =head2 HoldItem
652
653 Creates, for a borrower, an item-level hold request on a specific item of 
654 a bibliographic record in Koha.
655
656 Parameters:
657
658   - patron_id (Required)
659         a borrowernumber
660   - bib_id (Required)
661         a biblionumber
662   - item_id (Required)
663         an itemnumber
664   - pickup_location (Optional)
665         a branch code indicating the location to which to deliver the item for pickup
666   - needed_before_date (Optional)
667         date after which hold request is no longer needed
668   - pickup_expiry_date (Optional)
669         date after which item returned to shelf if item is not picked up 
670
671 =cut
672
673 sub HoldItem {
674     my ($cgi) = @_;
675
676     # Get the borrower or return an error code
677     my $borrowernumber = $cgi->param('patron_id');
678     my $borrower = GetMember( borrowernumber => $borrowernumber );
679     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
680
681     # Get the biblio or return an error code
682     my $biblionumber = $cgi->param('bib_id');
683     my $biblio = GetBiblio($biblionumber);
684     return { code => 'RecordNotFound' } unless $$biblio{biblionumber};
685
686     my $title = $$biblio{title};
687
688     # Get the item or return an error code
689     my $itemnumber = $cgi->param('item_id');
690     my $item = GetItem( $itemnumber );
691     return { code => 'RecordNotFound' } unless $$item{itemnumber};
692
693     # If the biblio does not match the item, return an error code
694     return { code => 'RecordNotFound' } if $$item{biblionumber} ne $$biblio{biblionumber};
695
696     # Check for item disponibility
697     my $canitembereserved = C4::Reserves::CanItemBeReserved( $borrowernumber, $itemnumber );
698     my $canbookbereserved = C4::Reserves::CanBookBeReserved( $borrowernumber, $biblionumber );
699     return { code => 'NotHoldable' } unless $canbookbereserved eq 'OK' and $canitembereserved eq 'OK';
700
701     # Pickup branch management
702     my $branch;
703     if ( $cgi->param('pickup_location') ) {
704         $branch = $cgi->param('pickup_location');
705         return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
706     } else { # if the request provide no branch, use the borrower's branch
707         $branch = $$borrower{branchcode};
708     }
709
710     # Add the reserve
711     #    $branch,    $borrowernumber, $biblionumber,
712     #    $constraint, $bibitems,  $priority, $resdate, $expdate, $notes,
713     #    $title,      $checkitem, $found
714     my $priority= C4::Reserves::CalculatePriority( $biblionumber );
715     AddReserve( $branch, $borrowernumber, $biblionumber, undef, $priority, undef, undef, undef, $title, $itemnumber, undef );
716
717     # Hashref building
718     my $out;
719     my $library = Koha::Libraries->find( $branch );
720     $out->{'pickup_location'} = $library ? $library->branchname : '';
721
722     # TODO $out->{'date_available'} = '';
723
724     return $out;
725 }
726
727 =head2 CancelHold
728
729 Cancels an active reserve request for the borrower.
730
731 Parameters:
732
733   - patron_id (Required)
734         a borrowernumber
735   - item_id (Required)
736         a reserve_id
737
738 =cut
739
740 sub CancelHold {
741     my ($cgi) = @_;
742
743     # Get the borrower or return an error code
744     my $borrowernumber = $cgi->param('patron_id');
745     my $borrower = GetMember( borrowernumber => $borrowernumber );
746     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
747
748     # Get the reserve or return an error code
749     my $reserve_id = $cgi->param('item_id');
750     my $reserve = C4::Reserves::GetReserve($reserve_id);
751     return { code => 'RecordNotFound' } unless $reserve;
752     return { code => 'RecordNotFound' } unless ($reserve->{borrowernumber} == $borrowernumber);
753
754     C4::Reserves::CancelReserve({reserve_id => $reserve_id});
755
756     return { code => 'Canceled' };
757 }
758
759 =head2 _availability
760
761 Returns, for an itemnumber, an array containing availability information.
762
763  my ($biblionumber, $status, $msg, $location) = _availability($id);
764
765 =cut
766
767 sub _availability {
768     my ($itemnumber) = @_;
769     my $item = GetItem( $itemnumber, undef, undef );
770
771     if ( not $item->{'itemnumber'} ) {
772         return ( undef, 'unknown', 'Error: could not retrieve availability for this ID', undef );
773     }
774
775     my $biblionumber = $item->{'biblioitemnumber'};
776     my $library = Koha::Libraries->find( $item->{holdingbranch} );
777     my $location = $library ? $library->branchname : '';
778
779     if ( $item->{'notforloan'} ) {
780         return ( $biblionumber, 'not available', 'Not for loan', $location );
781     } elsif ( $item->{'onloan'} ) {
782         return ( $biblionumber, 'not available', 'Checked out', $location );
783     } elsif ( $item->{'itemlost'} ) {
784         return ( $biblionumber, 'not available', 'Item lost', $location );
785     } elsif ( $item->{'withdrawn'} ) {
786         return ( $biblionumber, 'not available', 'Item withdrawn', $location );
787     } elsif ( $item->{'damaged'} ) {
788         return ( $biblionumber, 'not available', 'Item damaged', $location );
789     } else {
790         return ( $biblionumber, 'available', undef, $location );
791     }
792 }
793
794 1;