@@ -, +, @@ lose. dated for a return on the date the item is 'found' fine (dated for a 'return' on the date the item was 'lost' --- C4/Circulation.pm | 154 +++++++++++++++++- Koha/CirculationRules.pm | 19 ++- admin/smart-rules.pl | 16 +- .../prog/en/modules/admin/smart-rules.tt | 87 ++++++---- 4 files changed, 227 insertions(+), 49 deletions(-) --- a/C4/Circulation.pm +++ a/C4/Circulation.pm @@ -1534,6 +1534,9 @@ sub AddIssue { 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); @@ -1542,6 +1545,52 @@ sub AddIssue { $item_object->store({log_action => 0}); ModDateLastSeen( $item_object->itemnumber ); + # If the item was lost, it has now been found, restore/charge the overdue if necessary + if ($was_lost) { + my $lostreturn_policy = + Koha::CirculationRules->get_lostreturn_policy( + { + return_branch => C4::Context->userenv->{branch}, + item => $item_object + } + ); + + if ($lostreturn_policy) { + if ( $lostreturn_policy eq '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' ); + } + elsif ( $lostreturn_policy eq 'restore' ) { + _RestoreOverdueForLostAndFound( + $item_object->itemnumber ); + } + + if ( C4::Context->preference('AccountAutoReconcile') ) { + $account->reconcile_balance; + } + } + + } + # 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 ) { @@ -2047,6 +2096,42 @@ sub AddReturn { $messages->{'WasLost'} = 1; unless ( C4::Context->preference("BlockReturnOfLostItems") ) { $messages->{'LostItemFeeRefunded'} = $updated_item->{_refunded}; + + my $lostreturn_policy = + Koha::CirculationRules->get_lostreturn_policy( + { + return_branch => C4::Context->userenv->{branch}, + item => $updated_item + } + ); + + if ($lostreturn_policy) { + if ( $lostreturn_policy eq '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; + } + elsif ( $lostreturn_policy eq 'restore' ) { + _RestoreOverdueForLostAndFound( $item->itemnumber ); + $messages->{'LostItemFeeRestored'} = 1; + } + } } } @@ -2471,6 +2556,7 @@ sub _FixOverduesOnReturn { 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( @@ -2489,14 +2575,70 @@ sub _FixOverduesOnReturn { 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(); + } + ); - } + return $result; +} + +=head2 _RestoreOverdueForLostAndFound + + &_RestoreOverdueForLostAndFound( $itemnumber ); + +C<$itemnumber> itemnumber + +Internal function + +=cut + +sub _RestoreOverdueForLostAndFound { + my ( $item ) = @_; + + unless( $item ) { + warn "_RestoreOverdueForLostAndFound() not supplied valid itemnumber"; + return; + } + + my $schema = Koha::Database->schema; + + my $result = $schema->txn_do( + sub { + # check for lost overdue fine + my $accountlines = Koha::Account::Lines->search( + { + itemnumber => $item, + debit_type_code => 'OVERDUE', + status => 'LOST' + }, + { + order_by => { '-desc' => 'date' }, + rows => 1 + } + ); + return 0 unless $accountlines->count; # no warning, there's just nothing to fix + + # Update status of fine + my $overdue = $accountlines->next; + $overdue->status('RETURNED')->store(); + + # Find related forgive credit + my $refunds = $overdue->credits( + { + credit_type_code => 'FORGIVEN', + itemnumber => $item, + status => [ { '!=' => 'VOID' }, undef ] + }, + { order_by => { '-desc' => 'date' }, rows => 1 } + ); + return 0 unless $refunds->count; # no warning, there's just nothing to fix + + # Revert the forgive credit + my $refund = $refunds->next; + return $refund->void(); } ); --- a/Koha/CirculationRules.pm +++ a/Koha/CirculationRules.pm @@ -46,7 +46,7 @@ Any attempt to set a rule with a nonsensical scope (for instance, setting the C< =cut our $RULE_KINDS = { - refund => { + lostreturn => { scope => [ 'branchcode' ], }, @@ -433,7 +433,18 @@ sub get_onshelfholds_policy { =head3 get_lostreturn_policy - my $refund = Koha::CirculationRules->get_lostreturn_policy( { return_branch => $return_branch, item => $item } ); + my $lostrefund_policy = Koha::CirculationRules->get_lostreturn_policy( { return_branch => $return_branch, item => $item } ); + +Return values are: + +=over 2 + +=item 0 - Do not refund +=item refund - Refund the lost item charge +=item restore - Refund the lost item charge and restore the original overdue fine +=item charge - Refund the lost item charge and charge a new overdue fine + +=back =cut @@ -454,11 +465,11 @@ sub get_lostreturn_policy { my $rule = Koha::CirculationRules->get_effective_rule( { branchcode => $branch, - rule_name => 'refund', + rule_name => 'lostreturn', } ); - return $rule ? $rule->rule_value : 1; + return $rule ? $rule->rule_value : 'refund'; } =head3 article_requestable_rules --- a/admin/smart-rules.pl +++ a/admin/smart-rules.pl @@ -517,16 +517,16 @@ elsif ($op eq "add-branch-item") { } elsif ( $op eq 'mod-refund-lost-item-fee-rule' ) { - my $refund = $input->param('refund'); + my $lostreturn = $input->param('lostreturn'); - if ( $refund eq '*' ) { + if ( $lostreturn eq '*' ) { if ( $branch ne '*' ) { - # only do something for $refund eq '*' if branch-specific + # only do something for $lostreturn eq '*' if branch-specific Koha::CirculationRules->set_rules( { branchcode => $branch, rules => { - refund => undef + lostreturn => undef } } ); @@ -536,18 +536,18 @@ elsif ( $op eq 'mod-refund-lost-item-fee-rule' ) { { branchcode => $branch, rules => { - refund => $refund + lostreturn => $lostreturn } } ); } } -my $refundLostItemFeeRule = Koha::CirculationRules->find({ branchcode => ($branch eq '*') ? undef : $branch, rule_name => 'refund' }); -my $defaultLostItemFeeRule = Koha::CirculationRules->find({ branchcode => undef, rule_name => 'refund' }); +my $refundLostItemFeeRule = Koha::CirculationRules->find({ branchcode => ($branch eq '*') ? undef : $branch, rule_name => 'lostreturn' }); +my $defaultLostItemFeeRule = Koha::CirculationRules->find({ branchcode => undef, rule_name => 'lostreturn' }); $template->param( refundLostItemFeeRule => $refundLostItemFeeRule, - defaultRefundRule => $defaultLostItemFeeRule ? $defaultLostItemFeeRule->rule_value : 1 + defaultRefundRule => $defaultLostItemFeeRule ? $defaultLostItemFeeRule->rule_value : 'refund' ); my $patron_categories = Koha::Patron::Categories->search({}, { order_by => ['description'] }); --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt @@ -762,23 +762,35 @@ - [%# Default branch %] [% IF ( current_branch == '*' ) %] - [% IF ( defaultRefundRule ) %] - + + + + [% ELSIF ( defaultRefundRule == 'charge' ) %] + + + + + [% ELSIF ( defaultRefundRule == 'restore' ) %] + + + + + [% ELSIF ( defaultRefundRule == 0 ) %] + + + + [% ELSE %] - + + + [% END %] - Yes - - [% IF ( not defaultRefundRule ) %] - [% ELSE %] [%# Branch-specific %] [% IF ( not refundLostItemFeeRule ) %] @@ -786,30 +798,43 @@ [% ELSE %] [% IF ( not refundLostItemFeeRule ) %] - - + + + + [% ELSE %] - [% IF ( refundLostItemFeeRule.rule_value ) %] - + + + + [% ELSIF ( refundLostItemFeeRule.rule_value == 'charge' ) %] + + + + + [% ELSIF ( refundLostItemFeeRule.rule_value == 'restore' ) %] + + + + + [% ELSIF ( refundLostItemFeeRule.rule_value == 0 ) %] + + + + [% END %] - Yes - - [% IF ( not refundLostItemFeeRule.rule_value ) %] - [% END %] [% END %] --