X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FCirculation.pm;h=9ad4ff614cebab3278de25358822e29b692d443e;hb=7794cf2eb36662ce11e96cf87e7a87396df538f3;hp=9a65c34eb06be7a169150c2dc8feeea395c756de;hpb=620ead4445ff873a12d7fcb42e780595ab060dba;p=koha.git diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 9a65c34eb0..9ad4ff614c 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -109,7 +109,7 @@ Also deals with stocktaking. =head2 barcodedecode -=head3 $str = &barcodedecode($barcode); +=head3 $str = &barcodedecode($barcode, [$filter]); =over 4 @@ -120,6 +120,10 @@ to circulation.pl that differs from the barcode stored for the item. For proper functioning of this filter, calling the function on the correct barcode string (items.barcode) should return an unaltered barcode. +The optional $filter argument is to allow for testing or explicit +behavior that ignores the System Pref. Valid values are the same as the +System Pref options. + =back =cut @@ -128,31 +132,27 @@ correct barcode string (items.barcode) should return an unaltered barcode. # FIXME -- these plugins should be moved out of Circulation.pm # sub barcodedecode { - my ($barcode) = @_; - my $filter = C4::Context->preference('itemBarcodeInputFilter'); - if($filter eq 'whitespace') { + my ($barcode, $filter) = @_; + $filter = C4::Context->preference('itemBarcodeInputFilter') unless $filter; + $filter or return $barcode; # ensure filter is defined, else return untouched barcode + if ($filter eq 'whitespace') { $barcode =~ s/\s//g; - return $barcode; - } elsif($filter eq 'cuecat') { + } elsif ($filter eq 'cuecat') { chomp($barcode); my @fields = split( /\./, $barcode ); my @results = map( decode($_), @fields[ 1 .. $#fields ] ); - if ( $#results == 2 ) { - return $results[2]; - } - else { - return $barcode; - } - } elsif($filter eq 'T-prefix') { - if ( $barcode =~ /^[Tt]/) { - if (substr($barcode,1,1) eq '0') { - return $barcode; - } else { - $barcode = substr($barcode,2) + 0 ; - } + ($#results == 2) and return $results[2]; + } elsif ($filter eq 'T-prefix') { + if ($barcode =~ /^[Tt](\d)/) { + (defined($1) and $1 eq '0') and return $barcode; + $barcode = substr($barcode, 2) + 0; # FIXME: probably should be substr($barcode, 1) } - return sprintf( "T%07d",$barcode); + return sprintf("T%07d", $barcode); + # FIXME: $barcode could be "T1", causing warning: substr outside of string + # Why drop the nonzero digit after the T? + # Why pass non-digits (or empty string) to "T%07d"? } + return $barcode; # return barcode, modified or not } =head2 decode @@ -164,6 +164,9 @@ sub barcodedecode { =item Decodes a segment of a string emitted by a CueCat barcode scanner and returns it. +FIXME: Should be replaced with Barcode::Cuecat from CPAN +or Javascript based decoding on the client side. + =back =cut @@ -176,7 +179,7 @@ sub decode { my $l = ( $#s + 1 ) % 4; if ($l) { if ( $l == 1 ) { - warn "Error!"; + # warn "Error: Cuecat decode parsing failed!"; return; } $l = 4 - $l; @@ -645,10 +648,28 @@ sub CanBookBeIssued { $item->{'itemtype'}=$item->{'itype'}; my $dbh = C4::Context->dbh; + # MANDATORY CHECKS - unless item exists, nothing else matters + unless ( $item->{barcode} ) { + $issuingimpossible{UNKNOWN_BARCODE} = 1; + } + return ( \%issuingimpossible, \%needsconfirmation ) if %issuingimpossible; + # # DUE DATE is OK ? -- should already have checked. # - #$issuingimpossible{INVALID_DATE} = 1 unless ($duedate); + unless ( $duedate ) { + my $issuedate = strftime( "%Y-%m-%d", localtime ); + my $branch = (C4::Context->preference('CircControl') eq 'PickupLibrary') ? C4::Context->userenv->{'branch'} : + (C4::Context->preference('CircControl') eq 'PatronLibrary') ? $borrower->{'branchcode'} : + $item->{'homebranch'}; # fallback to item's homebranch + my $itype = ( C4::Context->preference('item-level_itypes') ) ? $item->{'itype'} : $biblioitem->{'itemtype'}; + my $loanlength = GetLoanLength( $borrower->{'categorycode'}, $itype, $branch ); + $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower ); + + # Offline circ calls AddIssue directly, doesn't run through here + # So issuingimpossible should be ok. + } + $issuingimpossible{INVALID_DATE} = $duedate->output('syspref') unless ( $duedate && $duedate->output('iso') ge C4::Dates->today('iso') ); # # BORROWER STATUS @@ -688,7 +709,7 @@ sub CanBookBeIssued { if ( $amount > $amountlimit && !$inprocess ) { $issuingimpossible{DEBT} = sprintf( "%.2f", $amount ); } - elsif ( $amount <= $amountlimit && !$inprocess ) { + elsif ( $amount > 0 && $amount <= $amountlimit && !$inprocess ) { $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); } } @@ -698,11 +719,25 @@ sub CanBookBeIssued { } } + my ($blocktype, $count) = C4::Members::IsMemberBlocked($borrower->{'borrowernumber'}); + if($blocktype == -1){ + ## remaining overdue documents + $needsconfirmation{USERBLOCKEDREMAINING} = $count; + }elsif($blocktype == 1){ + ## blocked because of overdue return + $issuingimpossible{USERBLOCKEDOVERDUE} = $count; + } + # # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS # my $toomany = TooMany( $borrower, $item->{biblionumber}, $item ); - $needsconfirmation{TOO_MANY} = $toomany if $toomany; + # if TooMany return / 0, then the user has no permission to check out this book + if ($toomany =~ /\/ 0/) { + $needsconfirmation{PATRON_CANT} = 1; + } else { + $needsconfirmation{TOO_MANY} = $toomany if $toomany; + } # # ITEM CHECKING @@ -713,7 +748,11 @@ sub CanBookBeIssued { if ( $item->{'notforloan'} && $item->{'notforloan'} > 0 ) { - $issuingimpossible{NOT_FOR_LOAN} = 1; + if(C4::Context->preference("AllowNotForLoanOverride")){ + $issuingimpossible{NOT_FOR_LOAN_CAN_FORCE} = 1; + }else{ + $issuingimpossible{NOT_FOR_LOAN} = 1; + } } elsif ( !$item->{'notforloan'} ){ # we have to check itemtypes.notforloan also @@ -770,7 +809,7 @@ sub CanBookBeIssued { elsif ($issue->{borrowernumber}) { # issued to someone else - my $currborinfo = GetMemberDetails( $issue->{borrowernumber} ); + my $currborinfo = C4::Members::GetMemberDetails( $issue->{borrowernumber} ); # warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})"; $needsconfirmation{ISSUED_TO_ANOTHER} = @@ -797,12 +836,6 @@ sub CanBookBeIssued { "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})"; } } - if ( C4::Context->preference("LibraryName") eq "Horowhenua Library Trust" ) { - if ( $borrower->{'categorycode'} eq 'W' ) { - my %emptyhash; - return ( \%emptyhash, \%needsconfirmation ); - } - } return ( \%issuingimpossible, \%needsconfirmation ); } @@ -844,29 +877,22 @@ AddIssue does the following things : =cut sub AddIssue { - my ( $borrower, $barcode, $datedue, $cancelreserve, $issuedate ) = @_; + my ( $borrower, $barcode, $datedue, $cancelreserve, $issuedate, $sipmode) = @_; my $dbh = C4::Context->dbh; my $barcodecheck=CheckValidBarcode($barcode); # $issuedate defaults to today. if ( ! defined $issuedate ) { $issuedate = strftime( "%Y-%m-%d", localtime ); + # TODO: for hourly circ, this will need to be a C4::Dates object + # and all calls to AddIssue including issuedate will need to pass a Dates object. } if ($borrower and $barcode and $barcodecheck ne '0'){ # find which item we issue my $item = GetItem('', $barcode) or return undef; # if we don't get an Item, abort. - my $branch; - # Get which branchcode we need - if (C4::Context->preference('CircControl') eq 'PickupLibrary'){ - $branch = C4::Context->userenv->{'branch'}; - } - elsif (C4::Context->preference('CircControl') eq 'PatronLibrary'){ - $branch = $borrower->{'branchcode'}; - } - else { - # items home library - $branch = $item->{'homebranch'}; - } + my $branch = (C4::Context->preference('CircControl') eq 'PickupLibrary') ? C4::Context->userenv->{'branch'} : + (C4::Context->preference('CircControl') eq 'PatronLibrary') ? $borrower->{'branchcode'} : + $item->{'homebranch'}; # fallback to item's homebranch # get actual issuing if there is one my $actualissue = GetItemIssue( $item->{itemnumber}); @@ -903,28 +929,23 @@ sub AddIssue { if ($restype) { my $resbor = $res->{'borrowernumber'}; if ( $resbor eq $borrower->{'borrowernumber'} ) { - # The item is reserved by the current patron ModReserveFill($res); } elsif ( $restype eq "Waiting" ) { - # warn "Waiting"; # The item is on reserve and waiting, but has been # reserved by some other patron. } elsif ( $restype eq "Reserved" ) { - # warn "Reserved"; # The item is reserved by someone else. if ($cancelreserve) { # cancel reserves on this item - CancelReserve( 0, $res->{'itemnumber'}, - $res->{'borrowernumber'} ); + CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'}); } } if ($cancelreserve) { - CancelReserve( $res->{'biblionumber'}, 0, - $res->{'borrowernumber'} ); + CancelReserve($res->{'biblionumber'}, 0, $res->{'borrowernumber'}); } else { # set waiting reserve to first in reserve queue as book isn't waiting now @@ -939,8 +960,8 @@ sub AddIssue { # Starting process for transfer job (checking transfert and validate it if we have one) my ($datesent) = GetTransfers($item->{'itemnumber'}); if ($datesent) { - # updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for lisibility of this case (maybe for stats ....) - my $sth = + # updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for visibility of this case (maybe for stats ....) + my $sth = $dbh->prepare( "UPDATE branchtransfers SET datearrived = now(), @@ -948,8 +969,7 @@ sub AddIssue { comments = 'Forced branchtransfer' WHERE itemnumber= ? AND datearrived IS NULL" ); - $sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'}); - $sth->finish; + $sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'}); } # Record in the database the fact that the book was issued. @@ -1002,8 +1022,8 @@ sub AddIssue { # Record the fact that this book was issued. &UpdateStats( C4::Context->userenv->{'branch'}, - 'issue', $charge, - '', $item->{'itemnumber'}, + 'issue', $charge, + ($sipmode ? "SIP-$sipmode" : ''), $item->{'itemnumber'}, $item->{'itype'}, $borrower->{'borrowernumber'} ); } @@ -1307,6 +1327,7 @@ sub AddReturn { my $curr_iteminfo = GetItem($iteminformation->{'itemnumber'}); $iteminformation->{'homebranch'} = $curr_iteminfo->{'homebranch'}; $iteminformation->{'holdingbranch'} = $curr_iteminfo->{'holdingbranch'}; + $iteminformation->{'itemlost'} = $curr_iteminfo->{'itemlost'}; $doreturn = 0; } @@ -1338,40 +1359,27 @@ sub AddReturn { # case of a return of document (deal with issues and holdingbranch) if ($doreturn) { - my $circControlBranch; + my $circControlBranch = GetCirculationBranch($iteminformation,$borrower); if($dropbox) { # don't allow dropbox mode to create an invalid entry in issues (issuedate > returndate) FIXME: actually checks eq, not gt undef($dropbox) if ( $iteminformation->{'issuedate'} eq C4::Dates->today('iso') ); - if (C4::Context->preference('CircControl') eq 'ItemHomeBranch' ) { - $circControlBranch = $iteminformation->{homebranch}; - } elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary') { - $circControlBranch = $borrower->{branchcode}; - } else { # CircControl must be PickupLibrary. - $circControlBranch = $iteminformation->{holdingbranch}; - # FIXME - is this right ? are we sure that the holdingbranch is still the pickup branch? - } } MarkIssueReturned($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'},$circControlBranch); - $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right? - } - - # continue to deal with returns cases, but not only if we have an issue - - # the holdingbranch is updated if the document is returned in an other location . - if ( $iteminformation->{'holdingbranch'} ne C4::Context->userenv->{'branch'} ) { - UpdateHoldingbranch(C4::Context->userenv->{'branch'},$iteminformation->{'itemnumber'}); - # reload iteminformation holdingbranch with the userenv value - $iteminformation->{'holdingbranch'} = C4::Context->userenv->{'branch'}; - } - ModDateLastSeen( $iteminformation->{'itemnumber'} ); - ModItem({ onloan => undef }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'}); - - if ($iteminformation->{borrowernumber}){ - ($borrower) = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 ); - } - # fix up the accounts..... - if ( $iteminformation->{'itemlost'} ) { - $messages->{'WasLost'} = 1; + $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right? + # continue to deal with returns cases, but not only if we have an issue + + + # We update the holdingbranch from circControlBranch variable + UpdateHoldingbranch($circControlBranch,$iteminformation->{'itemnumber'}); + $iteminformation->{'holdingbranch'} = $circControlBranch; + + + ModDateLastSeen( $iteminformation->{'itemnumber'} ); + ModItem({ onloan => undef }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'}); + + if ($iteminformation->{borrowernumber}){ + ($borrower) = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 ); + } } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -1401,6 +1409,7 @@ sub AddReturn { # fix up the accounts..... if ($iteminformation->{'itemlost'}) { FixAccountForLostAndReturned($iteminformation, $borrower); + ModItem({ itemlost => '0' }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'}); $messages->{'WasLost'} = 1; } # fix up the overdues in accounts... @@ -1432,9 +1441,9 @@ sub AddReturn { #adding message if holdingbranch is non equal a userenv branch to return the document to homebranch #we check, if we don't have reserv or transfert for this document, if not, return it to homebranch . - if ( ($iteminformation->{'holdingbranch'} ne $iteminformation->{'homebranch'}) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) and ($reserveDone ne 1) ){ + if ($doreturn and ($branch ne $iteminformation->{$hbr}) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) and ($reserveDone ne 1) ){ if (C4::Context->preference("AutomaticItemReturn") == 1) { - ModItemTransfer($iteminformation->{'itemnumber'}, C4::Context->userenv->{'branch'}, $iteminformation->{'homebranch'}); + ModItemTransfer($iteminformation->{'itemnumber'}, C4::Context->userenv->{'branch'}, $iteminformation->{$hbr}); $messages->{'WasTransfered'} = 1; } else { @@ -1598,7 +1607,6 @@ sub FixAccountForLostAndReturned { WHERE (borrowernumber = ?) AND (itemnumber = ?) AND (accountno = ?) "); $usth->execute($data->{'borrowernumber'},$itm,$acctno); - $usth->finish; #check if any credit is left if so writeoff other accounts my $nextaccntno = getnextacctno($data->{'borrowernumber'}); if ($amountleft < 0){ @@ -1630,9 +1638,8 @@ sub FixAccountForLostAndReturned { VALUES (?,?,?,?)"); $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos); - $usth->finish; } - $msth->finish; + $msth->finish; # $msth might actually have data left } if ($amountleft > 0){ $amountleft*=-1; @@ -1642,62 +1649,70 @@ sub FixAccountForLostAndReturned { (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding) VALUES (?,?,now(),?,?,'CR',?)"); $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft); - $usth->finish; $usth = $dbh->prepare("INSERT INTO accountoffsets (borrowernumber, accountno, offsetaccount, offsetamount) VALUES (?,?,?,?)"); $usth->execute($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset); - $usth->finish; ModItem({ paidfor => '' }, undef, $itm); } $sth->finish; return; } -=head2 GetItemIssue +=head2 GetCirculationBranch -$issues = &GetItemIssue($itemnumber); + &GetCirculationBranch($iteminfos,$borrower); -Returns patrons currently having a book. nothing if item is not issued atm +Return the branchcode that must be used for an item circulation -C<$itemnumber> is the itemnumber +C<$iteminfos> is a hashref to iteminfo. Only {itemnumber} is used. -Returns an array of hashes +C<$borrower> is a hashref to borrower. Only {borrowernumber is used. -FIXME: Though the above says that this function returns nothing if the -item is not issued, this actually returns a hasref that looks like -this: - { - itemnumber => 1, - overdue => 1 +Internal function, called by AddReturn + +=cut + +sub GetCirculationBranch { + my ($iteminfos, $borrower) = @_; + my $circcontrol = C4::Context->preference('CircControl'); + + if($circcontrol eq 'ItemHomeLibrary'){ + return $iteminfos->{C4::Context->preference("HomeOrHoldingBranch")}; + }elsif($circcontrol eq 'PatronLibrary'){ + return $borrower->{branchcode}; + }elsif($circcontrol eq 'PickupLibrary'){ + return C4::Context->userenv->{'branch'}; } + +} + +=head2 GetItemIssue + +$issues = &GetItemIssue($itemnumber); + +Returns patron currently having a book, or undef if not checked out. + +C<$itemnumber> is the itemnumber +C<$issues> is an array of hashes. =cut sub GetItemIssue { - my ( $itemnumber) = @_; + my ($itemnumber) = @_; return unless $itemnumber; - my $dbh = C4::Context->dbh; - my @GetItemIssues; - - # get today date - my $today = POSIX::strftime("%Y%m%d", localtime); - - my $sth = $dbh->prepare( - "SELECT * FROM issues + my $sth = C4::Context->dbh->prepare( + "SELECT * + FROM issues LEFT JOIN items ON issues.itemnumber=items.itemnumber - WHERE - issues.itemnumber=?"); + WHERE issues.itemnumber=?"); $sth->execute($itemnumber); my $data = $sth->fetchrow_hashref; - my $datedue = $data->{'date_due'}; - $datedue =~ s/-//g; - if ( $datedue < $today ) { - $data->{'overdue'} = 1; - } - $data->{'itemnumber'} = $itemnumber; # fill itemnumber, in case item is not on issue - $sth->finish; + return unless $data; + $data->{'overdue'} = ($data->{'date_due'} lt C4::Dates->today('iso')) ? 1 : 0; + $data->{'itemnumber'} = $itemnumber; # fill itemnumber, in case item is not on issue. + # FIXME: that would mean issues.itemnumber IS NULL and we didn't really match it. return ($data); } @@ -1708,23 +1723,20 @@ $issues = &GetItemIssues($itemnumber, $history); Returns patrons that have issued a book C<$itemnumber> is the itemnumber -C<$history> is 0 if you want actuel "issuer" (if it exist) and 1 if you want issues history +C<$history> is false if you just want the current "issuer" (if any) +and true if you want issues history from old_issues also. -Returns an array of hashes +Returns reference to an array of hashes =cut sub GetItemIssues { - my ( $itemnumber,$history ) = @_; - my $dbh = C4::Context->dbh; - my @GetItemIssues; + my ( $itemnumber, $history ) = @_; - # get today date - my $today = POSIX::strftime("%Y%m%d", localtime); - + my $today = C4::Dates->today('iso'); # get today date my $sql = "SELECT * FROM issues JOIN borrowers USING (borrowernumber) - JOIN items USING (itemnumber) + JOIN items USING (itemnumber) WHERE issues.itemnumber = ? "; if ($history) { $sql .= "UNION ALL @@ -1734,23 +1746,17 @@ sub GetItemIssues { WHERE old_issues.itemnumber = ? "; } $sql .= "ORDER BY date_due DESC"; - my $sth = $dbh->prepare($sql); + my $sth = C4::Context->dbh->prepare($sql); if ($history) { $sth->execute($itemnumber, $itemnumber); } else { $sth->execute($itemnumber); } - while ( my $data = $sth->fetchrow_hashref ) { - my $datedue = $data->{'date_due'}; - $datedue =~ s/-//g; - if ( $datedue < $today ) { - $data->{'overdue'} = 1; - } - my $itemnumber = $data->{'itemnumber'}; - push @GetItemIssues, $data; + my $results = $sth->fetchall_arrayref({}); + foreach (@$results) { + $_->{'overdue'} = ($_->{'date_due'} lt $today) ? 1 : 0; } - $sth->finish; - return ( \@GetItemIssues ); + return $results; } =head2 GetBiblioIssues @@ -1974,6 +1980,26 @@ sub AddRenewal { my $issuedata = $sth->fetchrow_hashref; $sth->finish; + # 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. + unless ($datedue && $datedue->output('iso')) { + + my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef; + my $loanlength = GetLoanLength( + $borrower->{'categorycode'}, + (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} , + $item->{homebranch} # item's homebranch determines loanlength OR do we want the branch specified by the AddRenewal argument? + ); + + $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ? + C4::Dates->new($issuedata->{date_due}, 'iso') : + C4::Dates->new(); + #FIXME -- use circControl? + $datedue = CalcDateDue($datedue,$loanlength,$branch,$borrower); # this branch is the transactional branch. + # The question of whether to use item's homebranch calendar is open. + } + # Update the issues record to have the new due date, and a new count # of how many times it has been renewed. my $renews = $issuedata->{'renewals'} + 1; @@ -1986,7 +2012,7 @@ sub AddRenewal { # Update the renewal count on the item, and tell zebra to reindex $renews = $biblio->{'renewals'} + 1; - ModItem({ renewals => $renews }, $biblio->{'biblionumber'}, $itemnumber); + ModItem({ renewals => $renews, onloan => $datedue->output('iso') }, $biblio->{'biblionumber'}, $itemnumber); # Charge a new rental fee, if applicable? my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber ); @@ -2275,17 +2301,32 @@ C<$loanlength> = loan length prior to adjustment =cut sub CalcDateDue { - my ($startdate,$loanlength,$branch) = @_; + my ($startdate,$loanlength,$branch,$borrower) = @_; + my $datedue; + if(C4::Context->preference('useDaysMode') eq 'Days') { # ignoring calendar - my $datedue = time + ($loanlength) * 86400; + my $timedue = time + ($loanlength) * 86400; #FIXME - assumes now even though we take a startdate - my @datearr = localtime($datedue); - return C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso'); + my @datearr = localtime($timedue); + $datedue = C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso'); } else { my $calendar = C4::Calendar->new( branchcode => $branch ); - my $datedue = $calendar->addDate($startdate, $loanlength); - return $datedue; + $datedue = $calendar->addDate($startdate, $loanlength); + } + + # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate + if ( C4::Context->preference('ReturnBeforeExpiry') && $datedue->output('iso') gt $borrower->{dateexpiry} ) { + $datedue = C4::Dates->new( $borrower->{dateexpiry}, 'iso' ); + } + + # if ceilingDueDate ON the datedue can't be after the ceiling date + if ( C4::Context->preference('ceilingDueDate') + && ( C4::Context->preference('ceilingDueDate') =~ C4::Dates->regexp('syspref') ) + && $datedue->output gt C4::Context->preference('ceilingDueDate') ) { + $datedue = C4::Dates->new( C4::Context->preference('ceilingDueDate') ); } + + return $datedue; } =head2 CheckValidDatedue