Bug 15758: Koha::Libraries - Remove GetBranches
[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         # We don't want MARC to be displayed
209         delete $biblioitem->{'marc'};
210
211         # Get most of the needed data
212         my $biblioitemnumber = $biblioitem->{'biblioitemnumber'};
213         my $reserves         = GetReservesFromBiblionumber({ biblionumber => $biblionumber });
214         my $issues           = GetBiblioIssues($biblionumber);
215         my $items            = GetItemsByBiblioitemnumber($biblioitemnumber);
216
217         # We loop over the items to clean them
218         foreach my $item (@$items) {
219
220             # This hides additionnal XML subfields, we don't need these info
221             delete $item->{'more_subfields_xml'};
222
223             # Display branch names instead of branch codes
224             my $home_library    = Koha::Libraries->find( $item->{homebranch} );
225             my $holding_library = Koha::Libraries->find( $item->{holdingbranch} );
226             $item->{'homebranchname'}    = $home_library    ? $home_library->branchname    : '';
227             $item->{'holdingbranchname'} = $holding_library ? $holding_library->branchname : '';
228         }
229
230         # Hashref building...
231         $biblioitem->{'items'}->{'item'}       = $items;
232         $biblioitem->{'reserves'}->{'reserve'} = $reserves;
233         $biblioitem->{'issues'}->{'issue'}     = $issues;
234
235         push @records, $biblioitem;
236     }
237
238     return { record => \@records };
239 }
240
241 =head2 GetAuthorityRecords
242
243 Given a list of authority record identifiers, returns a list of record 
244 objects that contain the authority records. The function user may request 
245 a specific metadata schema for the record objects.
246
247 Parameters:
248
249   - id (Required)
250     list of authority record identifiers
251   - schema (Optional)
252     specifies the metadata schema of records to be returned, possible values:
253       - MARCXML
254
255 =cut
256
257 sub GetAuthorityRecords {
258     my ($cgi) = @_;
259
260     # If the user asks for an unsupported schema, return an error code
261     if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
262         return { code => 'UnsupportedSchema' };
263     }
264
265     my @records;
266
267     # Let's loop over the authority IDs
268     foreach my $authid ( split( / /, $cgi->param('id') ) ) {
269
270         # Get the record as XML string, or error code
271         push @records, GetAuthorityXML($authid) || { code => 'RecordNotFound' };
272     }
273
274     return { record => \@records };
275 }
276
277 =head2 LookupPatron
278
279 Looks up a patron in the ILS by an identifier, and returns the borrowernumber.
280
281 Parameters:
282
283   - id (Required)
284         an identifier used to look up the patron in Koha
285   - id_type (Optional)
286         the type of the identifier, possible values:
287         - cardnumber
288         - firstname
289         - userid
290         - borrowernumber
291
292 =cut
293
294 sub LookupPatron {
295     my ($cgi) = @_;
296
297     # Get the borrower...
298     my $borrower = GetMember($cgi->param('id_type') => $cgi->param('id'));
299     if ( not $borrower->{'borrowernumber'} ) {
300         return { message => 'PatronNotFound' };
301     }
302
303     # Build the hashref
304     my $patron->{'id'} = $borrower->{'borrowernumber'};
305     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
306
307     # ...and return his ID
308     return $patron;
309 }
310
311 =head2 AuthenticatePatron
312
313 Authenticates a user's login credentials and returns the identifier for 
314 the patron.
315
316 Parameters:
317
318   - username (Required)
319     user's login identifier (userid or cardnumber)
320   - password (Required)
321     user's password
322
323 =cut
324
325 sub AuthenticatePatron {
326     my ($cgi) = @_;
327     my $username = $cgi->param('username');
328     my $password = $cgi->param('password');
329     my ($status, $cardnumber, $userid) = C4::Auth::checkpw( C4::Context->dbh, $username, $password );
330     if ( $status ) {
331         # Get the borrower
332         my $borrower = GetMember( cardnumber => $cardnumber );
333         my $patron->{'id'} = $borrower->{'borrowernumber'};
334         return $patron;
335     }
336     else {
337         return { code => 'PatronNotFound' };
338     }
339 }
340
341 =head2 GetPatronInfo
342
343 Returns specified information about the patron, based on options in the 
344 request. This function can optionally return patron's contact information, 
345 fine information, hold request information, and loan information.
346
347 Parameters:
348
349   - patron_id (Required)
350         the borrowernumber
351   - show_contact (Optional, default 1)
352         whether or not to return patron's contact information in the response
353   - show_fines (Optional, default 0)
354         whether or not to return fine information in the response
355   - show_holds (Optional, default 0)
356         whether or not to return hold request information in the response
357   - show_loans (Optional, default 0)
358         whether or not to return loan information request information in the response 
359
360 =cut
361
362 sub GetPatronInfo {
363     my ($cgi) = @_;
364
365     # Get Member details
366     my $borrowernumber = $cgi->param('patron_id');
367     my $borrower = GetMemberDetails( $borrowernumber );
368     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
369
370     # Cleaning the borrower hashref
371     $borrower->{'charges'}    = $borrower->{'flags'}->{'CHARGES'}->{'amount'};
372     my $library = Koha::Libraries->find( $borrower->{branchcode} );
373     $borrower->{'branchname'} = $library ? $library->branchname : '';
374     delete $borrower->{'flags'};
375     delete $borrower->{'userid'};
376     delete $borrower->{'password'};
377
378     # Contact fields management
379     if ( defined $cgi->param('show_contact') && $cgi->param('show_contact') eq "0" ) {
380
381         # Define contact fields
382         my @contactfields = (
383             'email',              'emailpro',           'fax',                 'mobile',          'phone',             'phonepro',
384             'streetnumber',       'zipcode',            'city',                'streettype',      'B_address',         'B_city',
385             'B_email',            'B_phone',            'B_zipcode',           'address',         'address2',          'altcontactaddress1',
386             'altcontactaddress2', 'altcontactaddress3', 'altcontactfirstname', 'altcontactphone', 'altcontactsurname', 'altcontactzipcode'
387         );
388
389         # and delete them
390         foreach my $field (@contactfields) {
391             delete $borrower->{$field};
392         }
393     }
394
395     # Fines management
396     if ( $cgi->param('show_fines') && $cgi->param('show_fines') eq "1" ) {
397         my @charges;
398         for ( my $i = 1 ; my @charge = getcharges( $borrowernumber, undef, $i ) ; $i++ ) {
399             push( @charges, @charge );
400         }
401         $borrower->{'fines'}->{'fine'} = \@charges;
402     }
403
404     # Reserves management
405     if ( $cgi->param('show_holds') && $cgi->param('show_holds') eq "1" ) {
406
407         # Get borrower's reserves
408         my @reserves = GetReservesFromBorrowernumber( $borrowernumber, undef );
409         foreach my $reserve (@reserves) {
410
411             # Get additional informations
412             my $item = GetBiblioFromItemNumber( $reserve->{'itemnumber'}, undef );
413             my $library = Koha::Libraries->find( $reserve->{branchcode} );
414             my $branchname = $library ? $library->branchname : '';
415
416             # Remove unwanted fields
417             delete $item->{'marc'};
418             delete $item->{'marcxml'};
419             delete $item->{'more_subfields_xml'};
420
421             # Add additional fields
422             $reserve->{'item'}       = $item;
423             $reserve->{'branchname'} = $branchname;
424             $reserve->{'title'}      = GetBiblio( $reserve->{'biblionumber'} )->{'title'};
425         }
426         $borrower->{'holds'}->{'hold'} = \@reserves;
427     }
428
429     # Issues management
430     if ( $cgi->param('show_loans') && $cgi->param('show_loans') eq "1" ) {
431         my $issues = GetPendingIssues($borrowernumber);
432         foreach my $issue ( @$issues ){
433             $issue->{'issuedate'} = $issue->{'issuedate'}->strftime('%Y-%m-%d %H:%M');
434             $issue->{'date_due'} = $issue->{'date_due'}->strftime('%Y-%m-%d %H:%M');
435         }
436         $borrower->{'loans'}->{'loan'} = $issues;
437     }
438
439     if ( $cgi->param('show_attributes') eq "1" ) {
440         my $attrs = GetBorrowerAttributes( $borrowernumber, 0, 1 );
441         $borrower->{'attributes'} = $attrs;
442     }
443
444     return $borrower;
445 }
446
447 =head2 GetPatronStatus
448
449 Returns a patron's status information.
450
451 Parameters:
452
453   - patron_id (Required)
454         the borrower ID
455
456 =cut
457
458 sub GetPatronStatus {
459     my ($cgi) = @_;
460
461     # Get Member details
462     my $borrowernumber = $cgi->param('patron_id');
463     my $borrower = GetMemberDetails( $borrowernumber );
464     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
465
466     # Return the results
467     return {
468         type   => $$borrower{categorycode},
469         status => 0, # TODO
470         expiry => $$borrower{dateexpiry},
471     };
472 }
473
474 =head2 GetServices
475
476 Returns information about the services available on a particular item for 
477 a particular patron.
478
479 Parameters:
480
481   - patron_id (Required)
482         a borrowernumber
483   - item_id (Required)
484         an itemnumber
485 =cut
486
487 sub GetServices {
488     my ($cgi) = @_;
489
490     # Get the member, or return an error code if not found
491     my $borrowernumber = $cgi->param('patron_id');
492     my $borrower = GetMemberDetails( $borrowernumber );
493     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
494
495     # Get the item, or return an error code if not found
496     my $itemnumber = $cgi->param('item_id');
497     my $item = GetItem( $itemnumber );
498     return { code => 'RecordNotFound' } unless $$item{itemnumber};
499
500     my @availablefor;
501
502     # Reserve level management
503     my $biblionumber = $item->{'biblionumber'};
504     my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
505     if ($canbookbereserved eq 'OK') {
506         push @availablefor, 'title level hold';
507         my $canitembereserved = IsAvailableForItemLevelRequest($item, $borrower);
508         if ($canitembereserved) {
509             push @availablefor, 'item level hold';
510         }
511     }
512
513     # Reserve cancellation management
514     my @reserves = GetReservesFromBorrowernumber( $borrowernumber, undef );
515     my @reserveditems;
516     foreach my $reserve (@reserves) {
517         push @reserveditems, $reserve->{'itemnumber'};
518     }
519     if ( grep { $itemnumber eq $_ } @reserveditems ) {
520         push @availablefor, 'hold cancellation';
521     }
522
523     # Renewal management
524     my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
525     if ( $renewal[0] ) {
526         push @availablefor, 'loan renewal';
527     }
528
529     # Issuing management
530     my $barcode = $item->{'barcode'} || '';
531     $barcode = barcodedecode($barcode) if ( $barcode && C4::Context->preference('itemBarcodeInputFilter') );
532     if ($barcode) {
533         my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $borrower, $barcode );
534
535         # TODO push @availablefor, 'loan';
536     }
537
538     my $out;
539     $out->{'AvailableFor'} = \@availablefor;
540
541     return $out;
542 }
543
544 =head2 RenewLoan
545
546 Extends the due date for a borrower's existing issue.
547
548 Parameters:
549
550   - patron_id (Required)
551         a borrowernumber
552   - item_id (Required)
553         an itemnumber
554   - desired_due_date (Required)
555         the date the patron would like the item returned by 
556
557 =cut
558
559 sub RenewLoan {
560     my ($cgi) = @_;
561
562     # Get borrower infos or return an error code
563     my $borrowernumber = $cgi->param('patron_id');
564     my $borrower = GetMemberDetails( $borrowernumber );
565     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
566
567     # Get the item, or return an error code
568     my $itemnumber = $cgi->param('item_id');
569     my $item = GetItem( $itemnumber );
570     return { code => 'RecordNotFound' } unless $$item{itemnumber};
571
572     # Add renewal if possible
573     my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
574     if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber ); }
575
576     my $issue = GetItemIssue($itemnumber);
577
578     # Hashref building
579     my $out;
580     $out->{'renewals'} = $issue->{'renewals'};
581     $out->{date_due}   = $issue->{date_due}->strftime('%Y-%m-%d %H:%S');
582     $out->{'success'}  = $renewal[0];
583     $out->{'error'}    = $renewal[1];
584
585     return $out;
586 }
587
588 =head2 HoldTitle
589
590 Creates, for a borrower, a biblio-level hold reserve.
591
592 Parameters:
593
594   - patron_id (Required)
595         a borrowernumber
596   - bib_id (Required)
597         a biblionumber
598   - request_location (Required)
599         IP address where the end user request is being placed
600   - pickup_location (Optional)
601         a branch code indicating the location to which to deliver the item for pickup
602   - needed_before_date (Optional)
603         date after which hold request is no longer needed
604   - pickup_expiry_date (Optional)
605         date after which item returned to shelf if item is not picked up 
606
607 =cut
608
609 sub HoldTitle {
610     my ($cgi) = @_;
611
612     # Get the borrower or return an error code
613     my $borrowernumber = $cgi->param('patron_id');
614     my $borrower = GetMemberDetails( $borrowernumber );
615     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
616
617     # Get the biblio record, or return an error code
618     my $biblionumber = $cgi->param('bib_id');
619     my $biblio = GetBiblio( $biblionumber );
620     return { code => 'RecordNotFound' } unless $$biblio{biblionumber};
621     
622     my $title = $$biblio{title};
623
624     # Check if the biblio can be reserved
625     return { code => 'NotHoldable' } unless CanBookBeReserved( $borrowernumber, $biblionumber ) eq 'OK';
626
627     my $branch;
628
629     # Pickup branch management
630     if ( $cgi->param('pickup_location') ) {
631         $branch = $cgi->param('pickup_location');
632         return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
633     } else { # if the request provide no branch, use the borrower's branch
634         $branch = $$borrower{branchcode};
635     }
636
637     # Add the reserve
638     #    $branch,    $borrowernumber, $biblionumber,
639     #    $constraint, $bibitems,  $priority, $resdate, $expdate, $notes,
640     #    $title,      $checkitem, $found
641     my $priority= C4::Reserves::CalculatePriority( $biblionumber );
642     AddReserve( $branch, $borrowernumber, $biblionumber, undef, $priority, undef, undef, undef, $title, undef, undef );
643
644     # Hashref building
645     my $out;
646     $out->{'title'}           = $title;
647     my $library = Koha::Libraries->find( $branch );
648     $out->{'pickup_location'} = $library ? $library->branchname : '';
649
650     # TODO $out->{'date_available'}  = '';
651
652     return $out;
653 }
654
655 =head2 HoldItem
656
657 Creates, for a borrower, an item-level hold request on a specific item of 
658 a bibliographic record in Koha.
659
660 Parameters:
661
662   - patron_id (Required)
663         a borrowernumber
664   - bib_id (Required)
665         a biblionumber
666   - item_id (Required)
667         an itemnumber
668   - pickup_location (Optional)
669         a branch code indicating the location to which to deliver the item for pickup
670   - needed_before_date (Optional)
671         date after which hold request is no longer needed
672   - pickup_expiry_date (Optional)
673         date after which item returned to shelf if item is not picked up 
674
675 =cut
676
677 sub HoldItem {
678     my ($cgi) = @_;
679
680     # Get the borrower or return an error code
681     my $borrowernumber = $cgi->param('patron_id');
682     my $borrower = GetMemberDetails( $borrowernumber );
683     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
684
685     # Get the biblio or return an error code
686     my $biblionumber = $cgi->param('bib_id');
687     my $biblio = GetBiblio($biblionumber);
688     return { code => 'RecordNotFound' } unless $$biblio{biblionumber};
689
690     my $title = $$biblio{title};
691
692     # Get the item or return an error code
693     my $itemnumber = $cgi->param('item_id');
694     my $item = GetItem( $itemnumber );
695     return { code => 'RecordNotFound' } unless $$item{itemnumber};
696
697     # If the biblio does not match the item, return an error code
698     return { code => 'RecordNotFound' } if $$item{biblionumber} ne $$biblio{biblionumber};
699
700     # Check for item disponibility
701     my $canitembereserved = C4::Reserves::CanItemBeReserved( $borrowernumber, $itemnumber );
702     my $canbookbereserved = C4::Reserves::CanBookBeReserved( $borrowernumber, $biblionumber );
703     return { code => 'NotHoldable' } unless $canbookbereserved eq 'OK' and $canitembereserved eq 'OK';
704
705     # Pickup branch management
706     my $branch;
707     if ( $cgi->param('pickup_location') ) {
708         $branch = $cgi->param('pickup_location');
709         return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
710     } else { # if the request provide no branch, use the borrower's branch
711         $branch = $$borrower{branchcode};
712     }
713
714     # Add the reserve
715     #    $branch,    $borrowernumber, $biblionumber,
716     #    $constraint, $bibitems,  $priority, $resdate, $expdate, $notes,
717     #    $title,      $checkitem, $found
718     my $priority= C4::Reserves::CalculatePriority( $biblionumber );
719     AddReserve( $branch, $borrowernumber, $biblionumber, undef, $priority, undef, undef, undef, $title, $itemnumber, undef );
720
721     # Hashref building
722     my $out;
723     my $library = Koha::Libraries->find( $branch );
724     $out->{'pickup_location'} = $library ? $library->branchname : '';
725
726     # TODO $out->{'date_available'} = '';
727
728     return $out;
729 }
730
731 =head2 CancelHold
732
733 Cancels an active reserve request for the borrower.
734
735 Parameters:
736
737   - patron_id (Required)
738         a borrowernumber
739   - item_id (Required)
740         a reserve_id
741
742 =cut
743
744 sub CancelHold {
745     my ($cgi) = @_;
746
747     # Get the borrower or return an error code
748     my $borrowernumber = $cgi->param('patron_id');
749     my $borrower = GetMemberDetails( $borrowernumber );
750     return { code => 'PatronNotFound' } unless $$borrower{borrowernumber};
751
752     # Get the reserve or return an error code
753     my $reserve_id = $cgi->param('item_id');
754     my $reserve = C4::Reserves::GetReserve($reserve_id);
755     return { code => 'RecordNotFound' } unless $reserve;
756     return { code => 'RecordNotFound' } unless ($reserve->{borrowernumber} == $borrowernumber);
757
758     C4::Reserves::CancelReserve({reserve_id => $reserve_id});
759
760     return { code => 'Canceled' };
761 }
762
763 =head2 _availability
764
765 Returns, for an itemnumber, an array containing availability information.
766
767  my ($biblionumber, $status, $msg, $location) = _availability($id);
768
769 =cut
770
771 sub _availability {
772     my ($itemnumber) = @_;
773     my $item = GetItem( $itemnumber, undef, undef );
774
775     if ( not $item->{'itemnumber'} ) {
776         return ( undef, 'unknown', 'Error: could not retrieve availability for this ID', undef );
777     }
778
779     my $biblionumber = $item->{'biblioitemnumber'};
780     my $library = Koha::Libraries->find( $item->{holdingbranch} );
781     my $location = $library ? $library->branchname : '';
782
783     if ( $item->{'notforloan'} ) {
784         return ( $biblionumber, 'not available', 'Not for loan', $location );
785     } elsif ( $item->{'onloan'} ) {
786         return ( $biblionumber, 'not available', 'Checked out', $location );
787     } elsif ( $item->{'itemlost'} ) {
788         return ( $biblionumber, 'not available', 'Item lost', $location );
789     } elsif ( $item->{'withdrawn'} ) {
790         return ( $biblionumber, 'not available', 'Item withdrawn', $location );
791     } elsif ( $item->{'damaged'} ) {
792         return ( $biblionumber, 'not available', 'Item damaged', $location );
793     } else {
794         return ( $biblionumber, 'available', undef, $location );
795     }
796 }
797
798 1;