Koha/Koha/Checkout.pm
Nick Clemens cc0a8bf423
Bug 34924: Handle final renewal errors
This patch updates Koha::Checkout->attempt_auto_renew to renew when the 'final' errors are passed, and to pass the error value back to the cronjob for processing

The sample notice for AUTO_RENEW and AUTO_RENEW_DGST are both updated to handle the new error

On the next cron the error will be updated to too_many or too_unseen, but a notice will not be sent.

To test:
 0 - Run reset_all to install the updated sample notices or copy the text
 1 - Set system preference UnseenRenewals to 'Allow'
 2 - Setup a circ rule to allow 2 renewals, 2 unseen renewals
 3 - Checkout an item to a patron who has an email defined and auto renewals selected in messaging preferences
 4 - Update the issue to be due/overdue:
    UPDATE issues SET date_due=DATE_SUB(NOW(), INTERVAL 1 DAYS);
 5 - perl misc/cronjobs/automatic_renewals.pl -v -c
 6 - Confirm patron notified and issue renewed
 7 - Set issue due/overdue again
 8 - perl misc/cronjobs/automatic_renewals.pl -v -c
 9 - Confirm patron notified of final unseen renewal
10 - perl misc/cronjobs/automatic_renewals.pl -v -c
11 - Confirm issue not renewed, patron not notified
12 - Update circ rules t all 4 renewals total
13 - Force renewal on staff side to clear unseen
14 - perl misc/cronjobs/automatic_renewals.pl -v -c
15 - Confirm patron notified of final renewal (allowed 4, 2 unseen from cron, 1 manual, this unseen from cron)
16 - perl misc/cronjobs/automatic_renewals.pl -v -c
17 - Confirm patron not notified, issue not renewed

Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Emily Lamancusa <emily.lamancusa@montgomerycountymd.gov>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
2023-10-31 11:02:47 -03:00

309 lines
8.1 KiB
Perl

package Koha::Checkout;
# Copyright ByWater Solutions 2015
# Copyright 2016 Koha Development Team
#
# 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 <http://www.gnu.org/licenses>.
use Modern::Perl;
use DateTime;
use Try::Tiny qw( catch try );
use C4::Circulation qw( AddRenewal CanBookBeRenewed LostItem MarkIssueReturned );
use Koha::Checkouts::Renewals;
use Koha::Checkouts::ReturnClaims;
use Koha::Database;
use Koha::DateUtils qw( dt_from_string );
use Koha::Items;
use Koha::Libraries;
use base qw(Koha::Object);
=head1 NAME
Koha::Checkout - Koha Checkout object class
=head1 API
=head2 Class methods
=cut
=head3 is_overdue
my $is_overdue = $checkout->is_overdue( [ $reference_dt ] );
Return 1 if the checkout is overdue.
A reference date can be passed, in this case it will be used, otherwise today
will be the reference date.
=cut
sub is_overdue {
my ( $self, $dt ) = @_;
$dt ||= dt_from_string();
my $is_overdue =
DateTime->compare( dt_from_string( $self->date_due, 'sql' ), $dt ) == -1
? 1
: 0;
return $is_overdue;
}
=head3 item
my $item = $checkout->item;
Return the checked out item
=cut
sub item {
my ( $self ) = @_;
my $item_rs = $self->_result->item;
return Koha::Item->_new_from_dbic( $item_rs );
}
=head3 account_lines
my $account_lines = $checkout->account_lines;
Return the checked out account_lines
=cut
sub account_lines {
my ( $self ) = @_;
my $account_lines_rs = $self->_result->account_lines;
return Koha::Account::Lines->_new_from_dbic( $account_lines_rs );
}
=head3 library
my $library = $checkout->library;
Return the library in which the transaction took place
=cut
sub library {
my ( $self ) = @_;
my $library_rs = $self->_result->library;
return Koha::Library->_new_from_dbic( $library_rs );
}
=head3 patron
my $patron = $checkout->patron
Return the patron for who the checkout has been done
=cut
sub patron {
my ( $self ) = @_;
my $patron_rs = $self->_result->patron;
return Koha::Patron->_new_from_dbic( $patron_rs );
}
=head3 issuer
my $issuer = $checkout->issuer
Return the patron by whom the checkout was done
=cut
sub issuer {
my ( $self ) = @_;
my $issuer_rs = $self->_result->issuer;
return unless $issuer_rs;
return Koha::Patron->_new_from_dbic( $issuer_rs );
}
=head3 renewals
my $renewals = $checkout->renewals;
Return a Koha::Checkouts::Renewals set attached to this checkout
=cut
sub renewals {
my ( $self ) = @_;
my $renewals_rs = $self->_result->renewals;
return unless $renewals_rs;
return Koha::Checkouts::Renewals->_new_from_dbic( $renewals_rs );
}
=head3 attempt_auto_renew
my ($success, $error, $updated) = $checkout->auto_renew({ confirm => 1 });
Attempt to automatically renew a book. Return error reason if it cannot be renewed.
Also return whether a change has been made to avoid notifying on more than one attempt.
If not passed confirm, we will only report and no changes will be made.
=cut
sub attempt_auto_renew {
my ( $self, $params ) = @_;
my $confirm = $params->{confirm} // 0;
# CanBookBeRenewed returns 'auto_renew' when the renewal should be done by this script
my ( $ok, $error ) = C4::Circulation::CanBookBeRenewed( $self->patron, $self, undef, 1 );
my $store_error;
if ( $error eq 'auto_renew' || $error eq 'auto_renew_final' || $error eq 'auto_unseen_final' ) {
if ($confirm) {
my $date_due = C4::Circulation::AddRenewal(
{
borrowernumber => $self->borrowernumber,
itemnumber => $self->itemnumber,
branch => $self->branchcode,
seen => 0,
automatic => 1,
}
);
$store_error = $error eq 'auto_renew' ? undef : $error;
$self->auto_renew_error($store_error)->store;
}
return ( 1, $store_error, 1 );
} else {
my $updated = 0;
if ( !$self->auto_renew_error || $error ne $self->auto_renew_error ) {
$updated = 1
unless (
$self->auto_renew_error
&& ( $self->auto_renew_error eq 'auto_renew_final' && $error eq 'too_many'
|| $self->auto_renew_error eq 'auto_unseen_final' && $error eq 'too_unseen' )
);
$self->auto_renew_error($error)->store if $confirm;
}
return ( 0, $error, $updated );
}
}
=head3 to_api_mapping
This method returns the mapping for representing a Koha::Checkout object
on the API.
=cut
sub to_api_mapping {
return {
issue_id => 'checkout_id',
borrowernumber => 'patron_id',
itemnumber => 'item_id',
date_due => 'due_date',
branchcode => 'library_id',
returndate => 'checkin_date',
lastreneweddate => 'last_renewed_date',
issuedate => 'checkout_date',
notedate => 'note_date',
noteseen => 'note_seen',
};
}
=head3 claim_returned
my $return_claim = $checkout->claim_returned();
This method sets the checkout as claimed return. It will:
1. Add a new row to the `return_claims` table
2. Set the item as lost using the 'ClaimReturnedLostValue'
3. Charge a fee depending on the value of ClaimReturnedChargeFee
3a. If set to charge, then accruing overdues will be halted
3b. If set to charge, then any existing transfers will be cancelled
and the holding branch will be set back to 'frombranch'.
4. The issue will be marked as returned as per the 'MarkLostItemsAsReturned' preference
=cut
sub claim_returned {
my ( $self, $params ) = @_;
my $charge_lost_fee = $params->{charge_lost_fee};
try {
$self->_result->result_source->schema->txn_do(
sub {
my $claim = Koha::Checkouts::ReturnClaim->new(
{
issue_id => $self->id,
itemnumber => $self->itemnumber,
borrowernumber => $self->borrowernumber,
notes => $params->{notes},
created_by => $params->{created_by},
created_on => dt_from_string,
}
)->store();
my $ClaimReturnedLostValue = C4::Context->preference('ClaimReturnedLostValue');
$self->item->itemlost($ClaimReturnedLostValue)->store;
my $ClaimReturnedChargeFee = C4::Context->preference('ClaimReturnedChargeFee');
$charge_lost_fee =
$ClaimReturnedChargeFee eq 'charge' ? 1
: $ClaimReturnedChargeFee eq 'no_charge' ? 0
: $charge_lost_fee; # $ClaimReturnedChargeFee eq 'ask'
if ( $charge_lost_fee ) {
C4::Circulation::LostItem( $self->itemnumber, 'claim_returned' );
}
elsif ( C4::Context->preference( 'MarkLostItemsAsReturned' ) =~ m/claim_returned/ ) {
C4::Circulation::MarkIssueReturned( $self->borrowernumber, $self->itemnumber, undef, $self->patron->privacy );
}
return $claim;
}
);
}
catch {
if ( $_->isa('Koha::Exception') ) {
$_->rethrow();
}
else {
# ?
Koha::Exception->throw( "Unhandled exception" );
}
};
}
=head2 Internal methods
=head3 _type
=cut
sub _type {
return 'Issue';
}
=head1 AUTHOR
Kyle M Hall <kyle@bywatersolutions.com>
Jonathan Druart <jonathan.druart@bugs.koha-community.org>
=cut
1;