From 7fdfb4a0cdfa0220e87083bcbef3a323c2bccc14 Mon Sep 17 00:00:00 2001 From: Aleisha Amohia Date: Mon, 4 Oct 2021 17:22:38 +1300 Subject: [PATCH] Issue 1: Convert reserves to recalls on staff client (cherry picked from commit 1c6c784e4c392ca5d12bd003559146fce60dcaed) Issue 1: Convert recalls and place staff-side recalls (cherry picked from commit 3a02cb1c0769213d9a5988600fadf7e305cc3637) https://bugs.koha-community.org/show_bug.cgi?id=31391 --- C4/Reserves.pm | 20 ++ Koha/Item.pm | 4 +- .../intranet-tmpl/prog/en/includes/holds_table.inc | 6 + .../prog/en/modules/recalls/request.tt | 233 +++++++++++++++++++-- .../prog/en/modules/reserve/request.tt | 8 + .../intranet-tmpl/prog/js/patron-autocomplete.js | 5 +- recalls/request.pl | 107 +++++++++- reserve/modrequest.pl | 11 + reserve/request.pl | 5 + 9 files changed, 375 insertions(+), 24 deletions(-) diff --git a/C4/Reserves.pm b/C4/Reserves.pm index d5e01f283d..3fd3618af4 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -1119,6 +1119,26 @@ sub ModReserve { if ( $rank eq "del" ) { $hold->cancel({ cancellation_reason => $cancellation_reason }); } + elsif ( $rank eq "recall" ) { + if ( !$biblionumber ) { + $biblionumber = $hold->biblionumber; + } + my $biblio = Koha::Biblios->find( $biblionumber ); + if ( !$borrowernumber ) { + $borrowernumber = $hold->borrowernumber; + } + my $patron = Koha::Patrons->find( $borrowernumber ); + if ( $biblio->can_be_recalled({ patron => $patron }) ) { + my ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({ + patron => $patron, + biblio => $biblio, + branchcode => $branchcode, + expirationdate => $date, + interface => 'intranet', + }); + $hold->cancel({ cancellation_reason => 'RECALLED' }); + } + } elsif ($hold->found && $hold->priority eq '0' && $date) { logaction( 'HOLDS', 'MODIFY', $hold->reserve_id, $hold ) if C4::Context->preference('HoldsLog'); diff --git a/Koha/Item.pm b/Koha/Item.pm index 653eb73d79..b31701ba62 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -1839,10 +1839,12 @@ sub can_be_waiting_recall { $branchcode = ( C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ) ? $self->homebranch : $self->holdingbranch; } + my $maybe_recall = $self->check_recalls; + # Check the circulation rule for each relevant itemtype for this item my $rule = Koha::CirculationRules->get_effective_rules({ branchcode => $branchcode, - categorycode => undef, + categorycode => $maybe_recall ? $maybe_recall->patron->categorycode : undef, itemtype => $self->effective_itemtype, rules => [ 'recalls_allowed', diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc index 0eebedc585..8f0a96f3dd 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc @@ -64,6 +64,9 @@ [% END %] + [% IF Koha.Preference('UseRecalls') AND Koha.Preference('RecallsInterface') != 'opac' %] + + [% END %] [% ELSE %] @@ -79,6 +82,9 @@ [% ELSE %] [% END %] + [% IF Koha.Preference('UseRecalls') AND Koha.Preference('RecallsInterface') != 'opac' %] + + [% END %] [% END %] [% ELSE %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt index 2f4a573cf8..3da6909bd2 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt @@ -2,8 +2,17 @@ [% USE Asset %] [% USE Koha %] [% USE KohaDates %] +[% USE TablesSettings %] +[% USE Branches %] +[% USE Categories %] +[% USE AuthorisedValues %] +[% USE ItemTypes %] [% SET footerjs = 1 %] [% INCLUDE 'doc-head-open.inc' %] +[% SET libraries = Branches.all %] +[% SET categories = Categories.all.unblessed %] +[% SET columns = ['name', 'cardnumber', 'dateofbirth', 'category', 'branch', 'address', 'phone'] %] +[% PROCESS "patron-search.inc" %] Confirm recalls › Recalls › Koha [% INCLUDE 'doc-head-close.inc' %] @@ -11,35 +20,193 @@ [% INCLUDE 'header.inc' %] [% INCLUDE 'circ-search.inc' %] - +
-

Existing recalls

+

Place a recall on [% INCLUDE 'biblio-title.inc' link = 1 %]

- [% IF Koha.Preference('UseRecalls') %] - [% IF recalls.count %] -
- - Select all - [% INCLUDE 'recalls.inc' %] + [% IF patron %] + + [% IF error %] +
+ Unable to place a recall. Check your circulation rules. +
+ [% END %] + +
+ Recall details + + + +
    +
  1. + [% INCLUDE 'patron-title.inc' %] + +
  2. + + [% UNLESS ( single_branch_mode ) %]
  3. + + +
  4. [% END %] + +
  5. [% INCLUDE 'date-format.inc' %]
  6. + + + + + + + + + + [% UNLESS ( single_branch_mode ) %] + + + [% END %] + + + + + + + [% FOREACH item IN items %] + + + + [% UNLESS ( single_branch_mode ) %] + + + [% END %] + + + + + + [% END %] +
    Select a specific item:
     Item typeBarcodeHome libraryLast locationCollectionCall numberCopy numberVol infoInformation
    + [% IF item.can_be_recalled( patron => patron ) %] + + [% ELSE %] + + Cannot be recalled + [% END %] + [% ItemTypes.GetDescription( item.effective_itemtype ) | html %][% item.barcode | html %][% Branches.GetName( item.homebranch ) | html %][% Branches.GetName( item.holdingbranch) | html %][% AuthorisedValues.GetByCode( 'CCODE', item.ccode, 1 ) | html %][% item.itemcallnumber | html %][% item.copynumber | html %][% item.enumchron | html %] + [% IF ( item.checkout ) %] + Due [% item.checkout.date_due | $KohaDates %] + [% ELSIF ( item.get_transfer ) %] + In transit from [% Branches.GetName( item.get_transfer.frombranch ) | html %] to [% Branches.GetName( item.get_transfer.tobranch ) | html %] since [% item.get_transfer.datesent | $KohaDates %] + [% END %] + [% IF ( item.itemlost || item.withdrawn ) %] + Unavailable (lost or missing) + [% END %] + [% IF ( item.notforloan ) %] + Not for loan ([% item.notforloan | html %]) + [% END %] + [% hold = item.current_holds.next %] + [% IF ( item.recall ) %] + + [% IF ( item.recall.waitingdate ) %] + Waiting for patron at [% Branches.GetName( item.recall.branchcode ) | html %] since [% item.recall.waitingdate | $KohaDates %]. + [% ELSIF ( item.recall.itemnumber == item.itemnumber ) %] + Recalled by patron expected at [% Branches.GetName( item.recall.branchcode ) | html %] since [% item.recall.recalldate | $KohaDates %]. + [% END %] + + [% ELSIF ( hold.waitingdate ) %] + + Waiting for patron at [% Branches.GetName( hold.branchcode ) | html %] since [% hold.waitingdate | $KohaDates %]. + + [% ELSIF ( hold.borrowernumber == logged_in_user.borrowernumber ) %] + + Patron has already placed a reserve on this item. + + [% END # / IF ( item.recall or hold ) %] +
    +
- + + + [% IF error %] + + [% ELSE %] + + [% END %] + Cancel
+ + [% ELSE %] + + [% IF Koha.Preference('RecallsInterface') == 'opac' %] +
+ Due to the RecallsInterface system preference, recalls cannot be placed through the staff client. +
[% ELSE %] -
No recalls have been made.
+ +
+

Search patrons

+
+ +
+
+ [% PROCESS patron_search_filters_simple %] + [% PROCESS patron_search_table table_id => 'table_borrowers', open_on_row_click => 1 %] +
+
+
+
+ [% END %] - [% ELSE %] -
Recalls have not been enabled. Enable the UseRecalls system preference to use recalls.
+ +

Existing recalls

+ + [% IF Koha.Preference('UseRecalls') %] + [% IF recalls.count %] +
+ + Select all + [% INCLUDE 'recalls.inc' %] +
+ +
+
+ [% ELSE %] +
No recalls have been made.
+ [% END %] + [% ELSE %] +
Recalls have not been enabled. Enable the UseRecalls system preference to use recalls.
+ [% END %] + [% END %]
@@ -58,6 +225,38 @@ [% Asset.js("js/recalls.js") | $raw %] [% INCLUDE 'datatables.inc' %] [% INCLUDE 'columns_settings.inc' %] + [% INCLUDE 'calendar.inc' %] + [% SET url_biblio_params = "biblionumber=" _ biblionumbers.join("&biblionumber=") %] + [% PROCESS patron_search_js table_id => 'table_borrowers', categories => categories, libraries => libraries, extended_attribute_types => attribute_type_codes, columns => columns, open_on_row_click => 1, on_click_url => '/cgi-bin/koha/recalls/request.pl?' _ url_biblio_params, redirect_if_one_result => 1, redirect_url => '/cgi-bin/koha/recalls/request.pl?' _ url_biblio_params, redirect_if_attribute_equal => 'cardnumber' %] + [% END %] [% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt index 4003c1cb0b..496b75ad77 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt @@ -216,6 +216,14 @@

No club with this name, please, try another

[% END %] + + [% IF ( recallerror ) %] +
+

Hold cannot be converted to a recall

+

A recall cannot be placed on one or more holds. Check your circulation and fine rules.

+
+ [% END %] +
[% UNLESS multi_hold %] [% IF clubcount %] diff --git a/koha-tmpl/intranet-tmpl/prog/js/patron-autocomplete.js b/koha-tmpl/intranet-tmpl/prog/js/patron-autocomplete.js index 6057cb8473..cf96f0067f 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/patron-autocomplete.js +++ b/koha-tmpl/intranet-tmpl/prog/js/patron-autocomplete.js @@ -13,6 +13,7 @@ function patron_autocomplete(node, options) { on_select_callback = options['on-select-callback']; } } + console.log(link_to); const search_fields = ['me.surname', 'me.firstname', 'me.cardnumber']; return node.autocomplete({ source: function( request, response ) { @@ -67,7 +68,9 @@ function patron_autocomplete(node, options) { ? "/cgi-bin/koha/circ/circulation.pl" : link_to == 'reserve' ? "/cgi-bin/koha/reserve/request.pl" - : "/cgi-bin/koha/members/moremember.pl"; + : link_to == 'recall' + ? "/cgi-bin/koha/recalls/request.pl" + : "/cgi-bin/koha/members/moremember.pl"; item.link += ( url_params ? '?' + url_params + '&' : "?" ) + 'borrowernumber=' + item.patron_id; } else { item.link = null; diff --git a/recalls/request.pl b/recalls/request.pl index 222f7ab38b..eec78148f3 100755 --- a/recalls/request.pl +++ b/recalls/request.pl @@ -38,26 +38,123 @@ my ($template, $loggedinuser, $cookie)= get_template_and_user( my $op = $input->param('op') || 'list'; my @recall_ids = $input->multi_param('recall_ids'); my $biblionumber = $input->param('biblionumber'); -my $recalls = Koha::Recalls->search({ biblio_id => $biblionumber, completed => 0 }); my $biblio = Koha::Biblios->find( $biblionumber ); +my $findborrower = $input->param('findborrower'); +$findborrower = '' unless defined $findborrower; +$findborrower =~ s|,| |g; +my $error = $input->param('error'); if ( $op eq 'cancel_multiple_recalls' ) { foreach my $id ( @recall_ids ) { - Koha::Recalls->find( $id )->set_cancelled; + my $recall = Koha::Recalls->find( $id )->set_cancelled; + $biblionumber = $recall->biblionumber; } - $op = 'list' } +elsif ( $op eq 'patron_select' ) { + if ( $findborrower ) { + my $patron = Koha::Patrons->find( $findborrower ); + $biblionumber = $input->param('biblionumber'); + $biblio = Koha::Biblios->find( $biblionumber ); + + if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsInterface') ne 'opac' and $patron and $patron->borrowernumber ) { + if ( !$biblio->can_be_recalled({ patron => $patron }) ) { + $error = 1; + } -if ( $op eq 'list' ) { - $recalls = Koha::Recalls->search({ biblio_id => $biblionumber, completed => 0 }); + $template->param( + patron => $patron, + error => $error, + ); + } + } else { + print $input->redirect("/cgi-bin/koha/recalls/request.pl?biblionumber=$biblionumber"); + } +} +elsif ( $op eq 'request' ) { + my $pickuploc = $input->param('pickup'); + my $expdate = $input->param('expirationdate'); + my $level = $input->param('type'); + my $itemnumber = $input->param('itemnumber'); + my $borrowernumber = $input->param('borrowernumber'); + $biblionumber = $input->param('biblionumber'); $biblio = Koha::Biblios->find( $biblionumber ); + + if ( C4::Context->preference('UseRecalls') and C4::Context->preference('RecallsInterface') ne 'opac' and $borrowernumber ) { + my $patron = Koha::Patrons->find( $borrowernumber ); + + unless ( $biblio->can_be_recalled({ patron => $patron }) ) { $error = 'unavailable'; } + my $items = Koha::Items->search({ biblionumber => $biblionumber }); + + # check if already recalled + my $recalled = scalar $biblio->recalls; + if ( defined $recalled and $recalled > 0 ) { + my $recalls_per_record = Koha::CirculationRules->get_effective_rule({ + categorycode => $patron->categorycode, + branchcode => undef, + itemtype => undef, + rule_name => 'recalls_per_record' + }); + if ( defined $recalls_per_record and $recalls_per_record->rule_value and $recalled >= $recalls_per_record->rule_value ){ + $error = 'duplicate'; + } + } + + # submitting recall request + if ( defined $error and $error eq 'unavailable' ){ + # no items available for recall + print $input->redirect("/cgi-bin/koha/recalls/request.pl?biblionumber=$biblionumber&error=unavailable"); + + } elsif ( !defined $error ){ + my ( $recall, $due_interval, $due_date ); + if ( $level eq 'itemlevel' and defined $itemnumber ) { + my $item = Koha::Items->find( $itemnumber ); + if ( $item->can_be_recalled({ patron => $patron }) ) { + ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({ + patron => $patron, + biblio => $biblio, + branchcode => $pickuploc, + item => $item, + expirationdate => $expdate, + interface => 'staff', + }); + } else { + $error = 'cannot'; + } + } else { + if ( $biblio->can_be_recalled({ patron => $patron }) ) { + ( $recall, $due_interval, $due_date ) = Koha::Recalls->add_recall({ + patron => $patron, + biblio => $biblio, + branchcode => $pickuploc, + expirationdate => $expdate, + interface => 'staff', + }); + } else { + $error = 'cannot'; + } + } + if ( !defined $recall ) { + $error = 'failed'; + } + } + } } +my $recalls = Koha::Recalls->search({ biblio_id => $biblionumber, completed => 0 }); +my $branches = Koha::Libraries->search; +my $single_branch_mode = $branches->count == 1; +my $items = Koha::Items->search({ biblionumber => $biblionumber }); + $template->param( recalls => $recalls, recallsview => 1, biblio => $biblio, checkboxes => 1, + findborrower => $findborrower, + single_branch_mode => $single_branch_mode, + branches => $branches, + items => $items, + error => $error, C4::Search::enabled_staff_search_views, ); diff --git a/reserve/modrequest.pl b/reserve/modrequest.pl index 8fa885dd23..919375fbf4 100755 --- a/reserve/modrequest.pl +++ b/reserve/modrequest.pl @@ -53,6 +53,7 @@ my @branch = $query->multi_param('pickup'); my @itemnumber = $query->multi_param('itemnumber'); my @biblionumber = $query->multi_param('biblionumber'); my $count=@rank; +my $recallerror=0; @biblionumber = uniq @biblionumber; @@ -87,6 +88,13 @@ else { $params->{reservedate} = $reservedates[$i] ? dt_from_string($reservedates[$i]) : undef; } + if ( $rank[$i] eq 'recall' and $recallerror == 0 ) { + my $hold = Koha::Holds->find( $reserve_id[$i] ); + if ( !$hold->biblio->can_be_recalled({ patron => $hold->patron }) ) { + $recallerror++; + } + } + try { ModReserve($params); } catch { @@ -114,5 +122,8 @@ if ( $from eq 'borrower'){ } else { my $url = URI->new("/cgi-bin/koha/reserve/request.pl"); $url->query_form( biblionumber => [@biblionumber]); + if ( $recallerror ) { + $url->query_form( recallerror => 1 ); + } print $query->redirect($url); } diff --git a/reserve/request.pl b/reserve/request.pl index 12c675240f..49a301af92 100755 --- a/reserve/request.pl +++ b/reserve/request.pl @@ -727,6 +727,11 @@ $template->param( borrowernumber => $borrowernumber_hold, ); +my $recallerror = $input->param('recallerror'); +if ( $recallerror ) { + $template->param( recallerror => 1 ); +} + # printout the page output_html_with_http_headers $input, $cookie, $template->output; -- 2.11.0