1 package C4::ILSDI::Services;
3 # Copyright 2009 SARL Biblibre
5 # This file is part of Koha.
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.
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.
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>.
24 use C4::Items qw( get_hostitemnumbers_of );
25 use C4::Circulation qw( CanBookBeRenewed barcodedecode CanBookBeIssued AddRenewal );
27 use C4::Biblio qw( GetMarcBiblio );
28 use C4::Reserves qw( CanBookBeReserved IsAvailableForItemLevelRequest CalculatePriority AddReserve CanItemBeReserved CanReserveBeCanceledFromOpac);
34 use Koha::DateUtils qw( dt_from_string );
35 use C4::AuthoritiesMarc qw( GetAuthorityXML );
45 C4::ILS-DI::Services - ILS-DI Services
49 Each function in this module represents an ILS-DI service.
50 They all takes a CGI instance as argument and most of them return a
51 hashref that will be printed by XML::Simple in opac/ilsdi.pl
55 use C4::ILSDI::Services;
61 $out = LookupPatron($cgi);
63 print CGI::header('text/xml');
68 xmldecl => '<?xml version="1.0" encoding="UTF-8" ?>',
69 RootName => 'LookupPatron',
76 =head2 GetAvailability
78 Given a set of biblionumbers or itemnumbers, returns a list with
79 availability of the items associated with the identifiers.
85 list of either biblionumbers or itemnumbers
87 =head3 id_type (Required)
89 defines the type of record identifier being used in the request,
95 =head3 return_type (Optional)
97 requests a particular level of detail in reporting availability,
103 =head3 return_fmt (Optional)
105 requests a particular format or set of formats in reporting
110 sub GetAvailability {
113 my $out = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
114 $out .= "<dlf:collection\n";
115 $out .= " xmlns:dlf=\"http://diglib.org/ilsdi/1.1\"\n";
116 $out .= " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
117 $out .= " xsi:schemaLocation=\"http://diglib.org/ilsdi/1.1\n";
118 $out .= " http://diglib.org/architectures/ilsdi/schemas/1.1/dlfexpanded.xsd\">\n";
120 foreach my $id ( split( / /, $cgi->param('id') ) ) {
121 if ( $cgi->param('id_type') eq "item" ) {
122 my ( $biblionumber, $status, $msg, $location, $itemcallnumber ) = _availability($id);
124 $out .= " <dlf:record>\n";
125 $out .= " <dlf:bibliographic id=\"" . ( $biblionumber || $id ) . "\" />\n";
126 $out .= " <dlf:items>\n";
127 $out .= " <dlf:item id=\"" . $id . "\">\n";
128 $out .= " <dlf:simpleavailability>\n";
129 $out .= " <dlf:identifier>" . $id . "</dlf:identifier>\n";
130 $out .= " <dlf:availabilitystatus>" . $status . "</dlf:availabilitystatus>\n";
131 if ($msg) { $out .= " <dlf:availabilitymsg>" . $msg . "</dlf:availabilitymsg>\n"; }
132 if ($location) { $out .= " <dlf:location>" . $location . "</dlf:location>\n"; }
133 if ($itemcallnumber) { $out .= " <dlf:itemcallnumber>" . $itemcallnumber. "</dlf:itemcallnumber>\n"; }
134 $out .= " </dlf:simpleavailability>\n";
135 $out .= " </dlf:item>\n";
136 $out .= " </dlf:items>\n";
137 $out .= " </dlf:record>\n";
141 my $items = Koha::Items->search({ biblionumber => $id });
144 $out .= " <dlf:record>\n";
145 $out .= " <dlf:bibliographic id=\"" .$id. "\" />\n";
146 $out .= " <dlf:items>\n";
147 # We loop over the items to clean them
148 while ( my $item = $items->next ) {
149 my $itemnumber = $item->itemnumber;
150 my ( $biblionumber, $status, $msg, $location, $itemcallnumber ) = _availability($itemnumber);
151 $out .= " <dlf:item id=\"" . $itemnumber . "\">\n";
152 $out .= " <dlf:simpleavailability>\n";
153 $out .= " <dlf:identifier>" . $itemnumber . "</dlf:identifier>\n";
154 $out .= " <dlf:availabilitystatus>" . $status . "</dlf:availabilitystatus>\n";
155 if ($msg) { $out .= " <dlf:availabilitymsg>" . $msg . "</dlf:availabilitymsg>\n"; }
156 if ($location) { $out .= " <dlf:location>" . $location . "</dlf:location>\n"; }
157 if ($itemcallnumber) { $out .= " <dlf:itemcallnumber>" . $itemcallnumber. "</dlf:itemcallnumber>\n"; }
158 $out .= " </dlf:simpleavailability>\n";
159 $out .= " </dlf:item>\n";
162 $out .= " </dlf:items>\n";
163 $out .= " </dlf:record>\n";
166 $msg = "Error: could not retrieve availability for this ID";
170 $out .= "</dlf:collection>\n";
177 Given a list of biblionumbers, returns a list of record objects that
178 contain bibliographic information, as well as associated holdings and item
179 information. The caller may request a specific metadata schema for the
180 record objects to be returned.
182 This function behaves similarly to HarvestBibliographicRecords and
183 HarvestExpandedRecords in Data Aggregation, but allows quick, real time
184 lookup by bibliographic identifier.
186 You can use OAI-PMH ListRecords instead of this service.
191 list of system record identifiers
193 Defines the metadata schema in which the records are returned,
202 # Check if the schema is supported. For now, GetRecords only supports MARCXML
203 if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
204 return { code => 'UnsupportedSchema' };
209 # Loop over biblionumbers
210 foreach my $biblionumber ( split( / /, $cgi->param('id') ) ) {
212 # Get the biblioitem from the biblionumber
213 my $biblio = Koha::Biblios->find( $biblionumber );
215 push @records, { code => "RecordNotFound" };
219 my $biblioitem = $biblio->biblioitem->unblessed;
222 my $record = GetMarcBiblio({
223 biblionumber => $biblionumber,
224 embed_items => $embed_items });
226 $biblioitem->{marcxml} = $record->as_xml_record();
229 # Get most of the needed data
230 my $biblioitemnumber = $biblioitem->{'biblioitemnumber'};
231 my $checkouts = Koha::Checkouts->search(
232 { biblionumber => $biblionumber },
235 '+select' => ['item.barcode'],
236 '+as' => ['barcode'],
239 foreach my $checkout (@$checkouts) {
240 delete $checkout->{'borrowernumber'};
242 my @items = $biblio->items->as_list;
244 $biblioitem->{items}->{item} = [];
246 # We loop over the items to clean them
247 foreach my $item (@items) {
248 my %item = %{ $item->unblessed };
250 # This hides additionnal XML subfields, we don't need these info
251 delete $item{'more_subfields_xml'};
253 # Display branch names instead of branch codes
254 my $home_library = $item->home_branch;
255 my $holding_library = $item->holding_branch;
256 $item{'homebranchname'} = $home_library ? $home_library->branchname : '';
257 $item{'holdingbranchname'} = $holding_library ? $holding_library->branchname : '';
259 if ($item->location) {
260 my $authorised_value = Koha::AuthorisedValues->find_by_koha_field({ kohafield => 'items.location', authorised_value => $item->location });
261 if ($authorised_value) {
262 $item{location_description} = $authorised_value->opac_description;
267 my $itemtype = Koha::ItemTypes->find($item->itype);
269 $item{itype_description} = $itemtype->description;
273 my $transfer = $item->get_transfer;
276 datesent => $transfer->datesent,
277 frombranch => $transfer->frombranch,
278 tobranch => $transfer->tobranch,
282 push @{ $biblioitem->{items}->{item} }, \%item;
286 my $holds = $biblio->current_holds->unblessed;
287 foreach my $hold (@$holds) {
288 delete $hold->{'borrowernumber'};
291 # Hashref building...
292 $biblioitem->{'reserves'}->{'reserve'} = $holds;
293 $biblioitem->{'issues'}->{'issue'} = $checkouts;
295 push @records, $biblioitem;
298 return { record => \@records };
301 =head2 GetAuthorityRecords
303 Given a list of authority record identifiers, returns a list of record
304 objects that contain the authority records. The function user may request
305 a specific metadata schema for the record objects.
310 list of authority record identifiers
312 specifies the metadata schema of records to be returned, possible values:
317 sub GetAuthorityRecords {
320 # If the user asks for an unsupported schema, return an error code
321 if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
322 return { code => 'UnsupportedSchema' };
327 # Let's loop over the authority IDs
328 foreach my $authid ( split( / /, $cgi->param('id') ) ) {
330 # Get the record as XML string, or error code
331 push @records, GetAuthorityXML($authid) || { code => 'RecordNotFound' };
334 return { record => \@records };
339 Looks up a patron in the ILS by an identifier, and returns the borrowernumber.
344 an identifier used to look up the patron in Koha
346 the type of the identifier, possible values:
359 my $id = $cgi->param('id');
361 return { message => 'PatronNotFound' };
365 my $passed_id_type = $cgi->param('id_type');
366 if($passed_id_type) {
367 $patrons = Koha::Patrons->search( { $passed_id_type => $id } );
369 foreach my $id_type ('cardnumber', 'userid', 'email', 'borrowernumber',
370 'surname', 'firstname') {
371 $patrons = Koha::Patrons->search( { $id_type => $id } );
372 last if($patrons->count);
375 unless ( $patrons->count ) {
376 return { message => 'PatronNotFound' };
379 return { id => $patrons->next->borrowernumber };
382 =head2 AuthenticatePatron
384 Authenticates a user's login credentials and returns the identifier for
389 - username (Required)
390 user's login identifier (userid or cardnumber)
391 - password (Required)
396 sub AuthenticatePatron {
398 my $username = $cgi->param('username');
399 my $password = $cgi->param('password');
400 my ($status, $cardnumber, $userid) = C4::Auth::checkpw( C4::Context->dbh, $username, $password );
401 if ( $status == 1 ) {
403 C4::Auth::track_login_daily( $userid );
405 my $patron = Koha::Patrons->find( { userid => $userid } );
406 return { id => $patron->borrowernumber };
408 elsif ( $status == -2 ){
409 return { code => 'PasswordExpired' };
412 return { code => 'PatronNotFound' };
418 Returns specified information about the patron, based on options in the
419 request. This function can optionally return patron's contact information,
420 fine information, hold request information, and loan information.
424 - patron_id (Required)
426 - show_contact (Optional, default 1)
427 whether or not to return patron's contact information in the response
428 - show_fines (Optional, default 0)
429 whether or not to return fine information in the response
430 - show_holds (Optional, default 0)
431 whether or not to return hold request information in the response
432 - show_loans (Optional, default 0)
433 whether or not to return loan information request information in the response
434 - show_attributes (Optional, default 0)
435 whether or not to return additional patron attributes, when enabled the attributes
436 are limited to those marked as opac visible only.
444 my $borrowernumber = $cgi->param('patron_id');
445 my $patron = Koha::Patrons->find( $borrowernumber );
446 return { code => 'PatronNotFound' } unless $patron;
448 # Cleaning the borrower hashref
449 my $borrower = $patron->unblessed;
450 $borrower->{charges} = sprintf "%.02f", $patron->account->non_issues_charges; # FIXME Formatting should not be done here
451 my $library = Koha::Libraries->find( $borrower->{branchcode} );
452 $borrower->{'branchname'} = $library ? $library->branchname : '';
453 delete $borrower->{'userid'};
454 delete $borrower->{'password'};
456 # Contact fields management
457 if ( defined $cgi->param('show_contact') && $cgi->param('show_contact') eq "0" ) {
459 # Define contact fields
460 my @contactfields = (
461 'email', 'emailpro', 'fax', 'mobile', 'phone', 'phonepro',
462 'streetnumber', 'zipcode', 'city', 'streettype', 'B_address', 'B_city',
463 'B_email', 'B_phone', 'B_zipcode', 'address', 'address2', 'altcontactaddress1',
464 'altcontactaddress2', 'altcontactaddress3', 'altcontactfirstname', 'altcontactphone', 'altcontactsurname', 'altcontactzipcode'
468 foreach my $field (@contactfields) {
469 delete $borrower->{$field};
474 if ( $cgi->param('show_fines') && $cgi->param('show_fines') eq "1" ) {
475 $borrower->{fines}{fine} = $patron->account->lines->unblessed;
478 # Reserves management
479 if ( $cgi->param('show_holds') && $cgi->param('show_holds') eq "1" ) {
481 # Get borrower's reserves
482 my $holds = $patron->holds;
483 while ( my $hold = $holds->next ) {
485 my ( $item, $biblio, $biblioitem ) = ( {}, {}, {} );
486 # Get additional informations
487 if ( $hold->itemnumber ) { # item level holds
488 $item = Koha::Items->find( $hold->itemnumber );
489 $biblio = $item->biblio;
490 $biblioitem = $biblio->biblioitem;
492 # Remove unwanted fields
493 $item = $item->unblessed;
494 delete $item->{more_subfields_xml};
495 $biblio = $biblio->unblessed;
496 $biblioitem = $biblioitem->unblessed;
499 # Add additional fields
500 my $unblessed_hold = $hold->unblessed;
501 $unblessed_hold->{item} = { %$item, %$biblio, %$biblioitem };
502 my $library = Koha::Libraries->find( $hold->branchcode );
503 my $branchname = $library ? $library->branchname : '';
504 $unblessed_hold->{branchname} = $branchname;
505 $biblio = Koha::Biblios->find( $hold->biblionumber ); # Should be $hold->get_biblio
506 $unblessed_hold->{title} = $biblio ? $biblio->title : ''; # Just in case, but should not be needed
508 push @{ $borrower->{holds}{hold} }, $unblessed_hold;
514 if ( $cgi->param('show_loans') && $cgi->param('show_loans') eq "1" ) {
515 my $per_page = $cgi->param('loans_per_page');
516 my $page = $cgi->param('loans_page');
518 my $pending_checkouts = $patron->pending_checkouts;
520 if ($page || $per_page) {
523 $borrower->{total_loans} = $pending_checkouts->count();
524 $pending_checkouts = $pending_checkouts->search(undef, {
531 while ( my $c = $pending_checkouts->next ) {
532 # FIXME We should only retrieve what is needed in the template
533 my $issue = $c->unblessed_all_relateds;
534 delete $issue->{'more_subfields_xml'};
535 push @checkouts, $issue
537 $borrower->{'loans'}->{'loan'} = \@checkouts;
540 my $show_attributes = $cgi->param('show_attributes');
541 if ( $show_attributes && $show_attributes eq "1" ) {
542 # FIXME Regression expected here, we do not retrieve the same field as previously
543 # Waiting for answer on bug 14257 comment 15
544 $borrower->{'attributes'} = [
546 $_->type->opac_display
549 %{ $_->type->unblessed },
550 value => $_->attribute, # Backward compatibility
551 value_description => $_->description, # Awkward retro-compability...
554 } $patron->extended_attributes->search->as_list
558 # Add is expired information
559 $borrower->{'is_expired'} = $patron->is_expired ? 1 : 0;
564 =head2 GetPatronStatus
566 Returns a patron's status information.
570 - patron_id (Required)
575 sub GetPatronStatus {
579 my $borrowernumber = $cgi->param('patron_id');
580 my $patron = Koha::Patrons->find( $borrowernumber );
581 return { code => 'PatronNotFound' } unless $patron;
585 type => $patron->categorycode,
587 expiry => $patron->dateexpiry,
593 Returns information about the services available on a particular item for
598 - patron_id (Required)
608 # Get the member, or return an error code if not found
609 my $borrowernumber = $cgi->param('patron_id');
610 my $patron = Koha::Patrons->find( $borrowernumber );
611 return { code => 'PatronNotFound' } unless $patron;
613 my $borrower = $patron->unblessed;
614 # Get the item, or return an error code if not found
615 my $itemnumber = $cgi->param('item_id');
616 my $item = Koha::Items->find($itemnumber);
617 return { code => 'RecordNotFound' } unless $item;
621 # Reserve level management
622 my $biblionumber = $item->biblionumber;
623 my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
624 if ($canbookbereserved->{status} eq 'OK') {
625 push @availablefor, 'title level hold';
626 my $canitembereserved = IsAvailableForItemLevelRequest($item, $patron);
627 if ($canitembereserved) {
628 push @availablefor, 'item level hold';
632 # Reserve cancellation management
633 my $holds = $patron->holds;
635 while ( my $hold = $holds->next ) { # FIXME This could be improved
636 push @reserveditems, $hold->itemnumber;
638 if ( grep { $itemnumber eq $_ } @reserveditems ) {
639 push @availablefor, 'hold cancellation';
643 my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
645 push @availablefor, 'loan renewal';
649 my $barcode = $item->barcode || '';
650 $barcode = barcodedecode($barcode) if $barcode;
652 my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron, $barcode );
654 # TODO push @availablefor, 'loan';
658 $out->{'AvailableFor'} = \@availablefor;
665 Extends the due date for a borrower's existing issue.
669 - patron_id (Required)
673 - desired_due_date (Required)
674 the date the patron would like the item returned by
681 # Get borrower infos or return an error code
682 my $borrowernumber = $cgi->param('patron_id');
683 my $patron = Koha::Patrons->find( $borrowernumber );
684 return { code => 'PatronNotFound' } unless $patron;
686 # Get the item, or return an error code
687 my $itemnumber = $cgi->param('item_id');
688 my $item = Koha::Items->find($itemnumber);
689 return { code => 'RecordNotFound' } unless $item;
691 # Add renewal if possible
692 my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
693 if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber, undef, undef, undef, undef, 0 ); }
695 my $issue = $item->checkout;
696 return unless $issue; # FIXME should be handled
700 $out->{'renewals'} = $issue->renewals_count;
701 $out->{date_due} = dt_from_string($issue->date_due)->strftime('%Y-%m-%d %H:%M');
702 $out->{'success'} = $renewal[0];
703 $out->{'error'} = $renewal[1];
710 Creates, for a borrower, a biblio-level hold reserve.
714 - patron_id (Required)
718 - request_location (Required)
719 IP address where the end user request is being placed
720 - pickup_location (Optional)
721 a branch code indicating the location to which to deliver the item for pickup
722 - start_date (Optional)
723 date after which hold request is no longer needed if the document has not been made available
724 - expiry_date (Optional)
725 date after which item returned to shelf if item is not picked up
732 # Get the borrower or return an error code
733 my $borrowernumber = $cgi->param('patron_id');
734 my $patron = Koha::Patrons->find( $borrowernumber );
735 return { code => 'PatronNotFound' } unless $patron;
738 # If borrower is restricted return an error code
739 return { code => 'PatronRestricted' } if $patron->is_debarred;
741 # Check for patron expired, category and syspref settings
742 return { code => 'PatronExpired' } if ($patron->category->effective_BlockExpiredPatronOpacActions && $patron->is_expired);
744 # Get the biblio record, or return an error code
745 my $biblionumber = $cgi->param('bib_id');
746 my $biblio = Koha::Biblios->find( $biblionumber );
747 return { code => 'RecordNotFound' } unless $biblio;
749 my @hostitems = get_hostitemnumbers_of($biblionumber);
752 push(@itemnumbers, @hostitems);
755 my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
757 unless ( $items->count ) {
758 return { code => 'NoItems' };
761 my $title = $biblio ? $biblio->title : '';
763 # Check if the biblio can be reserved
764 my $code = CanBookBeReserved( $borrowernumber, $biblionumber )->{status};
765 return { code => $code } unless ( $code eq 'OK' );
769 # Pickup branch management
770 if ( $cgi->param('pickup_location') ) {
771 $branch = $cgi->param('pickup_location');
772 return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
773 } else { # if the request provide no branch, use the borrower's branch
774 $branch = $patron->branchcode;
777 my $destination = Koha::Libraries->find($branch);
778 return { code => 'libraryNotPickupLocation' } unless $destination->pickup_location;
779 return { code => 'cannotBeTransferred' } unless $biblio->can_be_transferred({ to => $destination });
782 if ( $cgi->param('start_date') ) {
783 $resdate = $cgi->param('start_date');
787 if ( $cgi->param('expiry_date') ) {
788 $expdate = $cgi->param('expiry_date');
792 # $branch, $borrowernumber, $biblionumber,
793 # $constraint, $bibitems, $priority, $resdate, $expdate, $notes,
794 # $title, $checkitem, $found
795 my $priority= C4::Reserves::CalculatePriority( $biblionumber );
798 branchcode => $branch,
799 borrowernumber => $borrowernumber,
800 biblionumber => $biblionumber,
801 priority => $priority,
802 reservation_date => $resdate,
803 expiration_date => $expdate,
810 $out->{'title'} = $title;
811 my $library = Koha::Libraries->find( $branch );
812 $out->{'pickup_location'} = $library ? $library->branchname : '';
814 # TODO $out->{'date_available'} = '';
821 Creates, for a borrower, an item-level hold request on a specific item of
822 a bibliographic record in Koha.
826 - patron_id (Required)
832 - pickup_location (Optional)
833 a branch code indicating the location to which to deliver the item for pickup
834 - start_date (Optional)
835 date after which hold request is no longer needed if the item has not been made available
836 - expiry_date (Optional)
837 date after which item returned to shelf if item is not picked up
844 # Get the borrower or return an error code
845 my $borrowernumber = $cgi->param('patron_id');
846 my $patron = Koha::Patrons->find( $borrowernumber );
847 return { code => 'PatronNotFound' } unless $patron;
849 # If borrower is restricted return an error code
850 return { code => 'PatronRestricted' } if $patron->is_debarred;
852 # Check for patron expired, category and syspref settings
853 return { code => 'PatronExpired' } if ($patron->category->effective_BlockExpiredPatronOpacActions && $patron->is_expired);
855 # Get the biblio or return an error code
856 my $biblionumber = $cgi->param('bib_id');
857 my $biblio = Koha::Biblios->find( $biblionumber );
858 return { code => 'RecordNotFound' } unless $biblio;
860 my $title = $biblio ? $biblio->title : '';
862 # Get the item or return an error code
863 my $itemnumber = $cgi->param('item_id');
864 my $item = Koha::Items->find($itemnumber);
865 return { code => 'RecordNotFound' } unless $item;
867 # If the biblio does not match the item, return an error code
868 return { code => 'RecordNotFound' } if $item->biblionumber ne $biblio->biblionumber;
870 # Pickup branch management
872 if ( $cgi->param('pickup_location') ) {
873 $branch = $cgi->param('pickup_location');
874 return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
875 } else { # if the request provide no branch, use the borrower's branch
876 $branch = $patron->branchcode;
879 # Check for item disponibility
880 my $canitembereserved = C4::Reserves::CanItemBeReserved( $patron, $item, $branch )->{status};
881 return { code => $canitembereserved } unless $canitembereserved eq 'OK';
884 if ( $cgi->param('start_date') ) {
885 $resdate = $cgi->param('start_date');
889 if ( $cgi->param('expiry_date') ) {
890 $expdate = $cgi->param('expiry_date');
894 my $priority = C4::Reserves::CalculatePriority($biblionumber);
897 branchcode => $branch,
898 borrowernumber => $borrowernumber,
899 biblionumber => $biblionumber,
900 priority => $priority,
901 reservation_date => $resdate,
902 expiration_date => $expdate,
904 itemnumber => $itemnumber,
910 my $library = Koha::Libraries->find( $branch );
911 $out->{'pickup_location'} = $library ? $library->branchname : '';
913 # TODO $out->{'date_available'} = '';
920 Cancels an active reserve request for the borrower.
924 - patron_id (Required)
934 # Get the borrower or return an error code
935 my $borrowernumber = $cgi->param('patron_id');
936 my $patron = Koha::Patrons->find( $borrowernumber );
937 return { code => 'PatronNotFound' } unless $patron;
939 # Get the reserve or return an error code
940 my $reserve_id = $cgi->param('item_id');
941 my $hold = Koha::Holds->find( $reserve_id );
942 return { code => 'RecordNotFound' } unless $hold;
944 # Check if reserve belongs to the borrower and if it is in a state which allows cancellation
945 return { code => 'BorrowerCannotCancelHold' } unless CanReserveBeCanceledFromOpac( $reserve_id, $borrowernumber );
949 return { code => 'Canceled' };
954 Returns, for an itemnumber, an array containing availability information.
956 my ($biblionumber, $status, $msg, $location) = _availability($id);
961 my ($itemnumber) = @_;
962 my $item = Koha::Items->find($itemnumber);
967 return ( undef, __('unknown'), __('Error: could not retrieve availability for this ID'), undef );
970 my $biblionumber = $item->biblioitemnumber;
971 my $library = Koha::Libraries->find( $item->holdingbranch );
972 my $location = $library ? $library->branchname : '';
973 my $itemcallnumber = $item->itemcallnumber;
975 if ( $item->is_notforloan ) {
976 return ( $biblionumber, __('not available'), __('Not for loan'), $location, $itemcallnumber );
977 } elsif ( $item->onloan ) {
978 return ( $biblionumber, __('not available'), __('Checked out'), $location, $itemcallnumber );
979 } elsif ( $item->itemlost ) {
980 return ( $biblionumber, __('not available'), __('Item lost'), $location, $itemcallnumber );
981 } elsif ( $item->withdrawn ) {
982 return ( $biblionumber, __('not available'), __('Item withdrawn'), $location, $itemcallnumber );
983 } elsif ( $item->damaged ) {
984 return ( $biblionumber, __('not available'), __('Item damaged'), $location, $itemcallnumber );
986 return ( $biblionumber, __('available'), undef, $location, $itemcallnumber );