X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FCirculation.pm;h=d2506275e3a2e7095f41d49a23b0a5a5a1b7b3f1;hb=4fcf7af117153690cc4aca55eb47290dfc5814f6;hp=560abc143099ac0736912b1cd08e6793299db2e2;hpb=86f37b1eb3a3376f475e69d351f0b0983b2b482a;p=koha-ffzg.git diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 560abc1430..d2506275e3 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -18,9 +18,7 @@ package C4::Circulation; # You should have received a copy of the GNU General Public License # along with Koha; if not, see . - -use strict; -#use warnings; FIXME - Bug 2505 +use Modern::Perl; use DateTime; use POSIX qw( floor ); use Koha::DateUtils; @@ -54,13 +52,13 @@ use Koha::Database; use Koha::Libraries; use Koha::Account::Lines; use Koha::Holds; -use Koha::RefundLostItemFeeRule; use Koha::RefundLostItemFeeRules; use Koha::Account::Lines; use Koha::Account::Offsets; use Koha::Config::SysPrefs; use Koha::Charges::Fees; use Koha::Util::SystemPreferences; +use Koha::Checkouts::ReturnClaims; use Carp; use List::MoreUtils qw( uniq any ); use Scalar::Util qw( looks_like_number ); @@ -315,10 +313,10 @@ sub transferbook { unless ( $item ) { $messages->{'BadBarcode'} = $barcode; $dotransfer = 0; + return ( $dotransfer, $messages ); } my $itemnumber = $item->itemnumber; - my $issue = GetOpenIssue($itemnumber); # get branches of book... my $hbr = $item->homebranch; my $fbr = $item->holdingbranch; @@ -344,6 +342,7 @@ sub transferbook { } # check if it is still issued to someone, return it... + my $issue = Koha::Checkouts->find({ itemnumber => $itemnumber }); if ( $issue ) { AddReturn( $barcode, $fbr ); $messages->{'WasReturned'} = $issue->borrowernumber; @@ -375,8 +374,7 @@ sub transferbook { sub TooMany { my $borrower = shift; - my $biblionumber = shift; - my $item = shift; + my $item_object = shift; my $params = shift; my $onsite_checkout = $params->{onsite_checkout} || 0; my $switch_onsite_checkout = $params->{switch_onsite_checkout} || 0; @@ -384,11 +382,9 @@ sub TooMany { my $dbh = C4::Context->dbh; my $branch; # Get which branchcode we need - $branch = _GetCircControlBranch($item,$borrower); - my $type = (C4::Context->preference('item-level_itypes')) - ? $item->{'itype'} # item-level - : $item->{'itemtype'}; # biblio-level - + $branch = _GetCircControlBranch($item_object->unblessed,$borrower); + my $type = $item_object->effective_itemtype; + # given branch, patron category, and item type, determine # applicable issuing rule my $maxissueqty_rule = Koha::CirculationRules->get_effective_rule( @@ -710,8 +706,7 @@ sub CanBookBeIssued { unless ( $duedate ) { my $issuedate = $now->clone(); - my $branch = $circ_library; - $duedate = CalcDateDue( $issuedate, $effective_itemtype, $branch, $patron_unblessed ); + $duedate = CalcDateDue( $issuedate, $effective_itemtype, $circ_library->branchcode, $patron_unblessed ); # Offline circ calls AddIssue directly, doesn't run through here # So issuingimpossible should be ok. @@ -753,11 +748,11 @@ sub CanBookBeIssued { return( { STATS => 1 }, {}); } - if ( $patron->gonenoaddress == 1 ) { + if ( $patron->gonenoaddress && $patron->gonenoaddress == 1 ) { $issuingimpossible{GNA} = 1; } - if ( $patron->lost == 1 ) { + if ( $patron->lost && $patron->lost == 1 ) { $issuingimpossible{CARD_LOST} = 1; } if ( $patron->is_debarred ) { @@ -786,7 +781,7 @@ sub CanBookBeIssued { my $no_issues_charge_guarantees = C4::Context->preference("NoIssuesChargeGuarantees"); $no_issues_charge_guarantees = undef unless looks_like_number( $no_issues_charge_guarantees ); if ( defined $no_issues_charge_guarantees ) { - my @guarantees = $patron->guarantees(); + my @guarantees = map { $_->guarantee } $patron->guarantee_relationships(); my $guarantees_non_issues_charges; foreach my $g ( @guarantees ) { $guarantees_non_issues_charges += $g->account->non_issues_charges; @@ -906,7 +901,7 @@ sub CanBookBeIssued { and $issue and $issue->onsite_checkout and $issue->borrowernumber == $patron->borrowernumber ? 1 : 0 ); - my $toomany = TooMany( $patron_unblessed, $item_object->biblionumber, $item_unblessed, { onsite_checkout => $onsite_checkout, switch_onsite_checkout => $switch_onsite_checkout, } ); + my $toomany = TooMany( $patron_unblessed, $item_object, { onsite_checkout => $onsite_checkout, switch_onsite_checkout => $switch_onsite_checkout, } ); # if TooMany max_allowed returns 0 the user doesn't have permission to check out this book if ( $toomany && not exists $needsconfirmation{RENEW_ISSUE} ) { if ( $toomany->{max_allowed} == 0 ) { @@ -1001,16 +996,23 @@ sub CanBookBeIssued { if ( $patron->branchcode ne $userenv->{branch} ); } } + # # CHECK IF THERE IS RENTAL CHARGES. RENTAL MUST BE CONFIRMED BY THE BORROWER # my $rentalConfirmation = C4::Context->preference("RentalFeesCheckoutConfirmation"); - - if ( $rentalConfirmation ){ + if ($rentalConfirmation) { my ($rentalCharge) = GetIssuingCharges( $item_object->itemnumber, $patron->borrowernumber ); - my $itemtype = Koha::ItemTypes->find( $item_object->itype ); # GetItem sets effective itemtype - $rentalCharge += $fees->accumulate_rentalcharge({ from => dt_from_string(), to => $duedate }); - if ( $rentalCharge > 0 ){ + + my $itemtype_object = Koha::ItemTypes->find( $item_object->effective_itemtype ); + if ($itemtype_object) { + my $accumulate_charge = $fees->accumulate_rentalcharge(); + if ( $accumulate_charge > 0 ) { + $rentalCharge += $accumulate_charge; + } + } + + if ( $rentalCharge > 0 ) { $needsconfirmation{RENTALCHARGE} = $rentalCharge; } } @@ -1175,7 +1177,7 @@ sub CanBookBeReturned { sub checkHighHolds { my ( $item, $borrower ) = @_; - my $branch = _GetCircControlBranch( $item, $borrower ); + my $branchcode = _GetCircControlBranch( $item, $borrower ); my $item_object = Koha::Items->find( $item->{itemnumber} ); my $return_data = { @@ -1233,10 +1235,10 @@ sub checkHighHolds { my $issuedate = DateTime->now( time_zone => C4::Context->tz() ); - my $calendar = Koha::Calendar->new( branchcode => $branch ); + my $calendar = Koha::Calendar->new( branchcode => $branchcode ); my $itype = $item_object->effective_itemtype; - my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branch, $borrower ); + my $orig_due = C4::Circulation::CalcDateDue( $issuedate, $itype, $branchcode, $borrower ); my $decreaseLoanHighHoldsDuration = C4::Context->preference('decreaseLoanHighHoldsDuration'); @@ -1326,7 +1328,7 @@ sub AddIssue { or return; # if we don't get an Item, abort. my $item_unblessed = $item_object->unblessed; - my $branch = _GetCircControlBranch( $item_unblessed, $borrower ); + my $branchcode = _GetCircControlBranch( $item_unblessed, $borrower ); # get actual issuing if there is one my $actualissue = $item_object->checkout; @@ -1337,7 +1339,7 @@ sub AddIssue { $datedue = AddRenewal( $borrower->{'borrowernumber'}, $item_object->itemnumber, - $branch, + $branchcode, $datedue, $issuedate, # here interpreted as the renewal date ); @@ -1345,13 +1347,13 @@ sub AddIssue { else { unless ($datedue) { my $itype = $item_object->effective_itemtype; - $datedue = CalcDateDue( $issuedate, $itype, $branch, $borrower ); + $datedue = CalcDateDue( $issuedate, $itype, $branchcode, $borrower ); } $datedue->truncate( to => 'minute' ); my $patron = Koha::Patrons->find( $borrower ); - my $library = Koha::Libraries->find( $branch ); + my $library = Koha::Libraries->find( $branchcode ); my $fees = Koha::Charges::Fees->new( { patron => $patron, @@ -1392,7 +1394,7 @@ sub AddIssue { my $issuing_rule = Koha::IssuingRules->get_effective_issuing_rule( { categorycode => $borrower->{categorycode}, itemtype => $item_object->effective_itemtype, - branchcode => $branch + branchcode => $branchcode } ); @@ -1402,7 +1404,7 @@ sub AddIssue { # Record in the database the fact that the book was issued. unless ($datedue) { my $itype = $item_object->effective_itemtype; - $datedue = CalcDateDue( $issuedate, $itype, $branch, $borrower ); + $datedue = CalcDateDue( $issuedate, $itype, $branchcode, $borrower ); } $datedue->truncate( to => 'minute' ); @@ -1428,7 +1430,8 @@ sub AddIssue { } )->store; } - if ( $item_object->location eq 'CART' && $item_object->permanent_location ne 'CART' ) { + 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. CartToShelf( $item_object->itemnumber ); } @@ -1456,7 +1459,7 @@ sub AddIssue { ModItem( { - issues => $item_object->issues + 1, + issues => ( $item_object->issues || 0 ) + 1, holdingbranch => C4::Context->userenv->{'branch'}, itemlost => 0, onloan => $datedue->ymd(), @@ -1470,16 +1473,15 @@ sub AddIssue { # 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 > 0 ) { - my $description = "Rental"; - AddIssuingCharge( $issue, $charge, $description ); + if ( $charge && $charge > 0 ) { + AddIssuingCharge( $issue, $charge, 'RENT' ); } my $itemtype_object = Koha::ItemTypes->find( $item_object->effective_itemtype ); if ( $itemtype_object ) { my $accumulate_charge = $fees->accumulate_rentalcharge(); if ( $accumulate_charge > 0 ) { - AddIssuingCharge( $issue, $accumulate_charge, 'Daily rental' ) if $accumulate_charge > 0; + AddIssuingCharge( $issue, $accumulate_charge, 'RENT_DAILY' ); $charge += $accumulate_charge; $item_unblessed->{charge} = $charge; } @@ -1503,7 +1505,7 @@ sub AddIssue { # Send a checkout slip. my $circulation_alert = 'C4::ItemCirculationAlertPreference'; my %conditions = ( - branchcode => $branch, + branchcode => $branchcode, categorycode => $borrower->{categorycode}, item_type => $item_object->effective_itemtype, notification => 'CHECKOUT', @@ -1514,7 +1516,7 @@ sub AddIssue { type => 'CHECKOUT', item => $item_object->unblessed, borrower => $borrower, - branch => $branch, + branch => $branchcode, } ); } @@ -1950,17 +1952,15 @@ sub AddReturn { # case of a return of document (deal with issues and holdingbranch) if ($doreturn) { - my $is_overdue; die "The item is not issed and cannot be returned" unless $issue; # Just in case... $patron or warn "AddReturn without current borrower"; - $is_overdue = $issue->is_overdue( $return_date ); if ($patron) { eval { MarkIssueReturned( $borrowernumber, $item->itemnumber, $return_date, $patron->privacy ); }; unless ( $@ ) { - if ( C4::Context->preference('CalculateFinesOnReturn') && $is_overdue && !$item->itemlost ) { + if ( C4::Context->preference('CalculateFinesOnReturn') && !$item->itemlost ) { _CalculateAndUpdateFine( { issue => $issue, item => $item_unblessed, borrower => $patron_unblessed, return_date => $return_date } ); } } else { @@ -2031,7 +2031,7 @@ sub AddReturn { # fix up the overdues in accounts... if ($borrowernumber) { - my $fix = _FixOverduesOnReturn( $borrowernumber, $item->itemnumber, $exemptfine ); + my $fix = _FixOverduesOnReturn( $borrowernumber, $item->itemnumber, $exemptfine, 'RETURNED' ); defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->itemnumber...) failed!"; # zero is OK, check defined if ( $issue and $issue->is_overdue ) { @@ -2125,6 +2125,19 @@ sub AddReturn { } } + if ( C4::Context->preference('ClaimReturnedLostValue') ) { + my $claims = Koha::Checkouts::ReturnClaims->search( + { + itemnumber => $item->id, + resolution => undef, + } + ); + + if ( $claims->count ) { + $messages->{ReturnClaims} = $claims; + } + } + return ( $doreturn, $messages, $issue, ( $patron ? $patron->unblessed : {} )); } @@ -2156,7 +2169,7 @@ sub MarkIssueReturned { my $issue_id = $issue->issue_id; my $anonymouspatron; - if ( $privacy == 2 ) { + if ( $privacy && $privacy == 2 ) { # The default of 0 will not work due to foreign key constraints # The anonymisation will fail if AnonymousPatron is not a valid entry # We need to check if the anonymous patron exist, Koha will fail loudly if it does not @@ -2183,7 +2196,7 @@ sub MarkIssueReturned { my $old_checkout = Koha::Old::Checkout->new($issue->unblessed)->store; # anonymise patron checkout immediately if $privacy set to 2 and AnonymousPatron is set to a valid borrowernumber - if ( $privacy == 2) { + if ( $privacy && $privacy == 2) { $old_checkout->borrowernumber($anonymouspatron)->store; } @@ -2219,14 +2232,15 @@ Internal function, called only by AddReturn that calculates and updates Should only be called for overdue returns +Calculation of the debarment date has been moved to a separate subroutine _calculate_new_debar_dt +to ease testing. + =cut -sub _debar_user_on_return { +sub _calculate_new_debar_dt { my ( $borrower, $item, $dt_due, $return_date ) = @_; my $branchcode = _GetCircControlBranch( $item, $borrower ); - $return_date //= dt_from_string(); - my $circcontrol = C4::Context->preference('CircControl'); my $issuing_rule = Koha::IssuingRules->get_effective_issuing_rule( { categorycode => $borrower->{categorycode}, @@ -2238,84 +2252,95 @@ sub _debar_user_on_return { my $unit = $issuing_rule ? $issuing_rule->lengthunit : undef; my $chargeable_units = C4::Overdues::get_chargeable_units($unit, $dt_due, $return_date, $branchcode); - if ($finedays) { + return unless $finedays; - # finedays is in days, so hourly loans must multiply by 24 - # thus 1 hour late equals 1 day suspension * finedays rate - $finedays = $finedays * 24 if ( $unit eq 'hours' ); + # finedays is in days, so hourly loans must multiply by 24 + # thus 1 hour late equals 1 day suspension * finedays rate + $finedays = $finedays * 24 if ( $unit eq 'hours' ); - # grace period is measured in the same units as the loan - my $grace = - DateTime::Duration->new( $unit => $issuing_rule->firstremind ); + # grace period is measured in the same units as the loan + my $grace = + DateTime::Duration->new( $unit => $issuing_rule->firstremind ); - my $deltadays = DateTime::Duration->new( - days => $chargeable_units - ); - if ( $deltadays->subtract($grace)->is_positive() ) { - my $suspension_days = $deltadays * $finedays; - - # If the max suspension days is < than the suspension days - # the suspension days is limited to this maximum period. - my $max_sd = $issuing_rule->maxsuspensiondays; - if ( defined $max_sd ) { - $max_sd = DateTime::Duration->new( days => $max_sd ); - $suspension_days = $max_sd - if DateTime::Duration->compare( $max_sd, $suspension_days ) < 0; - } + my $deltadays = DateTime::Duration->new( + days => $chargeable_units + ); - my ( $has_been_extended, $is_a_reminder ); - if ( C4::Context->preference('CumulativeRestrictionPeriods') and $borrower->{debarred} ) { - my $debarment = @{ GetDebarments( { borrowernumber => $borrower->{borrowernumber}, type => 'SUSPENSION' } ) }[0]; - if ( $debarment ) { - $return_date = dt_from_string( $debarment->{expiration}, 'sql' ); - $has_been_extended = 1; - } - } + if ( $deltadays->subtract($grace)->is_positive() ) { + my $suspension_days = $deltadays * $finedays; - if ( $issuing_rule->suspension_chargeperiod > 1 ) { - # No need to / 1 and do not consider / 0 - $suspension_days = DateTime::Duration->new( - days => floor( $suspension_days->in_units('days') / $issuing_rule->suspension_chargeperiod ) - ); - } + if ( $issuing_rule->suspension_chargeperiod > 1 ) { + # No need to / 1 and do not consider / 0 + $suspension_days = DateTime::Duration->new( + days => floor( $suspension_days->in_units('days') / $issuing_rule->suspension_chargeperiod ) + ); + } - my $new_debar_dt; - # Use the calendar or not to calculate the debarment date - if ( C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed' ) { - my $calendar = Koha::Calendar->new( - branchcode => $branchcode, - days_mode => 'Calendar' - ); - $new_debar_dt = $calendar->addDate( $return_date, $suspension_days ); - } - else { - $new_debar_dt = $return_date->clone()->add_duration($suspension_days); - } + # If the max suspension days is < than the suspension days + # the suspension days is limited to this maximum period. + my $max_sd = $issuing_rule->maxsuspensiondays; + if ( defined $max_sd ) { + $max_sd = DateTime::Duration->new( days => $max_sd ); + $suspension_days = $max_sd + if DateTime::Duration->compare( $max_sd, $suspension_days ) < 0; + } - Koha::Patron::Debarments::AddUniqueDebarment({ - borrowernumber => $borrower->{borrowernumber}, - expiration => $new_debar_dt->ymd(), - type => 'SUSPENSION', - }); - # if borrower was already debarred but does not get an extra debarment - my $patron = Koha::Patrons->find( $borrower->{borrowernumber} ); - my $new_debarment_str; - if ( $borrower->{debarred} eq $patron->is_debarred ) { - $is_a_reminder = 1; - $new_debarment_str = $borrower->{debarred}; - } else { - $new_debarment_str = $new_debar_dt->ymd(); + my ( $has_been_extended ); + if ( C4::Context->preference('CumulativeRestrictionPeriods') and $borrower->{debarred} ) { + my $debarment = @{ GetDebarments( { borrowernumber => $borrower->{borrowernumber}, type => 'SUSPENSION' } ) }[0]; + if ( $debarment ) { + $return_date = dt_from_string( $debarment->{expiration}, 'sql' ); + $has_been_extended = 1; } - # FIXME Should return a DateTime object - return $new_debarment_str, $is_a_reminder; } + + my $new_debar_dt; + # Use the calendar or not to calculate the debarment date + if ( C4::Context->preference('SuspensionsCalendar') eq 'noSuspensionsWhenClosed' ) { + my $calendar = Koha::Calendar->new( + branchcode => $branchcode, + days_mode => 'Calendar' + ); + $new_debar_dt = $calendar->addDate( $return_date, $suspension_days ); + } + else { + $new_debar_dt = $return_date->clone()->add_duration($suspension_days); + } + return $new_debar_dt; } return; } +sub _debar_user_on_return { + my ( $borrower, $item, $dt_due, $return_date ) = @_; + + $return_date //= dt_from_string(); + + my $new_debar_dt = _calculate_new_debar_dt ($borrower, $item, $dt_due, $return_date); + + return unless $new_debar_dt; + + Koha::Patron::Debarments::AddUniqueDebarment({ + borrowernumber => $borrower->{borrowernumber}, + expiration => $new_debar_dt->ymd(), + type => 'SUSPENSION', + }); + # if borrower was already debarred but does not get an extra debarment + my $patron = Koha::Patrons->find( $borrower->{borrowernumber} ); + my ($new_debarment_str, $is_a_reminder); + if ( $borrower->{debarred} && $borrower->{debarred} eq $patron->is_debarred ) { + $is_a_reminder = 1; + $new_debarment_str = $borrower->{debarred}; + } else { + $new_debarment_str = $new_debar_dt->ymd(); + } + # FIXME Should return a DateTime object + return $new_debarment_str, $is_a_reminder; +} + =head2 _FixOverduesOnReturn - &_FixOverduesOnReturn($borrowernumber, $itemnumber, $exemptfine); + &_FixOverduesOnReturn($borrowernumber, $itemnumber, $exemptfine, $status); C<$borrowernumber> borrowernumber @@ -2323,12 +2348,14 @@ C<$itemnumber> itemnumber C<$exemptfine> BOOL -- remove overdue charge associated with this issue. +C<$status> ENUM -- reason for fix [ RETURNED, RENEWED, LOST, FORGIVEN ] + Internal function =cut sub _FixOverduesOnReturn { - my ( $borrowernumber, $item, $exemptfine ) = @_; + my ( $borrowernumber, $item, $exemptfine, $status ) = @_; unless( $borrowernumber ) { warn "_FixOverduesOnReturn() not supplied valid borrowernumber"; return; @@ -2337,6 +2364,10 @@ sub _FixOverduesOnReturn { warn "_FixOverduesOnReturn() not supplied valid itemnumber"; return; } + unless( $status ) { + warn "_FixOverduesOnReturn() not supplied valid status"; + return; + } my $schema = Koha::Database->schema; @@ -2345,10 +2376,10 @@ sub _FixOverduesOnReturn { # check for overdue fine my $accountlines = Koha::Account::Lines->search( { - borrowernumber => $borrowernumber, - itemnumber => $item, - accounttype => 'OVERDUE', - status => 'UNRETURNED' + borrowernumber => $borrowernumber, + itemnumber => $item, + debit_type_code => 'OVERDUE', + status => 'UNRETURNED' } ); return 0 unless $accountlines->count; # no warning, there's just nothing to fix @@ -2364,12 +2395,12 @@ sub _FixOverduesOnReturn { user_id => C4::Context->userenv ? C4::Context->userenv->{'number'} : undef, library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef, interface => C4::Context->interface, - type => 'forgiven', + type => 'FORGIVEN', item_id => $item } ); - $credit->apply({ debits => $accountlines->reset, offset_type => 'Forgiven' }); + $credit->apply({ debits => [ $accountline ], offset_type => 'Forgiven' }); $accountline->status('FORGIVEN'); @@ -2377,7 +2408,7 @@ sub _FixOverduesOnReturn { &logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item"); } } else { - $accountline->status('RETURNED'); + $accountline->status($status); } return $accountline->store(); @@ -2391,7 +2422,9 @@ sub _FixOverduesOnReturn { &_FixAccountForLostAndReturned($itemnumber, [$borrowernumber, $barcode]); -Calculates the charge for a book lost and returned. +Finds the most recent lost item charge for this item and refunds the borrower +appropriatly, taking into account any payments or writeoffs already applied +against the charge. Internal function, not exported, called only by AddReturn. @@ -2407,8 +2440,9 @@ sub _FixAccountForLostAndReturned { # check for charge made for lost book my $accountlines = Koha::Account::Lines->search( { - itemnumber => $itemnumber, - accounttype => { -in => [ 'L', 'W' ] }, + itemnumber => $itemnumber, + debit_type_code => 'LOST', + status => [ undef, { '<>' => 'RETURNED' } ] }, { order_by => { -desc => [ 'date', 'accountlines_id' ] } @@ -2449,22 +2483,19 @@ sub _FixAccountForLostAndReturned { $credit = $account->add_credit( { amount => $credit_total, description => 'Item Returned ' . $item_id, - type => 'lost_item_return', + type => 'LOST_RETURN', interface => C4::Context->interface, library_id => $branchcode } ); - # TODO: ->apply should just accept the accountline - $credit->apply( { debits => $accountlines->reset } ); + $credit->apply( { debits => [ $accountline ] } ); } - # Manually set the accounttype - $accountline->discard_changes->accounttype('LR'); + # Update the account status + $accountline->discard_changes->status('RETURNED'); $accountline->store; - ModItem( { paidfor => '' }, undef, $itemnumber, { log_action => 0 } ); - if ( defined $account and C4::Context->preference('AccountAutoReconcile') ) { $account->reconcile_balance; } @@ -2766,7 +2797,10 @@ sub CanBookBeRenewed { if ( C4::Context->preference('OPACFineNoRenewalsBlockAutoRenew') ) { my $fine_no_renewals = C4::Context->preference("OPACFineNoRenewals"); - my $amountoutstanding = $patron->account->balance; + 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" ); } @@ -2862,115 +2896,118 @@ sub AddRenewal { my $circ_library = Koha::Libraries->find( _GetCircControlBranch($item_unblessed, $patron_unblessed) ); - if ( C4::Context->preference('CalculateFinesOnReturn') && $issue->is_overdue ) { - _CalculateAndUpdateFine( { issue => $issue, item => $item_unblessed, borrower => $patron_unblessed } ); - } - _FixOverduesOnReturn( $borrowernumber, $itemnumber ); - - # If the due date wasn't specified, calculate it by adding the - # book's loan length to today's date or the current due date - # based on the value of the RenewalPeriodBase syspref. - my $itemtype = $item_object->effective_itemtype; - unless ($datedue) { - - $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ? - dt_from_string( $issue->date_due, 'sql' ) : - DateTime->now( time_zone => C4::Context->tz()); - $datedue = CalcDateDue($datedue, $itemtype, $circ_library->branchcode, $patron_unblessed, 'is a renewal'); - } + my $schema = Koha::Database->schema; + $schema->txn_do(sub{ - my $fees = Koha::Charges::Fees->new( - { - patron => $patron, - library => $circ_library, - item => $item_object, - from_date => dt_from_string( $issue->date_due, 'sql' ), - to_date => dt_from_string($datedue), + if ( C4::Context->preference('CalculateFinesOnReturn') ) { + _CalculateAndUpdateFine( { issue => $issue, item => $item_unblessed, borrower => $patron_unblessed } ); + } + _FixOverduesOnReturn( $borrowernumber, $itemnumber, undef, 'RENEWED' ); + + # If the due date wasn't specified, calculate it by adding the + # book's loan length to today's date or the current due date + # based on the value of the RenewalPeriodBase syspref. + my $itemtype = $item_object->effective_itemtype; + unless ($datedue) { + + $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ? + dt_from_string( $issue->date_due, 'sql' ) : + DateTime->now( time_zone => C4::Context->tz()); + $datedue = CalcDateDue($datedue, $itemtype, $circ_library->branchcode, $patron_unblessed, 'is a renewal'); } - ); - # 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 + 1; - my $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ? - WHERE borrowernumber=? - AND itemnumber=?" - ); + my $fees = Koha::Charges::Fees->new( + { + patron => $patron, + library => $circ_library, + item => $item_object, + from_date => dt_from_string( $issue->date_due, 'sql' ), + to_date => dt_from_string($datedue), + } + ); - $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $lastreneweddate, $borrowernumber, $itemnumber ); + # 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 = ? + WHERE borrowernumber=? + AND itemnumber=?" + ); - # Update the renewal count on the item, and tell zebra to reindex - $renews = $item_object->renewals + 1; - ModItem( { renewals => $renews, onloan => $datedue->strftime('%Y-%m-%d %H:%M')}, $item_object->biblionumber, $itemnumber, { log_action => 0 } ); + $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $lastreneweddate, $borrowernumber, $itemnumber ); - # Charge a new rental fee, if applicable - my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber ); - if ( $charge > 0 ) { - my $description = "Renewal of Rental Item " . $biblio->title . " " .$item_object->barcode; - AddIssuingCharge($issue, $charge, $description); - } + # Update the renewal count on the item, and tell zebra to reindex + $renews = ( $item_object->renewals || 0 ) + 1; + ModItem( { renewals => $renews, onloan => $datedue->strftime('%Y-%m-%d %H:%M')}, $item_object->biblionumber, $itemnumber, { log_action => 0 } ); - # Charge a new accumulate rental fee, if applicable - my $itemtype_object = Koha::ItemTypes->find( $itemtype ); - if ( $itemtype_object ) { - my $accumulate_charge = $fees->accumulate_rentalcharge(); - if ( $accumulate_charge > 0 ) { - my $type_desc = "Renewal of Daily Rental Item " . $biblio->title . " $item_unblessed->{'barcode'}"; - AddIssuingCharge( $issue, $accumulate_charge, $type_desc ) + # Charge a new rental fee, if applicable + my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber ); + if ( $charge > 0 ) { + AddIssuingCharge($issue, $charge, 'RENT_RENEW'); } - $charge += $accumulate_charge; - } - # Send a renewal slip according to checkout alert preferencei - if ( C4::Context->preference('RenewalSendNotice') eq '1' ) { - my $circulation_alert = 'C4::ItemCirculationAlertPreference'; - my %conditions = ( - branchcode => $branch, - categorycode => $patron->categorycode, - item_type => $itemtype, - notification => 'CHECKOUT', - ); - if ( $circulation_alert->is_enabled_for( \%conditions ) ) { - SendCirculationAlert( - { - type => 'RENEWAL', - item => $item_unblessed, - borrower => $patron->unblessed, - branch => $branch, - } - ); + # Charge a new accumulate rental fee, if applicable + my $itemtype_object = Koha::ItemTypes->find( $itemtype ); + if ( $itemtype_object ) { + my $accumulate_charge = $fees->accumulate_rentalcharge(); + if ( $accumulate_charge > 0 ) { + AddIssuingCharge( $issue, $accumulate_charge, 'RENT_DAILY_RENEW' ) + } + $charge += $accumulate_charge; } - } - # Remove any OVERDUES related debarment if the borrower has no overdues - if ( $patron - && $patron->is_debarred - && ! $patron->has_overdues - && @{ GetDebarments({ borrowernumber => $borrowernumber, type => 'OVERDUES' }) } - ) { - DelUniqueDebarment({ borrowernumber => $borrowernumber, type => 'OVERDUES' }); - } + # Send a renewal slip according to checkout alert preferencei + if ( C4::Context->preference('RenewalSendNotice') eq '1' ) { + my $circulation_alert = 'C4::ItemCirculationAlertPreference'; + my %conditions = ( + branchcode => $branch, + categorycode => $patron->categorycode, + item_type => $itemtype, + notification => 'CHECKOUT', + ); + if ( $circulation_alert->is_enabled_for( \%conditions ) ) { + SendCirculationAlert( + { + type => 'RENEWAL', + item => $item_unblessed, + borrower => $patron->unblessed, + branch => $branch, + } + ); + } + } - unless ( C4::Context->interface eq 'opac' ) { #if from opac we are obeying OpacRenewalBranch as calculated in opac-renew.pl - $branch = C4::Context->userenv ? C4::Context->userenv->{branch} : $branch; - } + # Remove any OVERDUES related debarment if the borrower has no overdues + if ( $patron + && $patron->is_debarred + && ! $patron->has_overdues + && @{ GetDebarments({ borrowernumber => $borrowernumber, type => 'OVERDUES' }) } + ) { + DelUniqueDebarment({ borrowernumber => $borrowernumber, type => 'OVERDUES' }); + } - # Add the renewal to stats - UpdateStats( - { - branch => $branch, - type => 'renew', - amount => $charge, - itemnumber => $itemnumber, - itemtype => $itemtype, - location => $item_object->location, - borrowernumber => $borrowernumber, - ccode => $item_object->ccode, + unless ( C4::Context->interface eq 'opac' ) { #if from opac we are obeying OpacRenewalBranch as calculated in opac-renew.pl + $branch = C4::Context->userenv ? C4::Context->userenv->{branch} : $branch; } - ); - #Log the renewal - logaction("CIRCULATION", "RENEWAL", $borrowernumber, $itemnumber) if C4::Context->preference("RenewalLog"); + # Add the renewal to stats + UpdateStats( + { + branch => $branch, + type => 'renew', + amount => $charge, + itemnumber => $itemnumber, + itemtype => $itemtype, + location => $item_object->location, + borrowernumber => $borrowernumber, + ccode => $item_object->ccode, + } + ); + + #Log the renewal + logaction("CIRCULATION", "RENEWAL", $borrowernumber, $itemnumber) if C4::Context->preference("RenewalLog"); + }); + return $datedue; } @@ -3238,12 +3275,12 @@ sub _get_discount_from_rule { =head2 AddIssuingCharge - &AddIssuingCharge( $checkout, $charge, [$description] ) + &AddIssuingCharge( $checkout, $charge, $type ) =cut sub AddIssuingCharge { - my ( $checkout, $charge, $description ) = @_; + my ( $checkout, $charge, $type ) = @_; # FIXME What if checkout does not exist? @@ -3251,12 +3288,11 @@ sub AddIssuingCharge { my $accountline = $account->add_debit( { amount => $charge, - description => $description, note => undef, user_id => C4::Context->userenv ? C4::Context->userenv->{'number'} : undef, library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef, interface => C4::Context->interface, - type => 'rent', + type => $type, item_id => $checkout->itemnumber, issue_id => $checkout->issue_id, } @@ -3571,7 +3607,7 @@ sub CalcDateDue { my $calendar = Koha::Calendar->new( branchcode => $branch ); if ( $calendar->is_holiday($datedue) ) { # Don't return on a closed day - $datedue = $calendar->prev_open_day( $datedue ); + $datedue = $calendar->prev_open_days( $datedue, 1 ); } } } @@ -3669,15 +3705,7 @@ sub DeleteBranchTransferLimits { sub ReturnLostItem{ my ( $borrowernumber, $itemnum ) = @_; - MarkIssueReturned( $borrowernumber, $itemnum ); - my $patron = Koha::Patrons->find( $borrowernumber ); - my $item = Koha::Items->find($itemnum); - my $old_note = ($item->paidfor && ($item->paidfor ne q{})) ? $item->paidfor.' / ' : q{}; - my @datearr = localtime(time); - my $date = ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3]; - my $bor = $patron->firstname . ' ' . $patron->surname . ' ' . $patron->cardnumber; - ModItem({ paidfor => $old_note."Paid for by $bor $date" }, undef, $itemnum); } @@ -3710,7 +3738,7 @@ sub LostItem{ if ( my $borrowernumber = $issues->{borrowernumber} ){ my $patron = Koha::Patrons->find( $borrowernumber ); - my $fix = _FixOverduesOnReturn($borrowernumber, $itemnumber, C4::Context->preference('WhenLostForgiveFine'), 0); # 1, 0 = exemptfine, no-dropbox + my $fix = _FixOverduesOnReturn($borrowernumber, $itemnumber, C4::Context->preference('WhenLostForgiveFine'), 'LOST'); defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $itemnumber...) failed!"; # zero is OK, check defined if (C4::Context->preference('WhenLostChargeReplacementFee')){