my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branchcode, $borrower );
- my $decreaseLoanHighHoldsDuration = C4::Context->preference('decreaseLoanHighHoldsDuration');
+ my $rule = Koha::CirculationRules->get_effective_rule(
+ {
+ categorycode => $borrower->{categorycode},
+ itemtype => $item_object->effective_itemtype,
+ branchcode => $branchcode,
+ rule_name => 'decreaseloanholds',
+ }
+ );
- my $reduced_datedue = $calendar->addDate( $issuedate, $decreaseLoanHighHoldsDuration );
+ my $duration;
+ if ( defined($rule) && $rule->rule_value ne '' ){
+ # overrides decreaseLoanHighHoldsDuration syspref
+ $duration = $rule->rule_value;
+ } else {
+ $duration = C4::Context->preference('decreaseLoanHighHoldsDuration');
+ }
+ my $reduced_datedue = $calendar->addDate( $issuedate, $duration );
$reduced_datedue->set_hour($orig_due->hour);
$reduced_datedue->set_minute($orig_due->minute);
$reduced_datedue->truncate( to => 'minute' );
if ( DateTime->compare( $reduced_datedue, $orig_due ) == -1 ) {
$return_data->{exceeded} = 1;
- $return_data->{duration} = $decreaseLoanHighHoldsDuration;
+ $return_data->{duration} = $duration;
$return_data->{due_date} = $reduced_datedue;
}
}
auto_renew => $auto_renew ? 1 : 0,
};
+ # Get ID of logged in user. if called from a batch job,
+ # no user session exists and C4::Context->userenv() returns
+ # the scalar '0'. Only do this if the syspref says so
+ if ( C4::Context->preference('RecordStaffUserOnCheckout') ) {
+ my $userenv = C4::Context->userenv();
+ my $usernumber = (ref($userenv) eq 'HASH') ? $userenv->{'number'} : undef;
+ if ($usernumber) {
+ $issue_attributes->{issuer_id} = $usernumber;
+ }
+ }
+
# In the case that the borrower has an on-site checkout
# and SwitchOnSiteCheckouts is enabled this converts it to a regular checkout
$issue = Koha::Checkouts->find( { itemnumber => $item_object->itemnumber } );
}
)->store;
}
+ $issue->discard_changes;
if ( $item_object->location && $item_object->location eq 'CART'
&& ( !$item_object->permanent_location || $item_object->permanent_location ne 'CART' ) ) {
## Item was moved to cart via UpdateItemLocationOnCheckin, anything issued should be taken off the cart.
UpdateTotalIssues( $item_object->biblionumber, 1 );
}
+ # Record if item was lost
+ my $was_lost = $item_object->itemlost;
+
$item_object->issues( ( $item_object->issues || 0 ) + 1);
$item_object->holdingbranch(C4::Context->userenv->{'branch'});
$item_object->itemlost(0);
$item_object->datelastseen( dt_from_string()->ymd() );
$item_object->store({log_action => 0});
+ # If the item was lost, it has now been found, charge the overdue if necessary
+ if ($was_lost) {
+ if ( $item_object->{_charge} ) {
+ $actualissue //= Koha::Old::Checkouts->search(
+ { itemnumber => $item_unblessed->{itemnumber} },
+ {
+ order_by => { '-desc' => 'returndate' },
+ rows => 1
+ }
+ )->single;
+ unless ( exists( $borrower->{branchcode} ) ) {
+ my $patron = $actualissue->patron;
+ $borrower = $patron->unblessed;
+ }
+ _CalculateAndUpdateFine(
+ {
+ issue => $actualissue,
+ item => $item_unblessed,
+ borrower => $borrower,
+ return_date => $issuedate
+ }
+ );
+ _FixOverduesOnReturn( $borrower->{borrowernumber},
+ $item_object->itemnumber, undef, 'RENEWED' );
+ }
+ }
+
# If it costs to borrow this book, charge it to the patron's account.
my ( $charge, $itemtype ) = GetIssuingCharges( $item_object->itemnumber, $borrower->{'borrowernumber'} );
if ( $charge && $charge > 0 ) {
my $updated_item = ModDateLastSeen( $item->itemnumber, $leave_item_lost, { skip_record_index => 1 } ); # will unset itemlost if needed
# fix up the accounts.....
- if ( $item_was_lost ) {
+ if ($item_was_lost) {
$messages->{'WasLost'} = 1;
unless ( C4::Context->preference("BlockReturnOfLostItems") ) {
$messages->{'LostItemFeeRefunded'} = $updated_item->{_refunded};
+ $messages->{'LostItemFeeRestored'} = $updated_item->{_restored};
+
+ if ( $updated_item->{_charge} ) {
+ $issue //= Koha::Old::Checkouts->search(
+ { itemnumber => $item->itemnumber },
+ { order_by => { '-desc' => 'returndate' }, rows => 1 } )
+ ->single;
+ unless ( exists( $patron_unblessed->{branchcode} ) ) {
+ my $patron = $issue->patron;
+ $patron_unblessed = $patron->unblessed;
+ }
+ _CalculateAndUpdateFine(
+ {
+ issue => $issue,
+ item => $item->unblessed,
+ borrower => $patron_unblessed,
+ return_date => $return_date
+ }
+ );
+ _FixOverduesOnReturn( $patron_unblessed->{borrowernumber},
+ $item->itemnumber, undef, 'RETURNED' );
+ $messages->{'LostItemFeeCharged'} = 1;
+ }
}
}
my $amountoutstanding = $accountline->amountoutstanding;
if ( $accountline->amount == 0 && $payments->count == 0 ) {
$accountline->delete;
+ return 0; # no warning, we've just removed a zero value fine (backdated return)
} elsif ($exemptfine && ($amountoutstanding != 0)) {
my $account = Koha::Account->new({patron_id => $borrowernumber});
my $credit = $account->add_credit(
if (C4::Context->preference("FinesLog")) {
&logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item");
}
-
- $accountline->status('FORGIVEN');
- $accountline->store();
- } else {
- $accountline->status($status);
- $accountline->store();
-
}
+
+ $accountline->status($status);
+ return $accountline->store();
}
);
'no_auto_renewal_after_hard_limit',
'lengthunit',
'norenewalbefore',
+ 'unseen_renewals_allowed'
]
}
);
return ( 0, "auto_renew" ) if $auto_renew eq "ok" && !$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.
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 {
my $datedue = shift;
my $lastreneweddate = shift || dt_from_string();
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;
}
);
+ # 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;
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);
$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