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