From c875042b4c59fb7fb267fb8b97ffa1e20362cfa9 Mon Sep 17 00:00:00 2001 From: Aleisha Amohia Date: Wed, 22 Apr 2020 16:47:17 +0000 Subject: [PATCH] Bug 19532: Expiry and overdue cronjobs, and overdue fine calculation - misc/cronjobs/recalls/expire_recalls.pl - misc/cronjobs/recalls/overdue_recalls.pl - tests for overdue fines in t/db_dependent/Circulation/CalcFine.t --- C4/Overdues.pm | 24 ++++++++++- misc/cronjobs/recalls/expire_recalls.pl | 68 ++++++++++++++++++++++++++++++++ misc/cronjobs/recalls/overdue_recalls.pl | 44 +++++++++++++++++++++ t/db_dependent/Circulation/CalcFine.t | 56 +++++++++++++++++++++++++- 4 files changed, 190 insertions(+), 2 deletions(-) create mode 100755 misc/cronjobs/recalls/expire_recalls.pl create mode 100755 misc/cronjobs/recalls/overdue_recalls.pl diff --git a/C4/Overdues.pm b/C4/Overdues.pm index a9de67d1ee..e4cb1ecbbb 100644 --- a/C4/Overdues.pm +++ b/C4/Overdues.pm @@ -36,6 +36,7 @@ use Koha::DateUtils; use Koha::Account::Lines; use Koha::Account::Offsets; use Koha::Libraries; +use Koha::Recalls; use vars qw(@ISA @EXPORT); @@ -243,6 +244,7 @@ sub CalcFine { 'fine', 'overduefinescap', 'cap_fine_to_replacement_price', + 'recall_overdue_fine', ] } ); @@ -262,7 +264,27 @@ sub CalcFine { # If chargeperiod_charge_at = 1, we charge a fine at the start of each charge period # if chargeperiod_charge_at = 0, we charge at the end of each charge period $charge_periods = defined $issuing_rule->{chargeperiod_charge_at} && $issuing_rule->{chargeperiod_charge_at} == 1 ? ceil($charge_periods) : floor($charge_periods); - $amount = $charge_periods * $issuing_rule->{fine}; + + # check if item has been recalled. recall should have been marked Overdue by cronjob, so only look at overdue recalls + # only charge using recall_overdue_fine if there is an item-level recall for this particular item, OR a biblio-level recall + my @recalls = Koha::Recalls->search({ biblionumber => $item->{biblionumber}, old => undef, status => 'O' }); + my $bib_level_recall = 0; + $bib_level_recall = 1 if scalar @recalls > 0; + foreach my $recall ( @recalls ) { + if ( $recall->item_level_recall and $recall->itemnumber == $item->{itemnumber} and $issuing_rule->{recall_overdue_fine} ) { + $bib_level_recall = 0; + $amount = $charge_periods * $issuing_rule->{recall_overdue_fine}; + last; + } + } + if ( $bib_level_recall and $issuing_rule->{recall_overdue_fine} ) { + # biblio-level recall + $amount = $charge_periods * $issuing_rule->{recall_overdue_fine}; + } + if ( scalar @recalls == 0 ) { + # no recall, use normal fine amount + $amount = $charge_periods * $issuing_rule->{fine}; + } } # else { # a zero (or null) chargeperiod or negative units_minus_grace value means no charge. } $amount = $issuing_rule->{overduefinescap} if $issuing_rule->{overduefinescap} && $amount > $issuing_rule->{overduefinescap}; diff --git a/misc/cronjobs/recalls/expire_recalls.pl b/misc/cronjobs/recalls/expire_recalls.pl new file mode 100755 index 0000000000..891911d546 --- /dev/null +++ b/misc/cronjobs/recalls/expire_recalls.pl @@ -0,0 +1,68 @@ +#!/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; + +BEGIN { + # find Koha's Perl modules + # test carefully before changing this + use FindBin; + eval { require "$FindBin::Bin/../kohalib.pl" }; +} + +# set overdue recalls as overdue. This includes: +# - recalls that have been requested and not fulfilled and have passed their expiration date +# - recalls that have been awaiting pickup for longer than the specified recall_shelf_time circulation rule, or the RecallsMaxPickUpDelay if circ rule is unset + +use Koha::Script -cron; +use Koha::DateUtils; +use Koha::Recalls; +use C4::Log; + +cronlogaction(); + +my @recalls = Koha::Recalls->search({ old => undef }); +foreach my $recall (@recalls) { + if ( ( $recall->requested or $recall->overdue ) and $recall->expirationdate and dt_from_string( $recall->expirationdate ) < dt_from_string() ){ + # recall is requested or overdue and has surpassed the specified expiration date + $recall->set_expired({ interface => 'COMMANDLINE' }); + } + if ( $recall->waiting ) { + my $recall_shelf_time = Koha::CirculationRules->get_effective_rule({ + categorycode => $recall->patron->categorycode, + itemtype => $recall->item->effective_itemtype, + branchcode => $recall->branchcode, + rule_name => 'recall_shelf_time', + }); + my $waitingdate = dt_from_string( $recall->waitingdate ); + my $now = dt_from_string(); + my $days_waiting = $now->subtract_datetime( $waitingdate ); + if ( defined $recall_shelf_time and $recall_shelf_time->rule_value > 0 ) { + if ( $days_waiting->days > $recall_shelf_time->rule_value ) { + # recall has been awaiting pickup for longer than the circ rules allow + $recall->set_expired({ interface => 'COMMANDLINE' }); + } + } else { + if ( $days_waiting->days > C4::Context->preference('RecallsMaxPickUpDelay') ) { + # recall has been awaiting pickup for longer than the syspref allows + $recall->set_expired({ interface => 'COMMANDLINE' }); + } + } + } +} diff --git a/misc/cronjobs/recalls/overdue_recalls.pl b/misc/cronjobs/recalls/overdue_recalls.pl new file mode 100755 index 0000000000..70f0ea9596 --- /dev/null +++ b/misc/cronjobs/recalls/overdue_recalls.pl @@ -0,0 +1,44 @@ +#!/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; + +BEGIN { + # find Koha's Perl modules + # test carefully before changing this + use FindBin; + eval { require "$FindBin::Bin/../kohalib.pl" }; +} + +# set overdue recalls as overdue + +use Koha::Script -cron; +use Koha::DateUtils; +use Koha::Checkouts; +use Koha::Recalls; +use C4::Log; + +cronlogaction(); + +my @recalls = Koha::Recalls->search({ status => 'R' }); +foreach my $recall (@recalls){ + if ( $recall->should_be_overdue ){ + $recall->set_overdue({ interface => 'COMMANDLINE' }); + } +} diff --git a/t/db_dependent/Circulation/CalcFine.t b/t/db_dependent/Circulation/CalcFine.t index f09a0e011d..c26aa82ce7 100644 --- a/t/db_dependent/Circulation/CalcFine.t +++ b/t/db_dependent/Circulation/CalcFine.t @@ -2,7 +2,7 @@ use Modern::Perl; -use Test::More tests => 3; +use Test::More tests => 4; use C4::Context; use C4::Overdues; @@ -204,6 +204,60 @@ subtest 'Test cap_fine_to_replacement_pricew with overduefinescap' => sub { teardown(); }; +subtest 'Recall overdue fines' => sub { + plan tests => 2; + + Koha::CirculationRules->set_rules( + { + branchcode => undef, + categorycode => undef, + itemtype => undef, + rules => { + fine => '1.00', + lengthunit => 'days', + finedays => 0, + firstremind => 0, + chargeperiod => 1, + recall_overdue_fine => '5.00', + }, + } + ); + + my $start_dt = DateTime->new( + year => 2000, + month => 1, + day => 1, + ); + + my $end_dt = DateTime->new( + year => 2000, + month => 1, + day => 6, + ); + + my $recall = Koha::Recall->new({ + borrowernumber => $patron->{borrowernumber}, + recalldate => dt_from_string, + biblionumber => $item->{biblionumber}, + branchcode => $branch->{branchcode}, + status => 'R', + itemnumber => $item->{itemnumber}, + expirationdate => undef, + item_level_recall => 1 + })->store; + $recall->set_overdue; + + my ($amount) = CalcFine( $item, $patron->{categorycode}, $branch->{branchcode}, $start_dt, $end_dt ); + is( int($amount), 25, 'Use recall fine amount specified in circulation rules' ); + + $recall->set_finished; + ($amount) = CalcFine( $item, $patron->{categorycode}, $branch->{branchcode}, $start_dt, $end_dt ); + is( int($amount), 5, 'With no recall, use normal fine amount' ); + + + teardown(); +}; + sub teardown { $dbh->do(q|DELETE FROM circulation_rules|); } -- 2.11.0