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::Reserves qw( CanBookBeReserved IsAvailableForItemLevelRequest CalculatePriority AddReserve CanItemBeReserved CanReserveBeCanceledFromOpac );
33 use Koha::DateUtils qw( dt_from_string );
34 use C4::AuthoritiesMarc qw( GetAuthorityXML );
44 C4::ILS-DI::Services - ILS-DI Services
48 Each function in this module represents an ILS-DI service.
49 They all takes a CGI instance as argument and most of them return a
50 hashref that will be printed by XML::Simple in opac/ilsdi.pl
54 use C4::ILSDI::Services;
60 $out = LookupPatron($cgi);
62 print CGI::header('text/xml');
67 xmldecl => '<?xml version="1.0" encoding="UTF-8" ?>',
68 RootName => 'LookupPatron',
75 =head2 GetAvailability
77 Given a set of biblionumbers or itemnumbers, returns a list with
78 availability of the items associated with the identifiers.
84 list of either biblionumbers or itemnumbers
86 =head3 id_type (Required)
88 defines the type of record identifier being used in the request,
94 =head3 return_type (Optional)
96 requests a particular level of detail in reporting availability,
102 =head3 return_fmt (Optional)
104 requests a particular format or set of formats in reporting
109 sub GetAvailability {
112 my $out = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
113 $out .= "<dlf:collection\n";
114 $out .= " xmlns:dlf=\"http://diglib.org/ilsdi/1.1\"\n";
115 $out .= " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
116 $out .= " xsi:schemaLocation=\"http://diglib.org/ilsdi/1.1\n";
117 $out .= " http://diglib.org/architectures/ilsdi/schemas/1.1/dlfexpanded.xsd\">\n";
119 foreach my $id ( split( / /, $cgi->param('id') ) ) {
120 if ( $cgi->param('id_type') eq "item" ) {
121 my ( $biblionumber, $status, $msg, $location, $itemcallnumber ) = _availability($id);
123 $out .= " <dlf:record>\n";
124 $out .= " <dlf:bibliographic id=\"" . ( $biblionumber || $id ) . "\" />\n";
125 $out .= " <dlf:items>\n";
126 $out .= " <dlf:item id=\"" . $id . "\">\n";
127 $out .= " <dlf:simpleavailability>\n";
128 $out .= " <dlf:identifier>" . $id . "</dlf:identifier>\n";
129 $out .= " <dlf:availabilitystatus>" . $status . "</dlf:availabilitystatus>\n";
130 if ($msg) { $out .= " <dlf:availabilitymsg>" . $msg . "</dlf:availabilitymsg>\n"; }
131 if ($location) { $out .= " <dlf:location>" . $location . "</dlf:location>\n"; }
132 if ($itemcallnumber) { $out .= " <dlf:itemcallnumber>" . $itemcallnumber. "</dlf:itemcallnumber>\n"; }
133 $out .= " </dlf:simpleavailability>\n";
134 $out .= " </dlf:item>\n";
135 $out .= " </dlf:items>\n";
136 $out .= " </dlf:record>\n";
140 my $items = Koha::Items->search({ biblionumber => $id });
143 $out .= " <dlf:record>\n";
144 $out .= " <dlf:bibliographic id=\"" .$id. "\" />\n";
145 $out .= " <dlf:items>\n";
146 # We loop over the items to clean them
147 while ( my $item = $items->next ) {
148 my $itemnumber = $item->itemnumber;
149 my ( $biblionumber, $status, $msg, $location, $itemcallnumber ) = _availability($itemnumber);
150 $out .= " <dlf:item id=\"" . $itemnumber . "\">\n";
151 $out .= " <dlf:simpleavailability>\n";
152 $out .= " <dlf:identifier>" . $itemnumber . "</dlf:identifier>\n";
153 $out .= " <dlf:availabilitystatus>" . $status . "</dlf:availabilitystatus>\n";
154 if ($msg) { $out .= " <dlf:availabilitymsg>" . $msg . "</dlf:availabilitymsg>\n"; }
155 if ($location) { $out .= " <dlf:location>" . $location . "</dlf:location>\n"; }
156 if ($itemcallnumber) { $out .= " <dlf:itemcallnumber>" . $itemcallnumber. "</dlf:itemcallnumber>\n"; }
157 $out .= " </dlf:simpleavailability>\n";
158 $out .= " </dlf:item>\n";
161 $out .= " </dlf:items>\n";
162 $out .= " </dlf:record>\n";
165 $msg = "Error: could not retrieve availability for this ID";
169 $out .= "</dlf:collection>\n";
176 Given a list of biblionumbers, returns a list of record objects that
177 contain bibliographic information, as well as associated holdings and item
178 information. The caller may request a specific metadata schema for the
179 record objects to be returned.
181 This function behaves similarly to HarvestBibliographicRecords and
182 HarvestExpandedRecords in Data Aggregation, but allows quick, real time
183 lookup by bibliographic identifier.
185 You can use OAI-PMH ListRecords instead of this service.
190 list of system record identifiers
192 Defines the metadata schema in which the records are returned,
201 # Check if the schema is supported. For now, GetRecords only supports MARCXML
202 if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
203 return { code => 'UnsupportedSchema' };
208 # Loop over biblionumbers
209 foreach my $biblionumber ( split( / /, $cgi->param('id') ) ) {
211 # Get the biblioitem from the biblionumber
212 my $biblio = Koha::Biblios->find( $biblionumber );
214 push @records, { code => "RecordNotFound" };
218 my $biblioitem = $biblio->biblioitem->unblessed;
220 my $record = $biblio->metadata->record({ embed_items => 1 });
222 $biblioitem->{marcxml} = $record->as_xml_record();
225 # Get most of the needed data
226 my $biblioitemnumber = $biblioitem->{'biblioitemnumber'};
227 my $checkouts = Koha::Checkouts->search(
228 { biblionumber => $biblionumber },
231 '+select' => ['item.barcode'],
232 '+as' => ['barcode'],
235 foreach my $checkout (@$checkouts) {
236 delete $checkout->{'borrowernumber'};
238 my @items = $biblio->items->as_list;
240 $biblioitem->{items}->{item} = [];
242 # We loop over the items to clean them
243 foreach my $item (@items) {
244 my %item = %{ $item->unblessed };
246 # This hides additionnal XML subfields, we don't need these info
247 delete $item{'more_subfields_xml'};
249 # Display branch names instead of branch codes
250 my $home_library = $item->home_branch;
251 my $holding_library = $item->holding_branch;
252 $item{'homebranchname'} = $home_library ? $home_library->branchname : '';
253 $item{'holdingbranchname'} = $holding_library ? $holding_library->branchname : '';
255 if ($item->location) {
256 my $authorised_value = Koha::AuthorisedValues->find_by_koha_field({ kohafield => 'items.location', authorised_value => $item->location });
257 if ($authorised_value) {
258 $item{location_description} = $authorised_value->opac_description;
263 my $itemtype = Koha::ItemTypes->find($item->itype);
265 $item{itype_description} = $itemtype->description;
269 my $transfer = $item->get_transfer;
272 datesent => $transfer->datesent,
273 frombranch => $transfer->frombranch,
274 tobranch => $transfer->tobranch,
278 push @{ $biblioitem->{items}->{item} }, \%item;
282 my $holds = $biblio->current_holds->unblessed;
283 foreach my $hold (@$holds) {
284 delete $hold->{'borrowernumber'};
287 # Hashref building...
288 $biblioitem->{'reserves'}->{'reserve'} = $holds;
289 $biblioitem->{'issues'}->{'issue'} = $checkouts;
291 push @records, $biblioitem;
294 return { record => \@records };
297 =head2 GetAuthorityRecords
299 Given a list of authority record identifiers, returns a list of record
300 objects that contain the authority records. The function user may request
301 a specific metadata schema for the record objects.
306 list of authority record identifiers
308 specifies the metadata schema of records to be returned, possible values:
313 sub GetAuthorityRecords {
316 # If the user asks for an unsupported schema, return an error code
317 if ( $cgi->param('schema') and $cgi->param('schema') ne "MARCXML" ) {
318 return { code => 'UnsupportedSchema' };
323 # Let's loop over the authority IDs
324 foreach my $authid ( split( / /, $cgi->param('id') ) ) {
326 # Get the record as XML string, or error code
327 push @records, GetAuthorityXML($authid) || { code => 'RecordNotFound' };
330 return { record => \@records };
335 Looks up a patron in the ILS by an identifier, and returns the borrowernumber.
340 an identifier used to look up the patron in Koha
342 the type of the identifier, possible values:
355 my $id = $cgi->param('id');
357 return { message => 'PatronNotFound' };
361 my $passed_id_type = $cgi->param('id_type');
362 if($passed_id_type) {
363 $patrons = Koha::Patrons->search( { $passed_id_type => $id } );
365 foreach my $id_type ('cardnumber', 'userid', 'email', 'borrowernumber',
366 'surname', 'firstname') {
367 $patrons = Koha::Patrons->search( { $id_type => $id } );
368 last if($patrons->count);
371 unless ( $patrons->count ) {
372 return { message => 'PatronNotFound' };
375 return { id => $patrons->next->borrowernumber };
378 =head2 AuthenticatePatron
380 Authenticates a user's login credentials and returns the identifier for
385 - username (Required)
386 user's login identifier (userid or cardnumber)
387 - password (Required)
392 sub AuthenticatePatron {
394 my $username = $cgi->param('username');
395 my $password = $cgi->param('password');
396 my ($status, $cardnumber, $userid) = C4::Auth::checkpw( $username, $password );
397 if ( $status == 1 ) {
399 C4::Auth::track_login_daily( $userid );
401 my $patron = Koha::Patrons->find( { userid => $userid } );
402 return { id => $patron->borrowernumber };
404 elsif ( $status == -2 ){
405 return { code => 'PasswordExpired' };
408 return { code => 'PatronNotFound' };
414 Returns specified information about the patron, based on options in the
415 request. This function can optionally return patron's contact information,
416 fine information, hold request information, and loan information.
420 - patron_id (Required)
422 - show_contact (Optional, default 1)
423 whether or not to return patron's contact information in the response
424 - show_fines (Optional, default 0)
425 whether or not to return fine information in the response
426 - show_holds (Optional, default 0)
427 whether or not to return hold request information in the response
428 - show_loans (Optional, default 0)
429 whether or not to return loan information request information in the response
430 - show_attributes (Optional, default 0)
431 whether or not to return additional patron attributes, when enabled the attributes
432 are limited to those marked as opac visible only.
440 my $borrowernumber = $cgi->param('patron_id');
441 my $patron = Koha::Patrons->find( $borrowernumber );
442 return { code => 'PatronNotFound' } unless $patron;
444 # Cleaning the borrower hashref
445 my $borrower = $patron->unblessed;
446 $borrower->{charges} = sprintf "%.02f", $patron->account->non_issues_charges; # FIXME Formatting should not be done here
447 my $library = Koha::Libraries->find( $borrower->{branchcode} );
448 $borrower->{'branchname'} = $library ? $library->branchname : '';
449 delete $borrower->{'userid'};
450 delete $borrower->{'password'};
452 # Contact fields management
453 if ( defined $cgi->param('show_contact') && $cgi->param('show_contact') eq "0" ) {
455 # Define contact fields
456 my @contactfields = (
457 'email', 'emailpro', 'fax', 'mobile', 'phone', 'phonepro',
458 'streetnumber', 'zipcode', 'city', 'streettype', 'B_address', 'B_city',
459 'B_email', 'B_phone', 'B_zipcode', 'address', 'address2', 'altcontactaddress1',
460 'altcontactaddress2', 'altcontactaddress3', 'altcontactfirstname', 'altcontactphone', 'altcontactsurname', 'altcontactzipcode'
464 foreach my $field (@contactfields) {
465 delete $borrower->{$field};
470 if ( $cgi->param('show_fines') && $cgi->param('show_fines') eq "1" ) {
471 $borrower->{fines}{fine} = $patron->account->lines->unblessed;
474 # Reserves management
475 if ( $cgi->param('show_holds') && $cgi->param('show_holds') eq "1" ) {
477 # Get borrower's reserves
478 my $holds = $patron->holds;
479 while ( my $hold = $holds->next ) {
481 my ( $item, $biblio, $biblioitem ) = ( {}, {}, {} );
482 # Get additional informations
483 if ( $hold->itemnumber ) { # item level holds
484 $item = Koha::Items->find( $hold->itemnumber );
485 $biblio = $item->biblio;
486 $biblioitem = $biblio->biblioitem;
488 # Remove unwanted fields
489 $item = $item->unblessed;
490 delete $item->{more_subfields_xml};
491 $biblio = $biblio->unblessed;
492 $biblioitem = $biblioitem->unblessed;
495 # Add additional fields
496 my $unblessed_hold = $hold->unblessed;
497 $unblessed_hold->{item} = { %$item, %$biblio, %$biblioitem };
498 my $library = Koha::Libraries->find( $hold->branchcode );
499 my $branchname = $library ? $library->branchname : '';
500 $unblessed_hold->{branchname} = $branchname;
501 $biblio = Koha::Biblios->find( $hold->biblionumber ); # Should be $hold->get_biblio
502 $unblessed_hold->{title} = $biblio ? $biblio->title : ''; # Just in case, but should not be needed
504 push @{ $borrower->{holds}{hold} }, $unblessed_hold;
510 if ( $cgi->param('show_loans') && $cgi->param('show_loans') eq "1" ) {
511 my $per_page = $cgi->param('loans_per_page');
512 my $page = $cgi->param('loans_page');
514 my $pending_checkouts = $patron->pending_checkouts;
516 if ($page || $per_page) {
519 $borrower->{total_loans} = $pending_checkouts->count();
520 $pending_checkouts = $pending_checkouts->search(undef, {
527 while ( my $c = $pending_checkouts->next ) {
528 # FIXME We should only retrieve what is needed in the template
529 my $issue = $c->unblessed_all_relateds;
530 delete $issue->{'more_subfields_xml'};
531 push @checkouts, $issue
533 $borrower->{'loans'}->{'loan'} = \@checkouts;
536 my $show_attributes = $cgi->param('show_attributes');
537 if ( $show_attributes && $show_attributes eq "1" ) {
538 # FIXME Regression expected here, we do not retrieve the same field as previously
539 # Waiting for answer on bug 14257 comment 15
540 $borrower->{'attributes'} = [
542 $_->type->opac_display
545 %{ $_->type->unblessed },
546 value => $_->attribute, # Backward compatibility
547 value_description => $_->description, # Awkward retro-compability...
550 } $patron->extended_attributes->search->as_list
554 # Add is expired information
555 $borrower->{'is_expired'} = $patron->is_expired ? 1 : 0;
560 =head2 GetPatronStatus
562 Returns a patron's status information.
566 - patron_id (Required)
571 sub GetPatronStatus {
575 my $borrowernumber = $cgi->param('patron_id');
576 my $patron = Koha::Patrons->find( $borrowernumber );
577 return { code => 'PatronNotFound' } unless $patron;
581 type => $patron->categorycode,
583 expiry => $patron->dateexpiry,
589 Returns information about the services available on a particular item for
594 - patron_id (Required)
604 # Get the member, or return an error code if not found
605 my $borrowernumber = $cgi->param('patron_id');
606 my $patron = Koha::Patrons->find( $borrowernumber );
607 return { code => 'PatronNotFound' } unless $patron;
609 my $borrower = $patron->unblessed;
610 # Get the item, or return an error code if not found
611 my $itemnumber = $cgi->param('item_id');
612 my $item = Koha::Items->find($itemnumber);
613 return { code => 'RecordNotFound' } unless $item;
617 # Reserve level management
618 my $biblionumber = $item->biblionumber;
619 my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
620 if ($canbookbereserved->{status} eq 'OK') {
621 push @availablefor, 'title level hold';
622 my $canitembereserved = IsAvailableForItemLevelRequest($item, $patron);
623 if ($canitembereserved) {
624 push @availablefor, 'item level hold';
628 # Reserve cancellation management
629 my $holds = $patron->holds;
631 while ( my $hold = $holds->next ) { # FIXME This could be improved
632 push @reserveditems, $hold->itemnumber;
634 if ( grep { $itemnumber eq $_ } @reserveditems ) {
635 push @availablefor, 'hold cancellation';
639 my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
641 push @availablefor, 'loan renewal';
645 my $barcode = $item->barcode || '';
646 $barcode = barcodedecode($barcode) if $barcode;
648 my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron, $barcode );
650 # TODO push @availablefor, 'loan';
654 $out->{'AvailableFor'} = \@availablefor;
661 Extends the due date for a borrower's existing issue.
665 - patron_id (Required)
669 - desired_due_date (Required)
670 the date the patron would like the item returned by
677 # Get borrower infos or return an error code
678 my $borrowernumber = $cgi->param('patron_id');
679 my $patron = Koha::Patrons->find( $borrowernumber );
680 return { code => 'PatronNotFound' } unless $patron;
682 # Get the item, or return an error code
683 my $itemnumber = $cgi->param('item_id');
684 my $item = Koha::Items->find($itemnumber);
685 return { code => 'RecordNotFound' } unless $item;
687 # Add renewal if possible
688 my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber );
689 if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber, undef, undef, undef, undef, 0 ); }
691 my $issue = $item->checkout;
692 return unless $issue; # FIXME should be handled
696 $out->{'renewals'} = $issue->renewals_count;
697 $out->{date_due} = dt_from_string($issue->date_due)->strftime('%Y-%m-%d %H:%M');
698 $out->{'success'} = $renewal[0];
699 $out->{'error'} = $renewal[1];
706 Creates, for a borrower, a biblio-level hold reserve.
710 - patron_id (Required)
714 - request_location (Required)
715 IP address where the end user request is being placed
716 - pickup_location (Optional)
717 a branch code indicating the location to which to deliver the item for pickup
718 - start_date (Optional)
719 date after which hold request is no longer needed if the document has not been made available
720 - expiry_date (Optional)
721 date after which item returned to shelf if item is not picked up
728 # Get the borrower or return an error code
729 my $borrowernumber = $cgi->param('patron_id');
730 my $patron = Koha::Patrons->find( $borrowernumber );
731 return { code => 'PatronNotFound' } unless $patron;
734 # If borrower is restricted return an error code
735 return { code => 'PatronRestricted' } if $patron->is_debarred;
737 # Check for patron expired, category and syspref settings
738 return { code => 'PatronExpired' } if ($patron->category->effective_BlockExpiredPatronOpacActions && $patron->is_expired);
740 # Get the biblio record, or return an error code
741 my $biblionumber = $cgi->param('bib_id');
742 my $biblio = Koha::Biblios->find( $biblionumber );
743 return { code => 'RecordNotFound' } unless $biblio;
745 my @hostitems = get_hostitemnumbers_of($biblionumber);
748 push(@itemnumbers, @hostitems);
751 my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
753 unless ( $items->count ) {
754 return { code => 'NoItems' };
757 my $title = $biblio ? $biblio->title : '';
759 # Check if the biblio can be reserved
760 my $code = CanBookBeReserved( $borrowernumber, $biblionumber )->{status};
761 return { code => $code } unless ( $code eq 'OK' );
765 # Pickup branch management
766 if ( $cgi->param('pickup_location') ) {
767 $branch = $cgi->param('pickup_location');
768 return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
769 } else { # if the request provide no branch, use the borrower's branch
770 $branch = $patron->branchcode;
773 my $destination = Koha::Libraries->find($branch);
774 return { code => 'libraryNotPickupLocation' } unless $destination->pickup_location;
775 return { code => 'cannotBeTransferred' } unless $biblio->can_be_transferred({ to => $destination });
778 if ( $cgi->param('start_date') ) {
779 $resdate = $cgi->param('start_date');
783 if ( $cgi->param('expiry_date') ) {
784 $expdate = $cgi->param('expiry_date');
788 # $branch, $borrowernumber, $biblionumber,
789 # $constraint, $bibitems, $priority, $resdate, $expdate, $notes,
790 # $title, $checkitem, $found
791 my $priority= C4::Reserves::CalculatePriority( $biblionumber );
794 branchcode => $branch,
795 borrowernumber => $borrowernumber,
796 biblionumber => $biblionumber,
797 priority => $priority,
798 reservation_date => $resdate,
799 expiration_date => $expdate,
806 $out->{'title'} = $title;
807 my $library = Koha::Libraries->find( $branch );
808 $out->{'pickup_location'} = $library ? $library->branchname : '';
810 # TODO $out->{'date_available'} = '';
817 Creates, for a borrower, an item-level hold request on a specific item of
818 a bibliographic record in Koha.
822 - patron_id (Required)
828 - pickup_location (Optional)
829 a branch code indicating the location to which to deliver the item for pickup
830 - start_date (Optional)
831 date after which hold request is no longer needed if the item has not been made available
832 - expiry_date (Optional)
833 date after which item returned to shelf if item is not picked up
840 # Get the borrower or return an error code
841 my $borrowernumber = $cgi->param('patron_id');
842 my $patron = Koha::Patrons->find( $borrowernumber );
843 return { code => 'PatronNotFound' } unless $patron;
845 # If borrower is restricted return an error code
846 return { code => 'PatronRestricted' } if $patron->is_debarred;
848 # Check for patron expired, category and syspref settings
849 return { code => 'PatronExpired' } if ($patron->category->effective_BlockExpiredPatronOpacActions && $patron->is_expired);
851 # Get the biblio or return an error code
852 my $biblionumber = $cgi->param('bib_id');
853 my $biblio = Koha::Biblios->find( $biblionumber );
854 return { code => 'RecordNotFound' } unless $biblio;
856 my $title = $biblio ? $biblio->title : '';
858 # Get the item or return an error code
859 my $itemnumber = $cgi->param('item_id');
860 my $item = Koha::Items->find($itemnumber);
861 return { code => 'RecordNotFound' } unless $item;
863 # If the biblio does not match the item, return an error code
864 return { code => 'RecordNotFound' } if $item->biblionumber ne $biblio->biblionumber;
866 # Pickup branch management
868 if ( $cgi->param('pickup_location') ) {
869 $branch = $cgi->param('pickup_location');
870 return { code => 'LocationNotFound' } unless Koha::Libraries->find($branch);
871 } else { # if the request provide no branch, use the borrower's branch
872 $branch = $patron->branchcode;
875 # Check for item disponibility
876 my $canitembereserved = C4::Reserves::CanItemBeReserved( $patron, $item, $branch )->{status};
877 return { code => $canitembereserved } unless $canitembereserved eq 'OK';
880 if ( $cgi->param('start_date') ) {
881 $resdate = $cgi->param('start_date');
885 if ( $cgi->param('expiry_date') ) {
886 $expdate = $cgi->param('expiry_date');
890 my $priority = C4::Reserves::CalculatePriority($biblionumber);
893 branchcode => $branch,
894 borrowernumber => $borrowernumber,
895 biblionumber => $biblionumber,
896 priority => $priority,
897 reservation_date => $resdate,
898 expiration_date => $expdate,
900 itemnumber => $itemnumber,
906 my $library = Koha::Libraries->find( $branch );
907 $out->{'pickup_location'} = $library ? $library->branchname : '';
909 # TODO $out->{'date_available'} = '';
916 Cancels an active reserve request for the borrower.
920 - patron_id (Required)
930 # Get the borrower or return an error code
931 my $borrowernumber = $cgi->param('patron_id');
932 my $patron = Koha::Patrons->find( $borrowernumber );
933 return { code => 'PatronNotFound' } unless $patron;
935 # Get the reserve or return an error code
936 my $reserve_id = $cgi->param('item_id');
937 my $hold = Koha::Holds->find( $reserve_id );
938 return { code => 'RecordNotFound' } unless $hold;
940 # Check if reserve belongs to the borrower and if it is in a state which allows cancellation
941 return { code => 'BorrowerCannotCancelHold' } unless CanReserveBeCanceledFromOpac( $reserve_id, $borrowernumber );
945 return { code => 'Canceled' };
950 Returns, for an itemnumber, an array containing availability information.
952 my ($biblionumber, $status, $msg, $location) = _availability($id);
957 my ($itemnumber) = @_;
958 my $item = Koha::Items->find($itemnumber);
963 return ( undef, __('unknown'), __('Error: could not retrieve availability for this ID'), undef );
966 my $biblionumber = $item->biblioitemnumber;
967 my $library = Koha::Libraries->find( $item->holdingbranch );
968 my $location = $library ? $library->branchname : '';
969 my $itemcallnumber = $item->itemcallnumber;
971 if ( $item->is_notforloan ) {
972 return ( $biblionumber, __('not available'), __('Not for loan'), $location, $itemcallnumber );
973 } elsif ( $item->onloan ) {
974 return ( $biblionumber, __('not available'), __('Checked out'), $location, $itemcallnumber );
975 } elsif ( $item->itemlost ) {
976 return ( $biblionumber, __('not available'), __('Item lost'), $location, $itemcallnumber );
977 } elsif ( $item->withdrawn ) {
978 return ( $biblionumber, __('not available'), __('Item withdrawn'), $location, $itemcallnumber );
979 } elsif ( $item->damaged ) {
980 return ( $biblionumber, __('not available'), __('Item damaged'), $location, $itemcallnumber );
982 return ( $biblionumber, __('available'), undef, $location, $itemcallnumber );