X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FCirculation.pm;h=30cbe2fa85197083d98221d0eeb76d8c068bc15a;hb=da0337b374c5824c0f9edcb3e8545c812644ff63;hp=71fe20f835fd8c35a1cf4a2c34d6f444d42b146f;hpb=ab59c080359170b156eceb101b7cf029e96d4e09;p=koha.git diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 71fe20f835..30cbe2fa85 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -36,7 +36,11 @@ use C4::Message; use C4::Debug; use C4::Branch; # GetBranches use C4::Log; # logaction -use C4::Koha qw(GetAuthorisedValueByCode); +use C4::Koha qw( + GetAuthorisedValueByCode + GetAuthValCode + GetKohaAuthorisedValueLib +); use C4::Overdues qw(CalcFine UpdateFine); use Algorithm::CheckDigits; @@ -772,29 +776,32 @@ sub CanBookBeIssued { # # DEBTS - my ($amount) = - C4::Members::GetMemberAccountRecords( $borrower->{'borrowernumber'}, '' && $duedate->ymd() ); + my ($balance, $non_issue_charges, $other_charges) = + C4::Members::GetMemberAccountBalance( $borrower->{'borrowernumber'} ); my $amountlimit = C4::Context->preference("noissuescharge"); my $allowfineoverride = C4::Context->preference("AllowFineOverride"); my $allfinesneedoverride = C4::Context->preference("AllFinesNeedOverride"); if ( C4::Context->preference("IssuingInProcess") ) { - if ( $amount > $amountlimit && !$inprocess && !$allowfineoverride) { - $issuingimpossible{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $amount > $amountlimit && !$inprocess && $allowfineoverride) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $allfinesneedoverride && $amount > 0 && $amount <= $amountlimit && !$inprocess ) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); + if ( $non_issue_charges > $amountlimit && !$inprocess && !$allowfineoverride) { + $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_charges ); + } elsif ( $non_issue_charges > $amountlimit && !$inprocess && $allowfineoverride) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges ); + } elsif ( $allfinesneedoverride && $non_issue_charges > 0 && $non_issue_charges <= $amountlimit && !$inprocess ) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges ); } } else { - if ( $amount > $amountlimit && $allowfineoverride ) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $amount > $amountlimit && !$allowfineoverride) { - $issuingimpossible{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $amount > 0 && $allfinesneedoverride ) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); + if ( $non_issue_charges > $amountlimit && $allowfineoverride ) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges ); + } elsif ( $non_issue_charges > $amountlimit && !$allowfineoverride) { + $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_charges ); + } elsif ( $non_issue_charges > 0 && $allfinesneedoverride ) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges ); } } + if ($balance > 0 && $other_charges > 0) { + $alerts{OTHER_CHARGES} = sprintf( "%.2f", $other_charges ); + } my ($blocktype, $count) = C4::Members::IsMemberBlocked($borrower->{'borrowernumber'}); if ($blocktype == -1) { @@ -828,16 +835,17 @@ sub CanBookBeIssued { # # ITEM CHECKING # - if ( $item->{'notforloan'} - && $item->{'notforloan'} > 0 ) + if ( $item->{'notforloan'} ) { if(!C4::Context->preference("AllowNotForLoanOverride")){ $issuingimpossible{NOT_FOR_LOAN} = 1; + $issuingimpossible{item_notforloan} = $item->{'notforloan'}; }else{ $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1; + $needsconfirmation{item_notforloan} = $item->{'notforloan'}; } } - elsif ( !$item->{'notforloan'} ){ + else { # we have to check itemtypes.notforloan also if (C4::Context->preference('item-level_itypes')){ # this should probably be a subroutine @@ -848,16 +856,20 @@ sub CanBookBeIssued { if ($notforloan->{'notforloan'}) { if (!C4::Context->preference("AllowNotForLoanOverride")) { $issuingimpossible{NOT_FOR_LOAN} = 1; + $issuingimpossible{itemtype_notforloan} = $item->{'itype'}; } else { $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1; + $needsconfirmation{itemtype_notforloan} = $item->{'itype'}; } } } elsif ($biblioitem->{'notforloan'} == 1){ if (!C4::Context->preference("AllowNotForLoanOverride")) { $issuingimpossible{NOT_FOR_LOAN} = 1; + $issuingimpossible{itemtype_notforloan} = $biblioitem->{'itemtype'}; } else { $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1; + $needsconfirmation{itemtype_notforloan} = $biblioitem->{'itemtype'}; } } } @@ -875,7 +887,7 @@ sub CanBookBeIssued { $needsconfirmation{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'confirm' ); $alerts{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'alert' ); } - if ( C4::Context->preference("IndependantBranches") ) { + if ( C4::Context->preference("IndependentBranches") ) { my $userenv = C4::Context->userenv; if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) { $issuingimpossible{ITEMNOTSAMEBRANCH} = 1 @@ -1305,7 +1317,7 @@ sub AddIssue { } } - logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'biblionumber'}) + logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'itemnumber'}) if C4::Context->preference("IssueLog"); } return ($datedue); # not necessarily the same as when it came in! @@ -1322,15 +1334,20 @@ Get loan length for an itemtype, a borrower type and a branch sub GetLoanLength { my ( $borrowertype, $itemtype, $branchcode ) = @_; my $dbh = C4::Context->dbh; - my $sth = - $dbh->prepare( -'select issuelength, lengthunit from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null' - ); -# warn "in get loan lenght $borrowertype $itemtype $branchcode "; -# try to find issuelength & return the 1st available. -# check with borrowertype, itemtype and branchcode, then without one of those parameters + my $sth = $dbh->prepare(qq{ + SELECT issuelength, lengthunit, renewalperiod + FROM issuingrules + WHERE categorycode=? + AND itemtype=? + AND branchcode=? + AND issuelength IS NOT NULL + }); + + # try to find issuelength & return the 1st available. + # check with borrowertype, itemtype and branchcode, then without one of those parameters $sth->execute( $borrowertype, $itemtype, $branchcode ); my $loanlength = $sth->fetchrow_hashref; + return $loanlength if defined($loanlength) && $loanlength->{issuelength}; @@ -1372,6 +1389,7 @@ sub GetLoanLength { # if no rule is set => 21 days (hardcoded) return { issuelength => 21, + renewalperiod => 21, lengthunit => 'days', }; @@ -1406,7 +1424,7 @@ sub GetHardDueDate { FIXME - This is a copy-paste of GetLoanLength as a stop-gap. Do not wish to change API for GetLoanLength -this close to release, however, Overdues::GetIssuingRules is broken. +this close to release. Get the issuing rule for an itemtype, a borrower type and a branch Returns a hashref from the issuingrules table. @@ -1767,19 +1785,36 @@ sub AddReturn { } if ($borrowernumber) { - if($issue->{'overdue'}){ - my ( $amount, $type, $unitcounttotal ) = C4::Overdues::CalcFine( $item, $borrower->{categorycode},$branch, $datedue, $today ); + if( C4::Context->preference('CalculateFinesOnReturn') && $issue->{'overdue'}){ + # we only need to calculate and change the fines if we want to do that on return + # Should be on for hourly loans + my $control = C4::Context->preference('CircControl'); + my $control_branchcode = + ( $control eq 'ItemHomeLibrary' ) ? $item->{homebranch} + : ( $control eq 'PatronLibrary' ) ? $borrower->{branchcode} + : $issue->{branchcode}; + + my ( $amount, $type, $unitcounttotal ) = + C4::Overdues::CalcFine( $item, $borrower->{categorycode}, + $control_branchcode, $datedue, $today ); + $type ||= q{}; - if ( $amount > 0 && ( C4::Context->preference('finesMode') eq 'production' )) { - C4::Overdues::UpdateFine( - $issue->{itemnumber}, - $issue->{borrowernumber}, - $amount, $type, output_pref($datedue) - ); - } + + if ( $amount > 0 + && C4::Context->preference('finesMode') eq 'production' ) + { + C4::Overdues::UpdateFine( $issue->{itemnumber}, + $issue->{borrowernumber}, + $amount, $type, output_pref($datedue) ); + } } - MarkIssueReturned($borrowernumber, $item->{'itemnumber'}, $circControlBranch, '', $borrower->{'privacy'}); - $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right? This could be the borrower hash. + + MarkIssueReturned( $borrowernumber, $item->{'itemnumber'}, + $circControlBranch, '', $borrower->{'privacy'} ); + + # FIXME is the "= 1" right? This could be the borrower hash. + $messages->{'WasReturned'} = 1; + } ModItem({ onloan => undef }, $issue->{'biblionumber'}, $item->{'itemnumber'}); @@ -1873,7 +1908,7 @@ sub AddReturn { }); } - logaction("CIRCULATION", "RETURN", $borrowernumber, $item->{'biblionumber'}) + logaction("CIRCULATION", "RETURN", $borrowernumber, $item->{'itemnumber'}) if C4::Context->preference("ReturnLog"); # FIXME: make this comment intelligible. @@ -1949,6 +1984,7 @@ sub MarkIssueReturned { if ( $privacy == 2) { # The default of 0 does not work due to foreign key constraints # The anonymisation will fail quietly if AnonymousPatron is not a valid entry + # FIXME the above is unacceptable - bug 9942 relates my $anonymouspatron = (C4::Context->preference('AnonymousPatron')) ? C4::Context->preference('AnonymousPatron') : 0; my $sth_ano = $dbh->prepare("UPDATE old_issues SET borrowernumber=? WHERE borrowernumber = ? @@ -2153,7 +2189,7 @@ sub _FixAccountForLostAndReturned { # FIXME: move prepares outside while loop! my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ? WHERE (accountlines_id = ?)"); - $usth->execute($newamtos,'$thisacct'); # FIXME: '$thisacct' is a string literal! + $usth->execute($newamtos,$thisacct); $usth = $dbh->prepare("INSERT INTO accountoffsets (borrowernumber, accountno, offsetaccount, offsetamount) VALUES @@ -2237,7 +2273,7 @@ sub GetItemIssue { my ($itemnumber) = @_; return unless $itemnumber; my $sth = C4::Context->dbh->prepare( - "SELECT * + "SELECT items.*, issues.* FROM issues LEFT JOIN items ON issues.itemnumber=items.itemnumber WHERE issues.itemnumber=?"); @@ -2403,8 +2439,6 @@ END_SQL Find out whether a borrowed item may be renewed. -C<$dbh> is a DBI handle to the Koha database. - C<$borrowernumber> is the borrower number of the patron who currently has the item on loan. @@ -2414,7 +2448,7 @@ C<$override_limit>, if supplied with a true value, causes the limit on the number of times that the loan can be renewed (as controlled by the item type) to be ignored. -C<$CanBookBeRenewed> returns a true value iff the item may be renewed. The +C<$CanBookBeRenewed> returns a true value if the item may be renewed. The item must currently be on loan to the specified borrower; renewals must be allowed for the item's type; and the borrower must not have already renewed the loan. $error will contain the reason the renewal can not proceed @@ -2428,65 +2462,29 @@ sub CanBookBeRenewed { my $dbh = C4::Context->dbh; my $renews = 1; my $renewokay = 0; - my $error; + my $error; - # Look in the issues table for this item, lent to this borrower, - # and not yet returned. + my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return; + my $item = GetItem($itemnumber) or return; + my $itemissue = GetItemIssue($itemnumber) or return; - # Look in the issues table for this item, lent to this borrower, - # and not yet returned. - my %branch = ( - 'ItemHomeLibrary' => 'items.homebranch', - 'PickupLibrary' => 'items.holdingbranch', - 'PatronLibrary' => 'borrowers.branchcode' - ); - my $controlbranch = $branch{C4::Context->preference('CircControl')}; - my $itype = C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype'; - - my $sthcount = $dbh->prepare(" - SELECT - borrowers.categorycode, biblioitems.itemtype, issues.renewals, renewalsallowed, $controlbranch - FROM issuingrules, - issues - LEFT JOIN items USING (itemnumber) - LEFT JOIN borrowers USING (borrowernumber) - LEFT JOIN biblioitems USING (biblioitemnumber) - - WHERE - (issuingrules.categorycode = borrowers.categorycode OR issuingrules.categorycode = '*') - AND - (issuingrules.itemtype = $itype OR issuingrules.itemtype = '*') - AND - (issuingrules.branchcode = $controlbranch OR issuingrules.branchcode = '*') - AND - borrowernumber = ? - AND - itemnumber = ? - ORDER BY - issuingrules.categorycode desc, - issuingrules.itemtype desc, - issuingrules.branchcode desc - LIMIT 1; - "); - - $sthcount->execute( $borrowernumber, $itemnumber ); - if ( my $data1 = $sthcount->fetchrow_hashref ) { - - if ( ( $data1->{renewalsallowed} && $data1->{renewalsallowed} > $data1->{renewals} ) || $override_limit ) { - $renewokay = 1; - } - else { - $error="too_many"; - } - - my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber); - if ($resfound) { - $renewokay = 0; - $error="on_reserve" - } + my $branchcode = _GetCircControlBranch($item, $borrower); + + my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode); + + if ( ( $issuingrule->{renewalsallowed} > $itemissue->{renewals} ) || $override_limit ) { + $renewokay = 1; + } else { + $error = "too_many"; + } + my $resstatus = C4::Reserves::GetReserveStatus($itemnumber); + if ( $resstatus eq "Waiting" or $resstatus eq "Reserved" ) { + $renewokay = 0; + $error = "on_reserve"; } - return ($renewokay,$error); + + return ( $renewokay, $error ); } =head2 AddRenewal @@ -2545,9 +2543,9 @@ sub AddRenewal { my $itemtype = (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'}; $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ? - $issuedata->{date_due} : + dt_from_string( $issuedata->{date_due} ) : DateTime->now( time_zone => C4::Context->tz()); - $datedue = CalcDateDue($datedue,$itemtype,$issuedata->{'branchcode'},$borrower); + $datedue = CalcDateDue($datedue, $itemtype, $issuedata->{'branchcode'}, $borrower, 'is a renewal'); } # Update the issues record to have the new due date, and a new count @@ -2841,7 +2839,7 @@ sub DeleteTransfer { =head2 AnonymiseIssueHistory - $rows = AnonymiseIssueHistory($date,$borrowernumber) + ($rows,$err_history_not_deleted) = AnonymiseIssueHistory($date,$borrowernumber) This function write NULL instead of C<$borrowernumber> given on input arg into the table issues. if C<$borrowernumber> is not set, it will delete the issue history for all borrower older than C<$date>. @@ -2849,7 +2847,7 @@ if C<$borrowernumber> is not set, it will delete the issue history for all borro If c<$borrowernumber> is set, it will delete issue history for only that borrower, regardless of their opac privacy setting (force delete). -return the number of affected rows. +return the number of affected rows and a value that evaluates to true if an error occurred deleting the history. =cut @@ -2876,8 +2874,9 @@ sub AnonymiseIssueHistory { } my $sth = $dbh->prepare($query); $sth->execute(@bind_params); + my $anonymisation_err = $dbh->err; my $rows_affected = $sth->rows; ### doublecheck row count return function - return $rows_affected; + return ($rows_affected, $anonymisation_err); } =head2 SendCirculationAlert @@ -3012,62 +3011,60 @@ C<$startdate> = C4::Dates object representing start date of loan period (assum C<$itemtype> = itemtype code of item in question C<$branch> = location whose calendar to use C<$borrower> = Borrower object +C<$isrenewal> = Boolean: is true if we want to calculate the date due for a renewal. Else is false. =cut sub CalcDateDue { - my ( $startdate, $itemtype, $branch, $borrower ) = @_; + my ( $startdate, $itemtype, $branch, $borrower, $isrenewal ) = @_; + + $isrenewal ||= 0; # loanlength now a href my $loanlength = - GetLoanLength( $borrower->{'categorycode'}, $itemtype, $branch ); + GetLoanLength( $borrower->{'categorycode'}, $itemtype, $branch ); - my $datedue; + my $length_key = ( $isrenewal and defined $loanlength->{renewalperiod} ) + ? qq{renewalperiod} + : qq{issuelength}; - # if globalDueDate ON the datedue is set to that date - if (C4::Context->preference('globalDueDate') - && ( C4::Context->preference('globalDueDate') =~ - C4::Dates->regexp('syspref') ) - ) { - $datedue = dt_from_string( - C4::Context->preference('globalDueDate'), - C4::Context->preference('dateformat') - ); + my $datedue; + if ( $startdate ) { + if (ref $startdate ne 'DateTime' ) { + $datedue = dt_from_string($datedue); + } else { + $datedue = $startdate->clone; + } } else { + $datedue = + DateTime->now( time_zone => C4::Context->tz() ) + ->truncate( to => 'minute' ); + } - # otherwise, calculate the datedue as normal - if ( C4::Context->preference('useDaysMode') eq 'Days' ) - { # ignoring calendar - my $dt = - DateTime->now( time_zone => C4::Context->tz() ) - ->truncate( to => 'minute' ); - if ( $loanlength->{lengthunit} eq 'hours' ) { - $dt->add( hours => $loanlength->{issuelength} ); - } else { # days - $dt->add( days => $loanlength->{issuelength} ); - $dt->set_hour(23); - $dt->set_minute(59); - } - # break - return $dt; - } else { - my $dur; - if ($loanlength->{lengthunit} eq 'hours') { - $dur = DateTime::Duration->new( hours => $loanlength->{issuelength}); - } - else { # days - $dur = DateTime::Duration->new( days => $loanlength->{issuelength}); - } - if (ref $startdate ne 'DateTime' ) { - $startdate = dt_from_string($startdate); - } - my $calendar = Koha::Calendar->new( branchcode => $branch ); - $datedue = $calendar->addDate( $startdate, $dur, $loanlength->{lengthunit} ); - if ($loanlength->{lengthunit} eq 'days') { - $datedue->set_hour(23); - $datedue->set_minute(59); - } + # calculate the datedue as normal + if ( C4::Context->preference('useDaysMode') eq 'Days' ) + { # ignoring calendar + if ( $loanlength->{lengthunit} eq 'hours' ) { + $datedue->add( hours => $loanlength->{$length_key} ); + } else { # days + $datedue->add( days => $loanlength->{$length_key} ); + $datedue->set_hour(23); + $datedue->set_minute(59); + } + } else { + my $dur; + if ($loanlength->{lengthunit} eq 'hours') { + $dur = DateTime::Duration->new( hours => $loanlength->{$length_key}); + } + else { # days + $dur = DateTime::Duration->new( days => $loanlength->{$length_key}); + } + my $calendar = Koha::Calendar->new( branchcode => $branch ); + $datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} ); + if ($loanlength->{lengthunit} eq 'days') { + $datedue->set_hour(23); + $datedue->set_minute(59); } } @@ -3088,6 +3085,7 @@ sub CalcDateDue { } # in all other cases, keep the date due as it is + } # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate @@ -3326,9 +3324,10 @@ sub GetOfflineOperation { } sub AddOfflineOperation { + my ( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount ) = @_; my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber) VALUES(?,?,?,?,?,?)"); - $sth->execute( @_ ); + my $sth = $dbh->prepare("INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber, amount) VALUES(?,?,?,?,?,?,?)"); + $sth->execute( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount ); return "Added."; } @@ -3347,6 +3346,8 @@ sub ProcessOfflineOperation { $report = ProcessOfflineReturn( $operation ); } elsif ( $operation->{action} eq 'issue' ) { $report = ProcessOfflineIssue( $operation ); + } elsif ( $operation->{action} eq 'payment' ) { + $report = ProcessOfflinePayment( $operation ); } DeleteOfflineOperation( $operation->{operationid} ) if $operation->{operationid}; @@ -3416,6 +3417,16 @@ sub ProcessOfflineIssue { } } +sub ProcessOfflinePayment { + my $operation = shift; + + my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber + my $amount = $operation->{amount}; + + recordpayment( $borrower->{borrowernumber}, $amount ); + + return "Success." +} =head2 TransferSlip