From 3f4f87839c82b8a1fbee444da404fbd10063bb53 Mon Sep 17 00:00:00 2001 From: Andrew Isherwood Date: Fri, 22 Nov 2019 11:08:53 +0000 Subject: [PATCH] Bug 24083: Add support for unseen_renewals This patch adds support for unseen renewals. Here we retrofit knowledge of unseen renewals and add the display of unseen renewal counts and warnings, in addition to adding the ability to specify a renewal as being "unseen". The functionality added here is goverened by the UnseenRenewals syspref. Signed-off-by: Sally Healey --- C4/Circulation.pm | 172 ++++++++++++++++++++- C4/ILSDI/Services.pm | 2 +- C4/SIP/ILS/Transaction/Renew.pm | 1 + Koha/CirculationRules.pm | 3 + Koha/REST/V1/Checkouts.pm | 4 +- api/v1/swagger/definitions/checkout.json | 4 + api/v1/swagger/parameters.json | 3 + api/v1/swagger/parameters/checkout.json | 7 + api/v1/swagger/paths/checkouts.json | 7 +- circ/renew.pl | 10 +- .../intranet-tmpl/prog/css/src/staff-global.scss | 12 ++ .../prog/en/includes/checkouts-table.inc | 4 + .../intranet-tmpl/prog/en/includes/strings.inc | 3 + .../prog/en/modules/circ/circulation.tt | 1 + .../intranet-tmpl/prog/en/modules/circ/renew.tt | 29 +++- .../prog/en/modules/members/moremember.tt | 1 + koha-tmpl/intranet-tmpl/prog/js/checkouts.js | 26 +++- .../opac-tmpl/bootstrap/en/modules/opac-user.tt | 46 +++++- misc/cronjobs/automatic_renewals.pl | 2 +- offline_circ/download.pl | 1 + opac/opac-renew.pl | 2 +- opac/opac-user.pl | 10 +- svc/checkouts | 12 +- svc/renew | 3 +- 24 files changed, 332 insertions(+), 33 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 405528675e..a2ddb44c33 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -2724,6 +2724,7 @@ sub CanBookBeRenewed { 'no_auto_renewal_after_hard_limit', 'lengthunit', 'norenewalbefore', + 'unseen_renewals_allowed' ] } ); @@ -2887,12 +2888,124 @@ sub CanBookBeRenewed { return ( 0, "on_reserve" ) if $resfound; # '' when no hold was found return ( 0, "auto_renew" ) if $auto_renew && !$override_limit; # 0 if auto-renewal should not succeed + return ( 1, undef ) if $override_limit; + + my $branchcode = _GetCircControlBranch( $item->unblessed, $patron->unblessed ); + my $issuing_rule = Koha::CirculationRules->get_effective_rules( + { + categorycode => $patron->categorycode, + itemtype => $item->effective_itemtype, + branchcode => $branchcode, + rules => [ + 'renewalsallowed', + 'no_auto_renewal_after', + 'no_auto_renewal_after_hard_limit', + 'lengthunit', + 'norenewalbefore', + 'unseen_renewals_allowed' + ] + } + ); + + return ( 0, "too_many" ) + if not $issuing_rule->{renewalsallowed} or $issuing_rule->{renewalsallowed} <= $issue->renewals; + + return ( 0, "too_unseen" ) + if C4::Context->preference('UnseenRenewals') && + $issuing_rule->{unseen_renewals_allowed} && + $issuing_rule->{unseen_renewals_allowed} <= $issue->unseen_renewals; + + my $overduesblockrenewing = C4::Context->preference('OverduesBlockRenewing'); + my $restrictionblockrenewing = C4::Context->preference('RestrictionBlockRenewing'); + $patron = Koha::Patrons->find($borrowernumber); # FIXME Is this really useful? + my $restricted = $patron->is_debarred; + my $hasoverdues = $patron->has_overdues; + + if ( $restricted and $restrictionblockrenewing ) { + return ( 0, 'restriction'); + } elsif ( ($hasoverdues and $overduesblockrenewing eq 'block') || ($issue->is_overdue and $overduesblockrenewing eq 'blockitem') ) { + return ( 0, 'overdue'); + } + + if ( $issue->auto_renew ) { + + if ( $patron->category->effective_BlockExpiredPatronOpacActions and $patron->is_expired ) { + return ( 0, 'auto_account_expired' ); + } + + if ( defined $issuing_rule->{no_auto_renewal_after} + and $issuing_rule->{no_auto_renewal_after} ne "" ) { + # Get issue_date and add no_auto_renewal_after + # If this is greater than today, it's too late for renewal. + my $maximum_renewal_date = dt_from_string($issue->issuedate, 'sql'); + $maximum_renewal_date->add( + $issuing_rule->{lengthunit} => $issuing_rule->{no_auto_renewal_after} + ); + my $now = dt_from_string; + if ( $now >= $maximum_renewal_date ) { + return ( 0, "auto_too_late" ); + } + } + if ( defined $issuing_rule->{no_auto_renewal_after_hard_limit} + and $issuing_rule->{no_auto_renewal_after_hard_limit} ne "" ) { + # If no_auto_renewal_after_hard_limit is >= today, it's also too late for renewal + if ( dt_from_string >= dt_from_string( $issuing_rule->{no_auto_renewal_after_hard_limit} ) ) { + return ( 0, "auto_too_late" ); + } + } + + if ( C4::Context->preference('OPACFineNoRenewalsBlockAutoRenew') ) { + my $fine_no_renewals = C4::Context->preference("OPACFineNoRenewals"); + my $amountoutstanding = + C4::Context->preference("OPACFineNoRenewalsIncludeCredit") + ? $patron->account->balance + : $patron->account->outstanding_debits->total_outstanding; + if ( $amountoutstanding and $amountoutstanding > $fine_no_renewals ) { + return ( 0, "auto_too_much_oweing" ); + } + } + } + + if ( defined $issuing_rule->{norenewalbefore} + and $issuing_rule->{norenewalbefore} ne "" ) + { + + # Calculate soonest renewal by subtracting 'No renewal before' from due date + my $soonestrenewal = dt_from_string( $issue->date_due, 'sql' )->subtract( + $issuing_rule->{lengthunit} => $issuing_rule->{norenewalbefore} ); + + # Depending on syspref reset the exact time, only check the date + if ( C4::Context->preference('NoRenewalBeforePrecision') eq 'date' + and $issuing_rule->{lengthunit} eq 'days' ) + { + $soonestrenewal->truncate( to => 'day' ); + } + + if ( $soonestrenewal > DateTime->now( time_zone => C4::Context->tz() ) ) + { + return ( 0, "auto_too_soon" ) if $issue->auto_renew; + return ( 0, "too_soon" ); + } + elsif ( $issue->auto_renew ) { + return ( 0, "auto_renew" ); + } + } + + # Fallback for automatic renewals: + # If norenewalbefore is undef, don't renew before due date. + if ( $issue->auto_renew ) { + my $now = dt_from_string; + return ( 0, "auto_renew" ) + if $now >= dt_from_string( $issue->date_due, 'sql' ); + return ( 0, "auto_too_soon" ); + } + return ( 1, undef ); } =head2 AddRenewal - &AddRenewal($borrowernumber, $itemnumber, $branch, [$datedue], [$lastreneweddate]); + &AddRenewal($borrowernumber, $itemnumber, $branch, [$datedue], [$lastreneweddate], [$seen]); Renews a loan. @@ -2917,6 +3030,10 @@ syspref) If C<$datedue> is the empty string, C<&AddRenewal> will calculate the due date automatically from the book's item type. +C<$seen> is a boolean flag indicating if the item was seen or not during the renewal. This +informs the incrementing of the unseen_renewals column. If this flag is not supplied, we +fallback to a true value + =cut sub AddRenewal { @@ -2926,6 +3043,10 @@ sub AddRenewal { my $datedue = shift; my $lastreneweddate = shift || DateTime->now(time_zone => C4::Context->tz); my $skipfinecalc = shift; + my $seen = shift; + + # Fallback on a 'seen' renewal + $seen = defined $seen && $seen == 0 ? 0 : 1; my $item_object = Koha::Items->find($itemnumber) or return; my $biblio = $item_object->biblio; @@ -2978,15 +3099,35 @@ sub AddRenewal { } ); + # Increment the unseen renewals, if appropriate + # We only do so if the syspref is enabled and + # a maximum value has been set in the circ rules + my $unseen_renewals = $issue->unseen_renewals; + if (C4::Context->preference('UnseenRenewals')) { + my $rule = Koha::CirculationRules->get_effective_rule( + { categorycode => $patron->categorycode, + itemtype => $item_object->effective_itemtype, + branchcode => $circ_library->branchcode, + rule_name => 'unseen_renewals_allowed' + } + ); + if (!$seen && $rule && $rule->rule_value) { + $unseen_renewals++; + } else { + # If the renewal is seen, unseen should revert to 0 + $unseen_renewals = 0; + } + } + # Update the issues record to have the new due date, and a new count # of how many times it has been renewed. my $renews = ( $issue->renewals || 0 ) + 1; - my $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ? + my $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, unseen_renewals = ?, lastreneweddate = ? WHERE borrowernumber=? AND itemnumber=?" ); - $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $lastreneweddate, $borrowernumber, $itemnumber ); + $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $unseen_renewals, $lastreneweddate, $borrowernumber, $itemnumber ); # Update the renewal count on the item, and tell zebra to reindex $renews = ( $item_object->renewals || 0 ) + 1; @@ -3068,8 +3209,11 @@ sub GetRenewCount { my ( $bornum, $itemno ) = @_; my $dbh = C4::Context->dbh; my $renewcount = 0; + my $unseencount = 0; my $renewsallowed = 0; + my $unseenallowed = 0; my $renewsleft = 0; + my $unseenleft = 0; my $patron = Koha::Patrons->find( $bornum ); my $item = Koha::Items->find($itemno); @@ -3088,22 +3232,34 @@ sub GetRenewCount { $sth->execute( $bornum, $itemno ); my $data = $sth->fetchrow_hashref; $renewcount = $data->{'renewals'} if $data->{'renewals'}; + $unseencount = $data->{'unseen_renewals'} if $data->{'unseen_renewals'}; # $item and $borrower should be calculated my $branchcode = _GetCircControlBranch($item->unblessed, $patron->unblessed); - my $rule = Koha::CirculationRules->get_effective_rule( + my $rules = Koha::CirculationRules->get_effective_rules( { categorycode => $patron->categorycode, itemtype => $item->effective_itemtype, branchcode => $branchcode, - rule_name => 'renewalsallowed', + rules => [ 'renewalsallowed', 'unseen_renewals_allowed' ] } ); - - $renewsallowed = $rule ? $rule->rule_value : 0; + $renewsallowed = $rules ? $rules->{renewalsallowed} : 0; + $unseenallowed = $rules->{unseen_renewals_allowed} ? + $rules->{unseen_renewals_allowed} : + 0; $renewsleft = $renewsallowed - $renewcount; + $unseenleft = $unseenallowed - $unseencount; if($renewsleft < 0){ $renewsleft = 0; } - return ( $renewcount, $renewsallowed, $renewsleft ); + if($unseenleft < 0){ $unseenleft = 0; } + return ( + $renewcount, + $renewsallowed, + $renewsleft, + $unseencount, + $unseenallowed, + $unseenleft + ); } =head2 GetSoonestRenewDate diff --git a/C4/ILSDI/Services.pm b/C4/ILSDI/Services.pm index 1ed424e18a..13606fc836 100644 --- a/C4/ILSDI/Services.pm +++ b/C4/ILSDI/Services.pm @@ -644,7 +644,7 @@ sub RenewLoan { # Add renewal if possible my @renewal = CanBookBeRenewed( $borrowernumber, $itemnumber ); - if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber ); } + if ( $renewal[0] ) { AddRenewal( $borrowernumber, $itemnumber, undef, undef, undef, 0 ); } my $issue = $item->checkout; return unless $issue; # FIXME should be handled diff --git a/C4/SIP/ILS/Transaction/Renew.pm b/C4/SIP/ILS/Transaction/Renew.pm index af96f93901..f046ba79b2 100644 --- a/C4/SIP/ILS/Transaction/Renew.pm +++ b/C4/SIP/ILS/Transaction/Renew.pm @@ -51,6 +51,7 @@ sub do_renew_for { } else { $renewerror=~s/on_reserve/Item unavailable due to outstanding holds/; $renewerror=~s/too_many/Item has reached maximum renewals/; + $renewerror=~s/too_unseen/Item has reached maximum consecutive renewals without being seen/; $renewerror=~s/item_denied_renewal/Item renewal is not allowed/; $self->screen_msg($renewerror); $self->renewal_ok(0); diff --git a/Koha/CirculationRules.pm b/Koha/CirculationRules.pm index 5042e6f55d..e2f5e6707c 100644 --- a/Koha/CirculationRules.pm +++ b/Koha/CirculationRules.pm @@ -145,6 +145,9 @@ our $RULE_KINDS = { renewalsallowed => { scope => [ 'branchcode', 'categorycode', 'itemtype' ], }, + unseen_renewals_allowed => { + scope => [ 'branchcode', 'categorycode', 'itemtype' ], + }, rentaldiscount => { scope => [ 'branchcode', 'categorycode', 'itemtype' ], }, diff --git a/Koha/REST/V1/Checkouts.pm b/Koha/REST/V1/Checkouts.pm index 3a5f448cc6..a23c5111d4 100644 --- a/Koha/REST/V1/Checkouts.pm +++ b/Koha/REST/V1/Checkouts.pm @@ -151,6 +151,7 @@ sub renew { my $c = shift->openapi->valid_input or return; my $checkout_id = $c->validation->param('checkout_id'); + my $seen = $c->validation->param('seen') || 1; my $checkout = Koha::Checkouts->find( $checkout_id ); unless ($checkout) { @@ -173,7 +174,7 @@ sub renew { ); } - AddRenewal($borrowernumber, $itemnumber, $checkout->branchcode); + AddRenewal($borrowernumber, $itemnumber, $checkout->branchcode, undef, undef, $seen); $checkout = Koha::Checkouts->find($checkout_id); $c->res->headers->location( $c->req->url->to_string ); @@ -222,6 +223,7 @@ sub allows_renewal { allows_renewal => $renewable, max_renewals => $rule->rule_value, current_renewals => $checkout->renewals, + unseen_renewals => $checkout->unseen_renewals, error => $error } ); diff --git a/api/v1/swagger/definitions/checkout.json b/api/v1/swagger/definitions/checkout.json index 42b9598843..fb98afd28e 100644 --- a/api/v1/swagger/definitions/checkout.json +++ b/api/v1/swagger/definitions/checkout.json @@ -35,6 +35,10 @@ "type": ["integer", "null"], "description": "Number of renewals" }, + "unseen_renewals": { + "type": ["integer", "null"], + "description": "Number of consecutive unseen renewals" + }, "auto_renew": { "type": "boolean", "description": "Auto renewal" diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json index 9c3d7ebc96..c8e5131577 100644 --- a/api/v1/swagger/parameters.json +++ b/api/v1/swagger/parameters.json @@ -32,6 +32,9 @@ "checkout_id_pp": { "$ref": "parameters/checkout.json#/checkout_id_pp" }, + "seen_pp": { + "$ref": "parameters/checkout.json#/seen_pp" + }, "match": { "name": "_match", "in": "query", diff --git a/api/v1/swagger/parameters/checkout.json b/api/v1/swagger/parameters/checkout.json index 2bdb20ab84..3b802f0c30 100644 --- a/api/v1/swagger/parameters/checkout.json +++ b/api/v1/swagger/parameters/checkout.json @@ -5,5 +5,12 @@ "description": "Internal checkout identifier", "required": true, "type": "integer" + }, + "seen_pp": { + "name": "seen", + "in": "query", + "description": "Item was seen flag", + "required": false, + "type": "integer" } } diff --git a/api/v1/swagger/paths/checkouts.json b/api/v1/swagger/paths/checkouts.json index ca6d970dbb..a97b9f0572 100644 --- a/api/v1/swagger/paths/checkouts.json +++ b/api/v1/swagger/paths/checkouts.json @@ -81,9 +81,10 @@ "x-mojo-to": "Checkouts#renew", "operationId": "renewCheckout", "tags": ["patrons", "checkouts"], - "parameters": [{ - "$ref": "../parameters.json#/checkout_id_pp" - }], + "parameters": [ + { "$ref": "../parameters.json#/checkout_id_pp" }, + { "$ref": "../parameters.json#/seen_pp" } + ], "produces": ["application/json"], "responses": { "201": { diff --git a/circ/renew.pl b/circ/renew.pl index c3f1d2a4b3..95e8c92bec 100755 --- a/circ/renew.pl +++ b/circ/renew.pl @@ -44,6 +44,7 @@ my ( $template, $librarian, $cookie, $flags ) = get_template_and_user( my $schema = Koha::Database->new()->schema(); my $barcode = $cgi->param('barcode'); +my $unseen = $cgi->param('unseen') || 0; $barcode =~ s/^\s*|\s*$//g; # remove leading/trailing whitespae $barcode = barcodedecode($barcode) if( $barcode && C4::Context->preference('itemBarcodeInputFilter')); my $override_limit = $cgi->param('override_limit'); @@ -99,7 +100,14 @@ if ($barcode) { if ( $cgi->param('renewonholdduedate') ) { $date_due = dt_from_string( scalar $cgi->param('renewonholdduedate')); } - $date_due = AddRenewal( undef, $item->itemnumber(), $branchcode, $date_due ); + $date_due = AddRenewal( + undef, + $item->itemnumber(), + $branchcode, + $date_due, + undef, + !$unseen + ); $template->param( date_due => $date_due ); } } diff --git a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss index 6a4f1e6c3f..37ff8ae04d 100644 --- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss +++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss @@ -1004,6 +1004,10 @@ div { width: auto; } + .renew_formfield { + margin-bottom: 1em; + } + .circmessage { margin-bottom: .3em; padding: 0 .4em .4em; @@ -2408,6 +2412,14 @@ li { position: relative; } +#renew_as_unseen_label { + margin-left: 1em; +} + +#renew_as_unseen_checkbox { + margin-right: 1em; +} + #clearscreen { position: absolute; right: 0; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc index 7b967bec89..3183ff735c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc @@ -48,6 +48,10 @@ [% END %] [% END %] [% IF ( CAN_user_circulate_circulate_remaining_permissions ) %] + [% IF Koha.Preference( 'UnseenRenewals' ) %] + + + [% 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 5ef174db7c..2812067b23 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc @@ -11,6 +11,7 @@ var RETURN_CLAIMED_NOTES = _("Notes about return claim"); var NOT_CHECKED_OUT = _("not checked out"); var TOO_MANY_RENEWALS = _("too many renewals"); + var TOO_MANY_UNSEEN = _("too many consecutive renewals without being seen by the library"); var ON_RESERVE = _("on hold"); var REASON_UNKNOWN = _("reason unknown"); var TODAYS_CHECKOUTS = _("Today's checkouts"); @@ -19,6 +20,7 @@ var ON_HOLD = _("On hold"); var PLACE_HOLD = _("Place hold"); var NOT_RENEWABLE = _("Not renewable"); + var NOT_RENEWABLE_UNSEEN = _("Must be renewed at the library"); var NOT_RENEWABLE_TOO_SOON = _("No renewal before %s"); var NOT_RENEWABLE_AUTO_TOO_SOON = _("Scheduled for automatic renewal"); var NOT_RENEWABLE_AUTO_TOO_LATE = _("Can no longer be auto-renewed - number of checkout days exceeded"); @@ -27,6 +29,7 @@ var NOT_RENEWABLE_AUTO_RENEW = _("Scheduled for automatic renewal"); var NOT_RENEWABLE_DENIED = _("Renewal denied by syspref"); var RENEWALS_REMAINING = _("%s of %s renewals remaining"); + var UNSEEN_REMAINING = _("%s of %s unseen renewals remaining"); var HOLD_IS_SUSPENDED = _("Hold is suspended"); var UNTIL = _("until %s"); var NEXT_AVAILABLE_ITYPE = _("Next available %s item"); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index 1eeb19e3af..ec158fadf6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -1030,6 +1030,7 @@ var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]"; var ClaimReturnedLostValue = "[% Koha.Preference('ClaimReturnedLostValue') | html %]"; var ClaimReturnedChargeFee = "[% Koha.Preference('ClaimReturnedChargeFee') | html %]"; + var UnseenRenewals = "[% Koha.Preference('UnseenRenewals') | html %]"; var ClaimReturnedWarningThreshold = "[% Koha.Preference('ClaimReturnedWarningThreshold') | html %]"; var MSG_DT_LOADING_RECORDS = _("Loading... you may continue scanning."); var interface = "[% interface | html %]"; 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..3fb11d4338 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/renew.tt @@ -52,6 +52,18 @@ [% END %] + [% ELSIF error == "too_unseen" %] + +

[% INCLUDE 'biblio-title.inc' biblio=item.biblio %] ( [% item.barcode | html %] ) has been renewed the maximum number of consecutive times without being seen by the library )

+ + [% IF Koha.Preference('AllowRenewalLimitOverride') %] +
+ + + +
+ [% END %] + [% ELSIF error == "too_soon" %]

[% INCLUDE 'biblio-title.inc' biblio=item.biblio %] ( [% item.barcode | html %] ) cannot be renewed before [% soonestrenewdate | $KohaDates %].

@@ -168,10 +180,19 @@
Renew - - - - + [% IF Koha.Preference('UnseenRenewals') %] +
+ + +
+
+ + +
+ [% ELSE %] + + + [% 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 cfc2a0ab23..2780a00e2b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -866,6 +866,7 @@ var ClaimReturnedLostValue = "[% Koha.Preference('ClaimReturnedLostValue') | html %]"; var ClaimReturnedChargeFee = "[% Koha.Preference('ClaimReturnedChargeFee') | html %]"; var ClaimReturnedWarningThreshold = "[% Koha.Preference('ClaimReturnedWarningThreshold') | html %]"; + var UnseenRenewals = "[% Koha.Preference('UnseenRenewals') | html %]"; var interface = "[% interface | html %]"; var theme = "[% theme | html %]"; var borrowernumber = "[% patron.borrowernumber | html %]"; diff --git a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js index 169bdc4e36..b064faf00a 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js +++ b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js @@ -143,9 +143,15 @@ $(document).ready(function() { itemnumber: itemnumber, borrowernumber: borrowernumber, branchcode: branchcode, - override_limit: override_limit, + override_limit: override_limit }; + if (UnseenRenewals) { + var ren = $("#renew_as_unseen_checkbox"); + var renew_unseen = ren.length > 0 && ren.is(':checked') ? 1 : 0; + params.seen = renew_unseen === 1 ? 0 : 1; + } + // Determine which due date we need to use var dueDate = isOnReserve ? $("#newonholdduedate input").val() : @@ -168,6 +174,8 @@ $(document).ready(function() { content += NOT_CHECKED_OUT; } else if ( data.error == "too_many" ) { content += TOO_MANY_RENEWALS; + } else if ( data.error == "too_unseen" ) { + content += TOO_MANY_UNSEEN; } else if ( data.error == "on_reserve" ) { content += ON_RESERVE; } else if ( data.error == "restriction" ) { @@ -432,6 +440,13 @@ $(document).ready(function() { span_style = "display: none"; span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "too_unseen" ) { + msg += "" + + NOT_RENEWABLE_UNSEEN + + ""; + + span_style = "display: none"; + span_class = "renewals-allowed"; } else if ( oObj.can_renew_error == "restriction" ) { msg += "" + NOT_RENEWABLE_RESTRICTION @@ -526,9 +541,12 @@ $(document).ready(function() { } content += msg; if ( can_renew || can_force_renew ) { - content += "(" - + RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed ) - + ")"; + content += "("; + content += RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed ); + if (UnseenRenewals && oObj.unseen_allowed) { + content += " / " + UNSEEN_REMAINING.format( oObj.unseen_remaining, oObj.unseen_allowed ); + } + content += ")"; } content += ""; diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt index 879867af0a..8d8d1dcf44 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt @@ -81,6 +81,8 @@
  • Your account has expired. Please contact the library for more information.
  • [% ELSIF error == 'too_many' %]
  • You have renewed this item the maximum number of times allowed.
  • + [% ELSIF error == 'too_unseen' %] +
  • You have renewed this item the maximum number of consecutive times without it being seen by the library.
  • [% ELSIF error == 'too_soon' %]
  • It is too soon after the checkout date for this item to be renewed.
  • [% ELSIF error == 'on_reserve' %] @@ -309,27 +311,54 @@ [% IF ISSUE.itemtype_object.rentalcharge_hourly > 0 %] [% ISSUE.itemtype_object.rentalcharge_hourly | $Price %] per hour [% END %] - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.on_reserve ) %] Not renewable (on hold) [% ELSIF ( ISSUE.too_many ) %] Not renewable + [% ELSIF ( ISSUE.too_unseen ) %] + Item must be seen by the library [% ELSIF ( ISSUE.norenew_overdue ) %] Not allowed (overdue) [% ELSIF ( ISSUE.auto_too_late ) %] No longer renewable [% ELSIF ISSUE.auto_too_much_oweing %] Automatic renewal failed, you have unpaid fines. - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ISSUE.auto_account_expired %] Automatic renewal failed, your account is expired. - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.auto_renew || ISSUE.auto_too_soon ) %] Automatic renewal - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.too_soon ) %] No renewal before [% ISSUE.soonestrenewdate | html %] - ([% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining) + ( + [% ISSUE.renewsleft | html %] of [% ISSUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% ISSUE.unseenleft | html %] of [% ISSUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( ISSUE.item_denied_renewal ) %] Renewal not allowed [% END %] @@ -582,7 +611,12 @@ [% IF ( canrenew ) %] Renew [% END %] - ([% OVERDUE.renewsleft | html %] of [% OVERDUE.renewsallowed | html %] renewals remaining) + ( + [% OVERDUE.renewsleft | html %] of [% OVERDUE.renewsallowed | html %] renewals remaining + [% IF Koha.Preference('UnseenRenewals') && ISSUE.unseenallowed %] + / [% OVERDUE.unseenleft | html %] of [% OVERDUE.unseenallowed | html %] renewals left before the item must be seen by the library + [% END %] + ) [% ELSIF ( OVERDUE.norenew_overdue ) %] Not allowed(overdue) [% ELSIF ( OVERDUE.onreserve ) %] diff --git a/misc/cronjobs/automatic_renewals.pl b/misc/cronjobs/automatic_renewals.pl index d7bc5a140b..f3e993db15 100755 --- a/misc/cronjobs/automatic_renewals.pl +++ b/misc/cronjobs/automatic_renewals.pl @@ -95,7 +95,7 @@ while ( my $auto_renew = $auto_renews->next ) { $auto_renew->issue_id, $auto_renew->borrowernumber, $auto_renew->itemnumber; } if ($confirm){ - my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode ); + my $date_due = AddRenewal( $auto_renew->borrowernumber, $auto_renew->itemnumber, $auto_renew->branchcode, undef, undef, 0 ); $auto_renew->auto_renew_error(undef)->store; } push @{ $report{ $auto_renew->borrowernumber } }, $auto_renew; diff --git a/offline_circ/download.pl b/offline_circ/download.pl index 386faebf41..1c3f0f2d79 100755 --- a/offline_circ/download.pl +++ b/offline_circ/download.pl @@ -67,6 +67,7 @@ my $issues_query = q{SELECT issues.date_due AS date_due, issues.issuedate AS issuedate, issues.renewals AS renewals, + issues.unseen_renewals AS unseen_renewals, borrowers.cardnumber AS cardnumber, CONCAT(borrowers.surname, ', ', borrowers.firstname) AS borrower_name FROM issues diff --git a/opac/opac-renew.pl b/opac/opac-renew.pl index 85570d54ad..37ae1e5970 100755 --- a/opac/opac-renew.pl +++ b/opac/opac-renew.pl @@ -82,7 +82,7 @@ else { else { $branchcode = 'OPACRenew'; } - AddRenewal( $borrowernumber, $itemnumber, $branchcode, undef, undef ); + AddRenewal( $borrowernumber, $itemnumber, $branchcode, undef, undef, 0 ); push( @renewed, $itemnumber ); } else { diff --git a/opac/opac-user.pl b/opac/opac-user.pl index 99a32ebb63..d394cf133f 100755 --- a/opac/opac-user.pl +++ b/opac/opac-user.pl @@ -203,7 +203,14 @@ if ( $pending_checkouts->count ) { # Useless test # check if item is renewable my ($status,$renewerror) = CanBookBeRenewed( $borrowernumber, $issue->{'itemnumber'} ); - ($issue->{'renewcount'},$issue->{'renewsallowed'},$issue->{'renewsleft'}) = GetRenewCount($borrowernumber, $issue->{'itemnumber'}); + ( + $issue->{'renewcount'}, + $issue->{'renewsallowed'}, + $issue->{'renewsleft'}, + $issue->{'unseencount'}, + $issue->{'unseenallowed'}, + $issue->{'unseenleft'} + ) = GetRenewCount($borrowernumber, $issue->{'itemnumber'}); ( $issue->{'renewalfee'}, $issue->{'renewalitemtype'} ) = GetIssuingCharges( $issue->{'itemnumber'}, $borrowernumber ); $issue->{itemtype_object} = Koha::ItemTypes->find( Koha::Items->find( $issue->{itemnumber} )->effective_itemtype ); if($status && C4::Context->preference("OpacRenewalAllowed")){ @@ -214,6 +221,7 @@ if ( $pending_checkouts->count ) { # Useless test if ($renewerror) { $issue->{'too_many'} = 1 if $renewerror eq 'too_many'; + $issue->{'too_unseen'} = 1 if $renewerror eq 'too_unseen'; $issue->{'on_reserve'} = 1 if $renewerror eq 'on_reserve'; $issue->{'norenew_overdue'} = 1 if $renewerror eq 'overdue'; $issue->{'auto_renew'} = 1 if $renewerror eq 'auto_renew'; diff --git a/svc/checkouts b/svc/checkouts index a749494028..b8293c1a64 100755 --- a/svc/checkouts +++ b/svc/checkouts @@ -158,7 +158,14 @@ while ( my $c = $sth->fetchrow_hashref() ) { ) : undef; - my ( $renewals_count, $renewals_allowed, $renewals_remaining ) = + my ( + $renewals_count, + $renewals_allowed, + $renewals_remaining, + $unseen_count, + $unseen_allowed, + $unseen_remaining + ) = GetRenewCount( $c->{borrowernumber}, $c->{itemnumber} ); my $type_for_stat = Koha::ItemTypes->find( $item_level_itypes ? $c->{itype} : $c->{itemtype} ); @@ -225,6 +232,9 @@ while ( my $c = $sth->fetchrow_hashref() ) { renewals_count => $renewals_count, renewals_allowed => $renewals_allowed, renewals_remaining => $renewals_remaining, + unseen_count => $unseen_count, + unseen_allowed => $unseen_allowed, + unseen_remaining => $unseen_remaining, return_claim_id => $c->{return_claim_id}, return_claim_notes => $c->{return_claim_notes}, diff --git a/svc/renew b/svc/renew index 51b915b004..98d7498a7c 100755 --- a/svc/renew +++ b/svc/renew @@ -46,6 +46,7 @@ my $borrowernumber = $input->param('borrowernumber'); my $override_limit = $input->param('override_limit'); my $branchcode = $input->param('branchcode') || C4::Context->userenv->{'branch'}; +my $seen = $input->param('seen'); my $date_due; if ( $input->param('date_due') ) { $date_due = dt_from_string( scalar $input->param('date_due') ); @@ -66,7 +67,7 @@ if ( $data->{error} && $data->{error} eq 'on_reserve' && C4::Context->preference } if ( $data->{renew_okay} ) { - $date_due = AddRenewal( $borrowernumber, $itemnumber, $branchcode, $date_due ); + $date_due = AddRenewal( $borrowernumber, $itemnumber, $branchcode, $date_due, undef, $seen ); $data->{date_due} = output_pref( { dt => $date_due, as_due_date => 1 } ); } -- 2.11.0