From e7b2746aa4aaca1445f08baa5116a52046005c3c Mon Sep 17 00:00:00 2001 From: Aleisha Amohia Date: Mon, 11 May 2020 23:52:09 +0000 Subject: [PATCH] Bug 19532: Recalls on intranet See recalls on Intranet - old recalls (all inactive recalls) - recalls queue (all active recalls) - cancel, expire, revert waiting status, multiple cancel, mark overdue - recalls to pull (available but not yet waiting) - cancel - recalls awaiting pickup (awaiting pickup, awaiting pickup more than RecallMaxPickUpDelay days) - expire, revert waiting status - overdue recalls (overdue to be returned) - cancel, multiple cancel - biblio recalls tab (all active recalls relevant to this bib) - cancel, expire, revert waiting status, mark overdue - patron recalls tab (all active recalls relevant to this patron) - cancel, expire, revert waiting status, mark overdue - patron recalls history tab (all recalls relevant to this patron) - cancel, expire, revert waiting status, mark overdue - log viewer and the general circulation of recalls Test plan in Comment 482. --- catalogue/detail.pl | 6 + circ/circulation.pl | 10 +- circ/returns.pl | 71 +++++++ circ/transferstoreceive.pl | 7 + .../prog/en/includes/biblio-view-menu.inc | 3 + .../intranet-tmpl/prog/en/includes/circ-menu.inc | 3 + .../intranet-tmpl/prog/en/includes/circ-nav.inc | 8 + .../prog/en/includes/patron-title.inc | 1 + .../intranet-tmpl/prog/en/includes/recalls.inc | 131 +++++++++++++ .../intranet-tmpl/prog/en/includes/strings.inc | 1 + .../prog/en/modules/catalogue/detail.tt | 15 +- .../prog/en/modules/catalogue/results.tt | 6 + .../prog/en/modules/cataloguing/addbooks.tt | 1 + .../prog/en/modules/circ/circulation-home.tt | 12 ++ .../prog/en/modules/circ/circulation.tt | 47 ++++- .../prog/en/modules/circ/printslip.tt | 2 + .../intranet-tmpl/prog/en/modules/circ/renew.tt | 3 + .../intranet-tmpl/prog/en/modules/circ/returns.tt | 203 +++++++++++++++++++++ .../prog/en/modules/circ/transferstoreceive.tt | 2 + .../prog/en/modules/members/moremember.tt | 15 ++ .../prog/en/modules/members/recallshistory.tt | 48 +++++ .../prog/en/modules/recalls/recalls_old_queue.tt | 56 ++++++ .../prog/en/modules/recalls/recalls_overdue.tt | 67 +++++++ .../prog/en/modules/recalls/recalls_queue.tt | 67 +++++++ .../prog/en/modules/recalls/recalls_to_pull.tt | 175 ++++++++++++++++++ .../prog/en/modules/recalls/recalls_waiting.tt | 181 ++++++++++++++++++ .../prog/en/modules/recalls/request.tt | 63 +++++++ .../prog/en/modules/reserve/request.tt | 4 +- .../intranet-tmpl/prog/en/modules/tools/viewlog.tt | 14 +- koha-tmpl/intranet-tmpl/prog/js/checkouts.js | 15 +- koha-tmpl/intranet-tmpl/prog/js/recalls.js | 147 +++++++++++++++ members/moremember.pl | 2 + members/recallshistory.pl | 47 +++++ recalls/recall_pickup_slip.pl | 74 ++++++++ recalls/recalls_old_queue.pl | 47 +++++ recalls/recalls_overdue.pl | 61 +++++++ recalls/recalls_queue.pl | 57 ++++++ recalls/recalls_to_pull.pl | 126 +++++++++++++ recalls/recalls_waiting.pl | 76 ++++++++ recalls/request.pl | 65 +++++++ reserve/request.pl | 4 + svc/checkouts | 21 +++ svc/recall | 79 ++++++++ tools/letter.pl | 2 +- tools/viewlog.pl | 10 + 45 files changed, 2045 insertions(+), 10 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/recalls.inc create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/recallshistory.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_old_queue.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_overdue.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_queue.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_to_pull.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_waiting.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/js/recalls.js create mode 100755 members/recallshistory.pl create mode 100755 recalls/recall_pickup_slip.pl create mode 100755 recalls/recalls_old_queue.pl create mode 100755 recalls/recalls_overdue.pl create mode 100755 recalls/recalls_queue.pl create mode 100755 recalls/recalls_to_pull.pl create mode 100755 recalls/recalls_waiting.pl create mode 100755 recalls/request.pl create mode 100755 svc/recall diff --git a/catalogue/detail.pl b/catalogue/detail.pl index c968e33dc9..5a564286f3 100755 --- a/catalogue/detail.pl +++ b/catalogue/detail.pl @@ -395,6 +395,12 @@ foreach my $item (@items) { } } + my $recall = Koha::Recalls->find({ itemnumber => $item->{itemnumber}, old => undef }); + if ( defined $recall ) { + $item->{recalled} = 1; + $item->{recall} = $recall; + } + if ($currentbranch and C4::Context->preference('SeparateHoldings')) { if ($itembranchcode and $itembranchcode eq $currentbranch) { push @itemloop, $item; diff --git a/circ/circulation.pl b/circ/circulation.pl index 5ec80c8f15..28b5646a86 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -178,6 +178,8 @@ if ( $restoreduedatespec && $restoreduedatespec eq "highholds_empty" ) { } my $issueconfirmed = $query->param('issueconfirmed'); my $cancelreserve = $query->param('cancelreserve'); +my $cancel_recall = $query->param('cancel_recall'); +my $recall_id = $query->param('recall_id'); my $debt_confirmed = $query->param('debt_confirmed') || 0; # Don't show the debt error dialog twice my $charges = $query->param('charges') || q{}; @@ -410,7 +412,11 @@ if (@$barcodes) { } unless($confirm_required) { my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED}; - my $issue = AddIssue( $patron->unblessed, $barcode, $datedue, $cancelreserve, undef, undef, { onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), switch_onsite_checkout => $switch_onsite_checkout, } ); + my $recall = Koha::Recalls->find({ borrowernumber => $patron->borrowernumber, itemnumber => $item->itemnumber, old => undef }); + if ( $recall and !$recall_id ){ + $recall_id = $recall->recall_id; + } + my $issue = AddIssue( $patron->unblessed, $barcode, $datedue, $cancelreserve, undef, undef, { onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), switch_onsite_checkout => $switch_onsite_checkout, cancel_recall => $cancel_recall, recall_id => $recall_id, } ); $template_params->{issue} = $issue; $session->clear('auto_renew'); $inprocess = 1; @@ -457,6 +463,8 @@ if ($patron) { $template->param( holds_count => $holds->count(), WaitingHolds => $waiting_holds, + recalls => $patron->recalls, + specific_patron => 1, ); } diff --git a/circ/returns.pl b/circ/returns.pl index e1bce3c3f1..cab4d6e21f 100755 --- a/circ/returns.pl +++ b/circ/returns.pl @@ -81,6 +81,15 @@ if ( $query->param('print_slip') ) { ); } +# print a recall slip +if ( $query->param('recall_slip') ) { + $template->param( + recall_slip => 1, + recall_id => scalar $query->param('recall_id'), + ); +} + + ##################### #Global vars my $userenv = C4::Context->userenv; @@ -176,6 +185,24 @@ if ( $query->param('reserve_id') ) { } } +if ( $query->param('recall_id') ) { + my $recall = Koha::Recalls->find( scalar $query->param('recall_id') ); + my $barcode = $query->param('barcode'); + my $return_branch = $query->param('returnbranch'); + + my $expirationdate = $recall->calc_expirationdate; + my $item; + if ( !$recall->item_level_recall ) { + $item = Koha::Items->find({ barcode => $barcode }); + } + + if ( $recall->branchcode ne $return_branch ) { + $recall->start_transfer({ item => $item }); + } else { + $recall->set_waiting({ item => $item }); + } +} + my $borrower; my $returned = 0; my $messages; @@ -234,6 +261,10 @@ if ($dotransfer){ if ($canceltransfer){ $itemnumber=$query->param('itemnumber'); DeleteTransfer($itemnumber); + my $recall_transfer_deleted = Koha::Recalls->find({ itemnumber => $itemnumber, status => 'T' }); + if ( defined $recall_transfer_deleted ) { + $recall_transfer_deleted->revert_transfer; + } if($dest eq "ttr"){ print $query->redirect("/cgi-bin/koha/circ/transferstoreceive.pl"); exit; @@ -351,6 +382,7 @@ $template->param( inputloop => \@inputloop ); my $found = 0; my $waiting = 0; my $reserved = 0; +my $recalled = 0; # new op dev : we check if the document must be returned to his homebranch directly, # if the document is transferred, we have warning message . @@ -462,6 +494,39 @@ if ( $messages->{'ResFound'}) { ); } +if ( $messages->{RecallFound} ) { + my $recall = $messages->{RecallFound}; + if ( dt_from_string( $recall->timestamp ) == dt_from_string ) { + # we just updated this recall + $template->param( recall => $recall ); + } else { + my $transfertodo = $messages->{RecallNeedsTransfer}; + $template->param( + found => 1, + recall => $recall, + recalled => $recall->waiting ? 0 : 1, + transfertodo => $transfertodo, + waitingrecall => $recall->waiting ? 1 : 0, + ); + } +} + +if ( $messages->{TransferredRecall} ) { + my $recall = $messages->{TransferredRecall}; + + # confirm transfer has arrived at the branch + my $transfer = Koha::Item::Transfers->search({ datearrived => { '!=' => undef }, itemnumber => $recall->itemnumber }, { order_by => { -desc => 'datearrived' } })->next; + + # if transfer has completed, show popup to confirm as waiting + if ( defined $transfer and $transfer->tobranch eq $recall->branchcode ) { + $template->param( + found => 1, + recall => $recall, + recalled => 1, + ); + } +} + # Error Messages my @errmsgloop; foreach my $code ( keys %$messages ) { @@ -534,6 +599,12 @@ foreach my $code ( keys %$messages ) { } elsif ( $code eq 'ReturnClaims' ) { $template->param( ReturnClaims => $messages->{ReturnClaims} ); + } elsif ( $code eq 'RecallFound' ) { + ; + } elsif ( $code eq 'RecallNeedsTransfer' ) { + ; + } elsif ( $code eq 'TransferredRecall' ) { + ; } else { die "Unknown error code $code"; # note we need all the (empty) elsif's above, or we die. # This forces the issue of staying in sync w/ Circulation.pm diff --git a/circ/transferstoreceive.pl b/circ/transferstoreceive.pl index b9123f6c01..33055d3812 100755 --- a/circ/transferstoreceive.pl +++ b/circ/transferstoreceive.pl @@ -117,6 +117,13 @@ while ( my $library = $libraries->next ) { if ( my $first_hold = $holds->next ) { $getransf{patron} = Koha::Patrons->find( $first_hold->borrowernumber ); } + + # check for a recall for this transfer + my $recall = $item->recall; + if ( defined $recall ) { + $getransf{recall} = $recall; + } + push( @transferloop, \%getransf ); } diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc index 2080b07707..4ef1173213 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc @@ -41,5 +41,8 @@ Checkout history [% IF ( CAN_user_tools_view_system_logs ) %][% IF ( logview ) %]
  • [% ELSE %]
  • [% END %]Modification log
  • [% END %] [% IF ( CAN_user_stockrotation_manage_rota_items && Koha.Preference('StockRotation') ) %][% IF ( stockrotationview ) %]
  • [% ELSE %]
  • [% END %]Rota
  • [% END %] + [% IF ( Koha.Preference('UseRecalls') && CAN_user_recalls ) %] + [% IF ( recallsview ) %]
  • [% ELSE %]
  • [% END %]Recalls ([% Biblio.RecallsCount( biblio_object_id ) | html %])
  • + [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc index 5e020f15f3..ebfea96ec6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc @@ -136,6 +136,9 @@ [% IF Koha.Preference('ILLModule') && CAN_user_ill %] [% IF illview %]
  • [% ELSE %]
  • [% END %]ILL requests history
  • [% END %] + [% IF Koha.Preference('UseRecalls') && CAN_user_recalls %] + [% IF recallsview %]
  • [% ELSE %]
  • [% END %]Recalls history
  • + [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc index 979871a0b9..535ba7be90 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc @@ -39,6 +39,14 @@ [% IF Koha.Preference('OnSiteCheckouts') %]
  • Pending on-site checkouts
  • [% END %] + + [% IF Koha.Preference('UseRecalls') and CAN_user_recalls %] +
  • Recalls queue
  • +
  • Recalls to pull
  • +
  • Overdue recalls
  • +
  • Recalls awaiting pickup
  • +
  • Old recalls
  • + [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc index 71a293588a..5a9b14d727 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-title.inc @@ -53,6 +53,7 @@ [%- END -%] [%- IF hide_patron_infos_if_needed AND ( display_patron_name OR display_cardnumber ) -%] [%- IF link_to == 'circulation_reserves' %] + [%- ELSIF link_to == 'circulation_recalls' %] [%- ELSE %] [%- END -%] [%- END -%] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/recalls.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/recalls.inc new file mode 100644 index 0000000000..2f43e9fb87 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/recalls.inc @@ -0,0 +1,131 @@ +
    +[% IF recalls.count %] + + + + [% IF checkboxes %][% END %] + + [% UNLESS specific_patron %][% END %] + + + + + [% UNLESS viewing_old %][% END %] + [% UNLESS viewing_old %][% END %] + + + + + [% FOREACH recall IN recalls %] + [% IF recall.old %][% ELSE %][% END %] + + [% IF checkboxes %] + + [% END %] + + + + [% UNLESS specific_patron %] + + [% END %] + + + + + + + + + + [% UNLESS viewing_old %] + + [% END %] + + [% UNLESS viewing_old %] + + [% END %] + + + [% END %] + +
     TitleRequested byPlaced onExpires onPickup locationStatusDue date 
    + [% IF recall.old %] +   + [% ELSE %] + + [% END %] + + + [% recall.biblio.title | html %] + [% FOREACH s IN recall.biblio.subtitle %] + [% s | html %] + [% END %] + + [% IF recall.item %][% recall.item.barcode | html %][% END %] + [% recall.biblio.author | html %] + + [% recall.patron.firstname | html %] [% recall.patron.surname | html %] ([% recall.patron.cardnumber | html %]) + + [% recall.recalldate | $KohaDates %] + + [% IF ( recall.expirationdate ) %] + [% recall.expirationdate | $KohaDates %] + [% ELSIF ( !recall.old ) %] + Never expires + [% ELSE %] + - + [% END %] + + [% recall.library.branchname | html %] + + [% IF ( recall.in_transit ) %] + In transit to [% recall.library.branchname | html %] + [% ELSIF ( recall.waiting ) %] + Ready for pickup + [% ELSIF ( recall.expired ) %] + Expired on [% recall.expirationdate | $KohaDates %] + [% ELSIF ( recall.cancelled ) %] + Cancelled on [% recall.cancellationdate | $KohaDates %] + [% ELSIF ( recall.overdue ) %] + Overdue to be returned + [% ELSIF ( recall.finished ) %] + Fulfilled + [% ELSE %] + Requested + [% END %] + + [% IF recall.requested and recall.checkout %] + Due to be returned by [% recall.checkout.date_due | $KohaDates %] + [% ELSIF recall.waiting and RECALL.expirationdate %] + Pick up by [% RECALL.expirationdate | $KohaDates %] + [% ELSE %] + - + [% END %] + + [% IF recall.old %] +   + [% ELSE %] +
    + Actions + +
    + [% END %] +
    + [% ELSE %] + [% IF patron.borrowernumber %] +
    Patron has no recalls.
    + [% ELSE %] +
    There are no recalls to show.
    + [% END %] + [% END %] +
    diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc index 26aed46765..e55d106e4d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc @@ -57,5 +57,6 @@ var BROWSER_RETURN_TO_SEARCH = _("Results"); var BROWSER_PREVIOUS = _("Previous"); var BROWSER_NEXT = _("Next"); + var RECALLED = _("Recalled"); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt index efbf34fb5d..949ff0cd0d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt @@ -393,14 +393,22 @@ Note that permanent location is a code, and location may be an authval. There is an item level hold on this item (priority = [% hold.priority | html %]). [% END %] [% END %] - [% UNLESS ( item.itemnotforloan || item.notforloan_per_itemtype || item.onloan || item.itemlost || item.withdrawn || item.damaged || item.transfertwhen || hold ) %] - Available + + [% IF item.recalled %] + [% IF item.recall.waitingdate %] + Waiting at [% Branches.GetName( item.recall.branchcode ) | html %] since [% item.recall.waitingdate | $KohaDates %] + [% ELSE %] + Item recalled by
    [% item.recall.patron.firstname | html %] [% item.recall.patron.surname | html %] ([% item.recall.patron.cardnumber | html %]) on [% item.recall.recalldate | $KohaDates %] + [% END %] + [% END %] + + [% UNLESS ( item.itemnotforloan || item.notforloan_per_itemtype || item.onloan || item.itemlost || item.withdrawn || item.damaged || item.transfertwhen || hold || item.recalled ) %] + Available [% END %] [% IF ( item.restricted ) %] ([% item.restrictedvalue | html %]) [% END %] - [% item.datelastseen | $KohaDates %] [% item.dateaccessioned | $KohaDates %] @@ -902,6 +910,7 @@ Note that permanent location is a code, and location may be an authval. [% MACRO jsinclude BLOCK %] [% INCLUDE 'catalog-strings.inc' %] [% Asset.js("js/catalog.js") | $raw %] + [% Asset.js("js/recalls.js") | $raw %] [% Asset.js("js/coce.js") | $raw %] [% INCLUDE 'str/members-menu.inc' %] [% Asset.js("js/members-menu.js") | $raw %] + [% Asset.js("js/recalls.js") | $raw %] [% END %] [% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt index 78f74fd4c1..bc053f873b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt @@ -8,6 +8,8 @@ Koha › Circulation › Transfers print receipt [% ELSIF ( caller == 'members' ) %] Koha › Patrons › Print receipt for [% borrowernumber | html %] +[% ELSIF ( caller == 'recall' ) %] +Koha › Circulation › Recall print receipt [% ELSIF ( title ) %][%# FIXME title is never defined %] Koha › Patrons › [% title | html %] [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt index 608ed5c84d..c3638f340b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt @@ -137,6 +137,9 @@ [% ELSIF error == "onsite_checkout" %]

    Item cannot be renewed because it's an onsite checkout

    + [% ELSIF error == 'recalled' %] +

    This item has been recalled.

    + [% ELSE %] [% error | html %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt index 57ad49518a..dd3c545d90 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt @@ -652,6 +652,199 @@ [% END #/IF reserved %] + + [% IF ( recalled ) %] + + + [% END #/IF recalled %] + + [% IF ( waitingrecall ) %] + + + [% END #/IF recalledwaiting %] [% END # /IF found %]
    @@ -934,6 +1127,11 @@ [% END %] var columns_settings = [% TablesSettings.GetColumns( 'circ', 'returns', 'checkedintable', 'json' ) | $raw %] + + [% IF recall_slip %] + Dopop('/cgi-bin/koha/recalls/recall_pickup_slip.pl?recall_id=[% recall.recall_id | uri %]'); + [% END %] + var returns_table = KohaTable("checkedintable", { "bFilter":false, "bPaginate":false, @@ -1039,6 +1237,11 @@ this.form.submit(); }); + $('.print-recall').on("click",function(e){ + this.form.recall_slip.value = 1; + this.form.submit(); + }); + $('.cancel-hold').on("click",function(e){ this.form.cancel_reserve.value = 1; this.form.submit(); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/transferstoreceive.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/transferstoreceive.tt index f0c3d4ffdf..dde2729931 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/transferstoreceive.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/transferstoreceive.tt @@ -68,6 +68,8 @@ [% reser.patron.first_valid_email_address | html %] [% END %] + [% ELSIF ( reser.recall ) %] + Recall requested by [% reser.recall.patron.surname | html %][% IF reser.recall.patron.firstname %], [% reser.recall.patron.firstname | html %][% END %] ([% reser.recall.patron.cardnumber | html %]) [% ELSE %]

    None

    [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt index 0757ae74fd..0c8dea4fac 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -765,6 +765,14 @@ [% END %] + + [% IF Koha.Preference('UseRecalls') %] +
  • + + [% recalls.count | html %] Recalls + +
  • + [% END %] [% INCLUDE "checkouts-table.inc" %] @@ -787,6 +795,12 @@
    [% END %] + [% IF Koha.Preference('UseRecalls') %] +
    + [% INCLUDE 'recalls.inc' %] +
    + [% END %] + [% INCLUDE borrower_debarments.inc %] [% IF ( CAN_user_circulate_circulate_remaining_permissions ) %] @@ -904,6 +918,7 @@ [% Asset.js("js/holds.js") | $raw %] [% INCLUDE 'str/members-menu.inc' %] [% Asset.js("js/members-menu.js") | $raw %] + [% Asset.js("js/recalls.js") | $raw %] [% Asset.js("js/messaging-preference-form.js") | $raw %] +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_waiting.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_waiting.tt new file mode 100644 index 0000000000..d7b40e2dc1 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/recalls_waiting.tt @@ -0,0 +1,181 @@ +[% USE raw %] +[% USE Koha %] +[% USE KohaDates %] +[% USE Asset %] +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Circulation › Recalls awaiting pickup +[% INCLUDE 'doc-head-close.inc' %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    + [% IF Koha.Preference('CircSidebar') %] +
    + [% ELSE %] +
    + [% END %] +
    +

    Recalls awaiting pickup

    + + [% IF Koha.Preference('UseRecalls') %] + +
    + + + +
    + [% IF ( recalls ) %] + + + + + + + + + + [% FOREACH recall IN recalls %] + + + + + + [% END %] + +
    Available sinceTitleRequested byPickup location 
    [% recall.waitingdate | $KohaDates %] + + [% recall.biblio.title | html %] + [% FOREACH s IN recall.biblio.subtitle %] + [% s | html %] + [% END %] + + [% recall.biblio.author | html %] +
    Barcode: [% recall.item.barcode | html %] +
    + [% recall.patron.firstname | html %] [% recall.patron.surname | html %] + [% IF ( recall.patron.phone ) %]
    [% recall.patron.phone | html %][% END %] + [% IF ( recall.patron.email ) %]
    [% recall.patron.email | html %][% END %] +
    [% recall.library.branchname | html %] +
    + + +
    + + +
    +
    +
    + [% ELSE %] +
    There are no recalls to show.
    + [% END %] +
    + +
    + [% IF ( over.size ) %] + [% IF ( Koha.Preference('RecallsMaxPickUpDelay') ) %]

    Recalls listed here have been awaiting pickup for more than [% Koha.Preference('RecallsMaxPickUpDelay') | html %] days.

    [% END %] + + + + + + + + + + [% FOREACH recall IN over %] + + + + + + [% END %] + +
    Available sinceTitleRequested byPickup location 
    [% recall.waitingdate | $KohaDates %] + + [% recall.biblio.title | html %] + [% FOREACH s IN recall.biblio.subtitles %] + [% s | html %] + [% END %] + [% recall.item.enumchron | html %] + + [% recall.biblio.author | html %] +
    Barcode: [% recall.item.barcode | html %] +
    + [% recall.patron.firstname | html %] [% recall.patron.surname | html %] + [% IF ( recall.patron.phone ) %]
    [% recall.patron.phone | html %][% END %] + [% IF ( recall.patron.email ) %]
    [% recall.patron.email | html %][% END %] +
    [% recall.library.branchname | html %] +
    + + +
    + + +
    +
    +
    + [% ELSE %] +
    There are no recalls to show.
    + [% END %] +
    + +
    + + [% ELSE %] +
    Recalls have not been enabled. Enable the UseRecalls system preference to use recalls.
    + [% END %] + +
    +
    + + [% IF Koha.Preference('CircSidebar') %] +
    + +
    + [% END %] + +
    +
    + +[% MACRO jsinclude BLOCK %] + [% INCLUDE 'datatables.inc' %] + [% INCLUDE 'columns_settings.inc' %] + +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt new file mode 100644 index 0000000000..c085368bb3 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/recalls/request.tt @@ -0,0 +1,63 @@ +[% USE raw %] +[% USE Asset %] +[% USE Koha %] +[% USE KohaDates %] +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Circulation › Recalls › Confirm recalls +[% INCLUDE 'doc-head-close.inc' %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'circ-search.inc' %] + + + +
    +
    +
    +
    + +

    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 %] + +
    +
    + +
    + +
    + +
    +
    + +[% MACRO jsinclude BLOCK %] + [% Asset.js("js/recalls.js") | $raw %] + [% INCLUDE 'datatables.inc' %] + [% INCLUDE 'columns_settings.inc' %] +[% 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 44c46d2395..bac485ebc6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt @@ -296,7 +296,7 @@
    [% END %] - [% IF ( exceeded_maxreserves || exceeded_holds_per_record || alreadyreserved || none_available || alreadypossession || ageRestricted ) %] + [% IF ( exceeded_maxreserves || exceeded_holds_per_record || alreadyreserved || none_available || alreadypossession || ageRestricted || recall ) %]
    [% UNLESS ( multi_hold ) %] @@ -316,6 +316,8 @@
  • No items are available to be placed on hold.
  • [% ELSIF ( maxreserves ) %]
  • Too many holds: [% patron.firstname | html %] [% patron.surname | html %] has too many holds.
  • + [% ELSIF ( recall ) %] +
  • [% patron.firstname | html %] [% patron.surname | html %] has already placed a recall on this item.
  • [% END # /IF exceeded_maxreserves %] [% ELSE # UNLESS multi_hold %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt index 259d36d32b..963875ea99 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/viewlog.tt @@ -53,6 +53,7 @@ fieldset.rows label.viewlog { [% CASE 'SYSTEMPREFERENCE' %]System prefs [% CASE 'CRONJOBS' %]Cron jobs [% CASE 'REPORTS' %]Reports +[% CASE 'RECALLS' %]Recalls [% CASE %][% module | html %] [% END %] [% END %] @@ -75,6 +76,9 @@ fieldset.rows label.viewlog { [% CASE 'DELCIRCMESSAGE' %]Delete circulation message [% CASE 'STATUS_CHANGE' %]Change ILL request status [% CASE 'Run' %]Run +[% CASE 'FULFILL' %]Fulfill +[% CASE 'OVERDUE' %]Overdue +[% CASE 'EXPIRE' %]Expire [% CASE %][% action | html %] [% END %] [% END %] @@ -124,7 +128,7 @@ fieldset.rows label.viewlog { [% ELSE %] [% END %] - [% FOREACH modx IN [ 'AUTH' 'CATALOGUING' 'AUTHORITIES' 'MEMBERS' 'ACQUISITIONS' 'SERIAL' 'HOLDS' 'ILL' 'CIRCULATION' 'LETTER' 'FINES' 'SYSTEMPREFERENCE' 'CRONJOBS', 'REPORTS' ] %] + [% FOREACH modx IN [ 'AUTH' 'CATALOGUING' 'AUTHORITIES' 'MEMBERS' 'ACQUISITIONS' 'SERIAL' 'HOLDS' 'ILL' 'CIRCULATION' 'LETTER' 'FINES' 'SYSTEMPREFERENCE' 'CRONJOBS' 'REPORTS' 'RECALLS' ] %] [% IF modules.grep(modx).size %] [% ELSE %] @@ -146,7 +150,7 @@ fieldset.rows label.viewlog { [% END %] - [% FOREACH actx IN [ 'ADD' 'DELETE' 'MODIFY' 'ISSUE' 'RETURN' 'RENEW' 'CREATE' 'CANCEL' 'SUSPEND' 'RESUME' 'ADDCIRCMESSAGE' 'DELCIRCMESSAGE' 'STATUS_CHANGE' 'CHANGE PASS' 'Run' ] %] + [% FOREACH actx IN [ 'ADD' 'DELETE' 'MODIFY' 'ISSUE' 'RETURN' 'RENEW' 'CREATE' 'CANCEL' 'SUSPEND' 'RESUME' 'ADDCIRCMESSAGE' 'DELCIRCMESSAGE' 'STATUS_CHANGE' 'CHANGE PASS' 'Run' 'OVERDUE' 'EXPIRE' 'FULFILL' ] %] [% IF actions.grep(actx).size %] [% ELSE %] @@ -264,6 +268,12 @@ fieldset.rows label.viewlog { [% ELSE %] [% loopro.object | html %] [% END %] + [% ELSIF ( loopro.module == 'RECALLS' ) %] + [% IF loopro.recall.item_level_recall %] + Item-level recall on item + [% ELSE %] + Biblio-level recall on biblio + [% END %] [% ELSE %] [% IF ( loopro.module == 'SERIAL' ) %] Subscription [% loopro.object | html %] diff --git a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js index 1b9b4a213d..3b183461ec 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js +++ b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js @@ -331,6 +331,10 @@ $(document).ready(function() { onsite_checkout += " (" + INHOUSE_USE + ")"; } + if ( oObj.recalled == 1 ) { + title += " - This item has been recalled and the due date updated."; + } + title += " " + "" + RECALLED + "" + + ""; + + span_style = "display: none"; + span_class = "renewals-allowed-recalled"; } else if ( oObj.can_renew_error == "on_reserve" ) { msg += "" + "" + ON_HOLD + "" @@ -541,7 +552,9 @@ $(document).ready(function() { "bSortable": false, "bVisible": AllowCirculate ? true : false, "mDataProp": function ( oObj ) { - if ( oObj.can_renew_error == "on_reserve" ) { + if ( oObj.can_renew_error == "recalled" ) { + return "" + RECALLED + ""; + } else if ( oObj.can_renew_error == "on_reserve" ) { return "" + ON_HOLD + ""; } else { return ""; diff --git a/koha-tmpl/intranet-tmpl/prog/js/recalls.js b/koha-tmpl/intranet-tmpl/prog/js/recalls.js new file mode 100644 index 0000000000..ee637de582 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/recalls.js @@ -0,0 +1,147 @@ +$(document).ready(function() { + + $(".cancel_recall").click(function(e){ + if (confirmDelete(_("Are you sure you want to remove this recall?"))){ + var $self = $(this); + var $recall_id = $(this).data('id'); + var $action = $(this).data('action'); + var ajaxData = { + 'recall_id': $recall_id, + 'action' : $action, + }; + + $.ajax({ + url: '/cgi-bin/koha/svc/recall', + type: 'POST', + dataType: 'json', + data: ajaxData, + }) + .done(function(data) { + var message = ""; + if(data.success == 0) { + message = _("The recall may have already been cancelled. Please refresh the page."); + } else { + message = _("Cancelled"); + } + $self.parent().parent().parent().parent().html(message); + }); + } + }); + + $(".expire_recall").click(function(e){ + if (confirmDelete(_("Are you sure you want to expire this recall?"))){ + var $self = $(this); + var $recall_id = $(this).data('id'); + var $action = $(this).data('action'); + var ajaxData = { + 'recall_id': $recall_id, + 'action' : $action, + }; + + $.ajax({ + url: '/cgi-bin/koha/svc/recall', + type: 'POST', + dataType: 'json', + data: ajaxData, + }) + .done(function(data) { + var message = ""; + if(data.success == 0) { + message = _("The recall may have already been expired. Please refresh the page."); + } else { + message = _("Expired"); + } + $self.parent().parent().parent().parent().html(message); + }); + } + }); + + $(".revert_recall").click(function(e){ + if (confirmDelete(_("Are you sure you want to revert the waiting status of this recall?"))){ + var $self = $(this); + var $recall_id = $(this).data('id'); + var $action = $(this).data('action'); + var ajaxData = { + 'recall_id': $recall_id, + 'action' : $action, + }; + + $.ajax({ + url: '/cgi-bin/koha/svc/recall', + type: 'POST', + dataType: 'json', + data: ajaxData, + }) + .done(function(data) { + var message = ""; + if(data.success == 0) { + message = _("The recall waiting status may have already been reverted. Please refresh the page."); + } else { + message = _("Status updated"); + } + $self.parent().parent().parent().parent().html(message); + }); + } + }); + + $(".overdue_recall").click(function(e){ + if (confirmDelete(_("Are you sure you want to mark this recall as overdue?"))){ + var $self = $(this); + var $recall_id = $(this).data('id'); + var $action = $(this).data('action'); + var ajaxData = { + 'recall_id': $recall_id, + 'action' : $action, + }; + + $.ajax({ + url: '/cgi-bin/koha/svc/recall', + type: 'POST', + dataType: 'json', + data: ajaxData, + }) + .done(function(data) { + var message = ""; + if(data.success == 0) { + message = _("The recall may have already been marked as overdue. Please refresh the page."); + } else { + message = _("Marked overdue"); + } + $self.parent().parent().parent().parent().html(message); + }); + } + }); + + $("#recalls-table").dataTable($.extend(true, {}, dataTablesDefaults, { + "aoColumnDefs": [ + { 'bSortable': false, 'aTargets': [ 'nosort' ] }, + { "sType": "title-string", "aTargets" : [ "title-string" ] }, + { "sType": "anti-the", "aTargets": [ "anti-the" ] } + ], + "sPaginationType": "full_numbers" + })); + + $("#cancel_selected").click(function(e){ + if ($("input[name='recall_ids']:checked").length > 0){ + return confirmDelete(_("Are you sure you want to remove the selected recall(s)?")); + } else { + alert(_("Please make a selection.")); + } + }); + + $("#select_all").click(function(){ + if ($("#select_all").prop("checked")){ + $("input[name='recall_ids']").prop("checked", true); + } else { + $("input[name='recall_ids']").prop("checked", false); + } + }); + + $("#hide_old").click(function(){ + if ($("#hide_old").prop("checked")){ + $(".old").show(); + } else { + $(".old").hide(); + } + }); +}); diff --git a/members/moremember.pl b/members/moremember.pl index 656ee35a93..9e8ad05d7e 100755 --- a/members/moremember.pl +++ b/members/moremember.pl @@ -210,6 +210,8 @@ $template->param( relatives_issues_count => $relatives_issues_count, relatives_borrowernumbers => \@relatives, logged_in_user => $logged_in_user, + recalls => $patron->recalls, + specific_patron => 1, ); output_html_with_http_headers $input, $cookie, $template->output; diff --git a/members/recallshistory.pl b/members/recallshistory.pl new file mode 100755 index 0000000000..175f15945e --- /dev/null +++ b/members/recallshistory.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. + +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; + +my $input = CGI->new; +my ($template, $loggedinuser, $cookie)= get_template_and_user( + { + template_name => "members/recallshistory.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => 1 }, + debug => 1, + } +); + +my $borrowernumber = $input->param('borrowernumber'); +my $recalls = Koha::Recalls->search({ borrowernumber => $borrowernumber }, { order_by => { '-desc' => 'recalldate' } }); +my $patron = Koha::Patrons->find( $borrowernumber ); + +$template->param( + patron => $patron, + recalls => $recalls, + recallsview => 1, + specific_patron => 1, +); + +output_html_with_http_headers $input, $cookie, $template->output; diff --git a/recalls/recall_pickup_slip.pl b/recalls/recall_pickup_slip.pl new file mode 100755 index 0000000000..7a130871f2 --- /dev/null +++ b/recalls/recall_pickup_slip.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Context; +use C4::Output; +use C4::Auth; + +my $input = new CGI; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "circ/printslip.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { circulate => "circulate_remaining_permissions" }, + } +); + +my $recallid = $input->param('recall_id'); +my $recall = Koha::Recalls->find($recallid); + +my $itemnumber; +if ( $recall->itemnumber ){ + $itemnumber = $recall->itemnumber; +} else { + $itemnumber = $recall->checkout->itemnumber; +} + +# Print slip to inform library staff of details of recall requester, so the item can be put aside for requester +my $letter = C4::Letters::GetPreparedLetter( + module => 'circulation', + letter_code => 'RECALL_REQUESTER_DET', + message_transport_type => 'print', + tables => { + 'branches' => $recall->branchcode, + 'borrowers' => $recall->borrowernumber, + 'biblio' => $recall->biblionumber, + 'items' => $itemnumber, + 'recalls' => $recall->recall_id, + } +); + +my ($slip, $is_html); +if ($letter) { + $slip = $letter->{content}; + $is_html = $letter->{is_html}; +} + +$template->param( + slip => $slip, + plain => !$is_html, + caller => 'recall', +); + +output_html_with_http_headers $input, $cookie, $template->output; diff --git a/recalls/recalls_old_queue.pl b/recalls/recalls_old_queue.pl new file mode 100755 index 0000000000..161dcd41a1 --- /dev/null +++ b/recalls/recalls_old_queue.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use Koha::BiblioFrameworks; + +my $query = new CGI; +my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user( + { + template_name => "recalls/recalls_old_queue.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => "manage_recalls" }, + debug => 1, + } +); + +my $recalls = Koha::Recalls->search({ old => 1 }); +$template->param( + recalls => $recalls, + viewing_old => 1 +); + +# Checking if there is a Fast Cataloging Framework +$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find( 'FA' ); + +# writing the template +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/recalls/recalls_overdue.pl b/recalls/recalls_overdue.pl new file mode 100755 index 0000000000..91c1e6973d --- /dev/null +++ b/recalls/recalls_overdue.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use Koha::BiblioFrameworks; +use Koha::DateUtils; + +my $query = new CGI; +my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user( + { + template_name => "recalls/recalls_overdue.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => "manage_recalls" }, + debug => 1, + } +); + +my $op = $query->param('op') || 'list'; +my @recall_ids = $query->multi_param('recall_ids'); + +if ( $op eq 'cancel_multiple_recalls' ) { + foreach my $id ( @recall_ids ) { + Koha::Recalls->find( $id )->set_cancelled; + } + $op = 'list' +} + +if ( $op eq 'list' ) { + my $recalls = Koha::Recalls->search({ status => 'O' }); + # will be set as Overdue by the misc/cronjobs/recall/overdue_recalls.pl cronjob + $template->param( + recalls => $recalls, + checkboxes => 1, + ); +} + +# Checking if there is a Fast Cataloging Framework +$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find( 'FA' ); + +# writing the template +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/recalls/recalls_queue.pl b/recalls/recalls_queue.pl new file mode 100755 index 0000000000..0d820592a0 --- /dev/null +++ b/recalls/recalls_queue.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use Koha::BiblioFrameworks; + +my $query = new CGI; +my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user( + { + template_name => "recalls/recalls_queue.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => 'manage_recalls' }, + debug => 1, + } +); + +my $op = $query->param('op') || 'list'; +my @recall_ids = $query->multi_param('recall_ids'); +if ( $op eq 'cancel_multiple_recalls' ) { + foreach my $id ( @recall_ids ) { + Koha::Recalls->find( $id )->set_cancelled; + } + $op = 'list' +} +elsif ( $op eq 'list' ) { + my $recalls = Koha::Recalls->search({ old => undef }); + $template->param( + recalls => $recalls, + checkboxes => 1, + ); +} + +# Checking if there is a Fast Cataloging Framework +$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find( 'FA' ); + +# writing the template +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/recalls/recalls_to_pull.pl b/recalls/recalls_to_pull.pl new file mode 100755 index 0000000000..8582de1460 --- /dev/null +++ b/recalls/recalls_to_pull.pl @@ -0,0 +1,126 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use Koha::BiblioFrameworks; + +my $query = new CGI; +my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user( + { + template_name => "recalls/recalls_to_pull.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => 'manage_recalls' }, + debug => 1, + } +); + +my $op = $query->param('op') || 'list'; +my $recall_id = $query->param('recall_id'); +if ( $op eq 'cancel' ) { + my $recall = Koha::Recalls->find( $recall_id ); + if ( $recall->in_transit ) { + C4::Items::ModItemTransfer( $recall->item->itemnumber, $recall->item->holdingbranch, $recall->item->homebranch, 'CancelRecall' ); + } + $recall->set_cancelled; + $op = 'list'; +} + +if ( $op eq 'list' ) { + my @recalls = Koha::Recalls->search({ status => [ 'R','O','T' ] }); + my @pull_list; + my %seen_bib; + foreach my $recall ( @recalls ) { + if ( $seen_bib{$recall->biblionumber} ){ + # we've already looked at the recalls on this biblio + next; + } else { + # this is an unseen biblio + $seen_bib{$recall->biblionumber}++; + + # get recall data about this biblio + my @this_bib_recalls = Koha::Recalls->search({ biblionumber => $recall->biblionumber, status => [ 'R','O','T' ] }, { order_by => { -asc => 'recalldate' } }); + my $recalls_count = scalar @this_bib_recalls; + my @unique_patrons = do { my %seen; grep { !$seen{$_->borrowernumber}++ } @this_bib_recalls }; + my $patrons_count = scalar @unique_patrons; + my $first_recall = $this_bib_recalls[0]; + + my $items_count = 0; + my @callnumbers; + my @copynumbers; + my @enumchrons; + my @itemtypes; + my @locations; + my @libraries; + + my @items = Koha::Items->search({ biblionumber => $recall->biblionumber }); + foreach my $item ( @items ) { + if ( $item->can_be_waiting_recall and !$item->checkout ) { + # if item can be pulled to fulfill recall, collect item data + $items_count++; + push( @callnumbers, $item->itemcallnumber ) if ( $item->itemcallnumber ); + push( @copynumbers, $item->copynumber ) if ( $item->copynumber ); + push( @enumchrons, $item->enumchron ) if ( $item->enumchron ); + push( @itemtypes, $item->effective_itemtype ) if ( $item->effective_itemtype ); + push( @locations, $item->location ) if ( $item->location ); + push( @libraries, $item->holdingbranch ) if ( $item->holdingbranch ); + } + } + + if ( $items_count > 0 ) { + # don't push data if there are no items available for this recall + + # get unique values + my @unique_callnumbers = do { my %seen; grep { !$seen{$_}++ } @callnumbers }; + my @unique_copynumbers = do { my %seen; grep { !$seen{$_}++ } @copynumbers }; + my @unique_enumchrons = do { my %seen; grep { !$seen{$_}++ } @enumchrons }; + my @unique_itemtypes = do { my %seen; grep { !$seen{$_}++ } @itemtypes }; + my @unique_locations = do { my %seen; grep { !$seen{$_}++ } @locations }; + my @unique_libraries = do { my %seen; grep { !$seen{$_}++ } @libraries }; + + push( @pull_list, { + biblio => $recall->biblio, + items_count => $items_count, + recalls_count => $recalls_count, + patrons_count => $patrons_count, + pull_count => $items_count <= $recalls_count ? $items_count : $recalls_count, + first_recall => $first_recall, + callnumbers => \@unique_callnumbers, + copynumbers => \@unique_copynumbers, + enumchrons => \@unique_enumchrons, + itemtypes => \@unique_itemtypes, + locations => \@unique_locations, + libraries => \@unique_libraries, + }); + } + } + } + $template->param( + recalls => \@pull_list, + ); +} + +# Checking if there is a Fast Cataloging Framework +$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find( 'FA' ); + +# writing the template +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/recalls/recalls_waiting.pl b/recalls/recalls_waiting.pl new file mode 100755 index 0000000000..4026fee1dc --- /dev/null +++ b/recalls/recalls_waiting.pl @@ -0,0 +1,76 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use Koha::Recalls; +use Koha::BiblioFrameworks; +use Koha::DateUtils; +use Koha::Patrons; + +my $query = new CGI; +my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user( + { + template_name => "recalls/recalls_waiting.tt", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => "manage_recalls" }, + debug => 1, + } +); + +my $op = $query->param('op') || 'list'; + +if ( $op eq 'modify' ) { + my $expire = $query->param('expire') || ''; + my $revert = $query->param('revert') || ''; + my $recall_id = $query->param('recall_id'); + if ( $expire ) { + Koha::Recalls->find( $recall_id )->set_expired({ interface => 'INTRANET' }); + } elsif ( $revert ) { + Koha::Recalls->find( $recall_id )->revert_waiting; + } + $op = 'list'; +} + +if ( $op eq 'list' ) { + my @recalls = Koha::Recalls->search({ status => 'W' }); + my $borrower = Koha::Patrons->find( $loggedinuser ); + my @over; + my $maxdelay = C4::Context->preference('RecallsMaxPickUpDelay') || 7; + my $today = dt_from_string(); + foreach my $r ( @recalls ){ + my $lastwaitingday = dt_from_string( $r->waitingdate )->add( days => $maxdelay ); + if ( $today > $lastwaitingday ){ + push @over, $r; + } + } + $template->param( + recalls => \@recalls, + over => \@over, + ); +} + +# Checking if there is a Fast Cataloging Framework +$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find( 'FA' ); + +# writing the template +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/recalls/request.pl b/recalls/request.pl new file mode 100755 index 0000000000..59a4b4555b --- /dev/null +++ b/recalls/request.pl @@ -0,0 +1,65 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Output; +use C4::Search; +use Koha::Recalls; +use Koha::Biblios; + +my $input = CGI->new; +my ($template, $loggedinuser, $cookie)= get_template_and_user( + { + template_name => "recalls/request.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { recalls => "manage_recalls" }, + debug => 1, + } +); + +my $op = $input->param('op') || 'list'; +my @recall_ids = $input->multi_param('recall_ids'); +my $biblionumber = $input->param('biblionumber'); +my $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef }); +my $biblio = Koha::Biblios->find( $biblionumber ); + +if ( $op eq 'cancel_multiple_recalls' ) { + foreach my $id ( @recall_ids ) { + Koha::Recalls->find( $id )->set_cancelled; + } + $op = 'list' +} + +if ( $op eq 'list' ) { + $recalls = Koha::Recalls->search({ biblionumber => $biblionumber, old => undef }); + $biblio = Koha::Biblios->find( $biblionumber ); +} + +$template->param( + recalls => $recalls, + recallsview => 1, + biblio => $biblio, + checkboxes => 1, + C4::Search::enabled_staff_search_views, +); + +output_html_with_http_headers $input, $cookie, $template->output; diff --git a/reserve/request.pl b/reserve/request.pl index 26e40dd685..2a1cb2190e 100755 --- a/reserve/request.pl +++ b/reserve/request.pl @@ -314,6 +314,10 @@ foreach my $biblionumber (@biblionumbers) { $template->param( $canReserve->{status} => 1 ); $biblioloopiter{ $canReserve->{status} } = 1; } + elsif ( $canReserve->{status} eq 'recall' ) { + $template->param( $canReserve->{status} => 1 ); + $biblioloopiter{ $canReserve->{status} } = 1; + } else { $biblioloopiter{ $canReserve->{status} } = 1; } diff --git a/svc/checkouts b/svc/checkouts index fa470b3a2a..cd42e3b48c 100755 --- a/svc/checkouts +++ b/svc/checkouts @@ -187,6 +187,26 @@ while ( my $c = $sth->fetchrow_hashref() ) { $damaged = $av->count ? $av->next->lib : ''; } my @subtitles = split(/ \| /, $c->{'subtitle'} // '' ); + + my $item = Koha::Items->find( $c->{itemnumber} ); + my $recalled = 0; + my $recall = $item->check_recalls if $item->can_be_waiting_recall; + if ( defined $recall ) { + if ( $recall->item_level_recall ) { + if ( $recall->itemnumber == $c->{itemnumber} ) { + # item-level recall on this item + $recalled = 1; + } else { + $recalled = 0; + } + } else { + # biblio-level recall, but don't want to mark recalled if the recall has been allocated a different item + if ( !$recall->waiting ) { + $recalled = 1; + } + } + } + my $checkout = { DT_RowId => $c->{itemnumber} . '-' . $c->{borrowernumber}, title => $c->{title}, @@ -254,6 +274,7 @@ while ( my $c = $sth->fetchrow_hashref() ) { cardnumber => $c->{cardnumber}, }, issued_today => !$c->{not_issued_today}, + recalled => $recalled, }; if ( $c->{not_issued_today} ) { diff --git a/svc/recall b/svc/recall new file mode 100755 index 0000000000..85e1a477d4 --- /dev/null +++ b/svc/recall @@ -0,0 +1,79 @@ +#!/usr/bin/perl + +# Copyright 2020 Aleisha Amohia +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use CGI; +use JSON qw(encode_json); + +use C4::Context; +use C4::Auth qw(check_cookie_auth); +use C4::Output qw(output_with_http_headers); +use C4::Circulation qw(AddReturn); +use Koha::Recalls; + +my $input = new CGI; +my $json = encode_json({ success => 0 }); + +my ( $auth_status, $sessionID ) = check_cookie_auth( $input->cookie('CGISESSID'), { recall => 'manage_recalls' } ); + +if ( $auth_status ne "ok" ) { + print $input->header(-type => 'text/plain', -status => '403 Forbidden'); + exit 0; +} + +my $recall_id = $input->param('recall_id'); +my $recall = Koha::Recalls->find( $recall_id ); +unless ( $recall ) { + my $json = encode_json({ success => 0 }); + output_with_http_headers( $input, undef, $json, "json" ); + exit; +} + +my $op = $input->param('action'); + +if ( $op eq 'cancel' ) { + # cancel recall + $recall->set_cancelled; + if ( $recall->cancelled ){ + $json = encode_json({ success => 1 }); + } + +} elsif ( $op eq 'expire' ) { + # expire recall + $recall->set_expired({ interface => 'INTRANET' }); + if ( $recall->expired ){ + $json = encode_json({ success => 1 }); + } + +} elsif ( $op eq 'revert' ) { + # revert recall waiting status + $recall->revert_waiting; + if ( $recall->requested ){ + $json = encode_json({ success => 1 }); + } + +} elsif ( $op eq 'overdue' ) { + # mark recall as overdue + $recall->set_overdue({ interface => 'INTRANET' }); + if ( $recall->overdue ){ + $json = encode_json({ success => 1 }); + } +} + +output_with_http_headers( $input, undef, $json, "json" ); diff --git a/tools/letter.pl b/tools/letter.pl index 6044243c09..e95746e1e2 100755 --- a/tools/letter.pl +++ b/tools/letter.pl @@ -248,7 +248,7 @@ sub add_form { {value => 'items.fine', text => 'items.fine'}, add_fields('borrowers'); if ($module eq 'circulation') { - push @{$field_selection}, add_fields('opac_news'); + push @{$field_selection}, add_fields('opac_news', 'recalls'); } diff --git a/tools/viewlog.pl b/tools/viewlog.pl index 2ccd472f0e..3377c561a9 100755 --- a/tools/viewlog.pl +++ b/tools/viewlog.pl @@ -188,6 +188,16 @@ if ($do_it) { } } } + + # get recall information + if ( $log->module eq "RECALLS" ) { + if ( $log->object ) { + my $recall = Koha::Recalls->find( $log->object ); + if ( $recall && $output eq 'screen' ) { + $result->{recall} = $recall; + } + } + } push @data, $result; } if ( $output eq "screen" ) { -- 2.11.0