added autocomplete="off"
[koha.git] / C4 / Circulation.pm
index a285713..e68db64 100644 (file)
@@ -24,7 +24,6 @@ use strict;
 use C4::Context;
 use C4::Stats;
 use C4::Reserves;
-use C4::Koha;
 use C4::Biblio;
 use C4::Items;
 use C4::Members;
@@ -43,6 +42,8 @@ use Date::Calc qw(
   Date_to_Days
   Day_of_Week
   Add_Delta_Days       
+  check_date
+  Delta_Days
 );
 use POSIX qw(strftime);
 use C4::Branch; # GetBranches
@@ -59,8 +60,9 @@ BEGIN {
 
        # FIXME subs that should probably be elsewhere
        push @EXPORT, qw(
-               &FixOverduesOnReturn
                &barcodedecode
+        &LostItem
+        &ReturnLostItem
        );
 
        # subs to deal with issuing a book
@@ -72,7 +74,6 @@ BEGIN {
                &GetRenewCount
                &GetItemIssue
                &GetItemIssues
-               &GetBorrowerIssues
                &GetIssuingCharges
                &GetIssuingRule
         &GetBranchBorrowerCircRule
@@ -319,7 +320,7 @@ sub transferbook {
 
     # find reserves.....
     # That'll save a database query.
-    my ( $resfound, $resrec ) =
+    my ( $resfound, $resrec, undef ) =
       CheckReserves( $itemnumber );
     if ( $resfound and not $ignoreRs ) {
         $resrec->{'ResFound'} = $resfound;
@@ -578,7 +579,7 @@ C<$issuingimpossible> and C<$needsconfirmation> are some hashref.
 
 =over 4
 
-=item C<$borrower> hash with borrower informations (from GetMemberDetails)
+=item C<$borrower> hash with borrower informations (from GetMember or GetMemberDetails)
 
 =item C<$barcode> is the bar code of the book being issued.
 
@@ -817,7 +818,7 @@ sub CanBookBeIssued {
             }
         }
     }
-    if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 )
+    if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} > 0 )
     {
         $issuingimpossible{WTHDRAWN} = 1;
     }
@@ -858,7 +859,7 @@ sub CanBookBeIssued {
     elsif ($issue->{borrowernumber}) {
 
         # issued to someone else
-        my $currborinfo =    C4::Members::GetMemberDetails( $issue->{borrowernumber} );
+        my $currborinfo =    C4::Members::GetMember( borrowernumber => $issue->{borrowernumber} );
 
 #        warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
         $needsconfirmation{ISSUED_TO_ANOTHER} = 1;
@@ -869,10 +870,10 @@ sub CanBookBeIssued {
     }
 
     # See if the item is on reserve.
-    my ( $restype, $res ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
+    my ( $restype, $res, undef ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
     if ($restype) {
                my $resbor = $res->{'borrowernumber'};
-               my ( $resborrower ) = C4::Members::GetMemberDetails( $resbor, 0 );
+               my ( $resborrower ) = C4::Members::GetMember( borrowernumber => $resbor );
                my $branches  = GetBranches();
                my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
         if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" )
@@ -909,7 +910,7 @@ Issue a book. Does no check, they are done in CanBookBeIssued. If we reach this
 
 =over 4
 
-=item C<$borrower> is a hash with borrower informations (from GetMemberDetails).
+=item C<$borrower> is a hash with borrower informations (from GetMember or GetMemberDetails).
 
 =item C<$barcode> is the barcode of the item being issued.
 
@@ -983,39 +984,7 @@ sub AddIssue {
                                );
                        }
 
-                       # See if the item is on reserve.
-                       my ( $restype, $res ) =
-                         C4::Reserves::CheckReserves( $item->{'itemnumber'} );
-                       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'});
-                                       }
-                               }
-                               if ($cancelreserve) {
-                                       CancelReserve($res->{'biblionumber'}, 0, $res->{'borrowernumber'});
-                               }
-                               else {
-                                       # set waiting reserve to first in reserve queue as book isn't waiting now
-                                       ModReserve(1,
-                                               $res->{'biblionumber'},
-                                               $res->{'borrowernumber'},
-                                               $res->{'branchcode'}
-                                       );
-                               }
-                       }
+            MoveReserve( $item->{'itemnumber'}, $borrower->{'borrowernumber'}, $cancelreserve );
 
                        # Starting process for transfer job (checking transfert and validate it if we have one)
             my ($datesent) = GetTransfers($item->{'itemnumber'});
@@ -1056,6 +1025,12 @@ sub AddIssue {
           CartToShelf( $item->{'itemnumber'} );
         }
         $item->{'issues'}++;
+
+        ## If item was lost, it has now been found, reverse any list item charges if neccessary.
+        if ( $item->{'itemlost'} ) {
+            _FixAccountForLostAndReturned( $item->{'itemnumber'}, undef, $item->{'barcode'} );
+        }
+
         ModItem({ issues           => $item->{'issues'},
                   holdingbranch    => C4::Context->userenv->{'branch'},
                   itemlost         => 0,
@@ -1515,7 +1490,8 @@ sub AddReturn {
     my $biblio;
     my $doreturn       = 1;
     my $validTransfert = 0;
-    
+    my $stat_type = 'return';    
+
     # get information on item
     my $itemnumber = GetItemnumberFromBarcode( $barcode );
     unless ($itemnumber) {
@@ -1532,6 +1508,11 @@ sub AddReturn {
         # even though item is not on loan, it may still be transferred;  therefore, get current branch info
         $doreturn = 0;
         # No issue, no borrowernumber.  ONLY if $doreturn, *might* you have a $borrower later.
+        # Record this as a local use, instead of a return, if the RecordLocalUseOnReturn is on
+        if (C4::Context->preference("RecordLocalUseOnReturn")) {
+           $messages->{'LocalUse'} = 1;
+           $stat_type = 'localuse';
+        }
     }
 
     my $item = GetItem($itemnumber) or die "GetItem($itemnumber) failed";
@@ -1626,11 +1607,15 @@ sub AddReturn {
     if ($borrowernumber) {
         my $fix = _FixOverduesOnReturn($borrowernumber, $item->{itemnumber}, $exemptfine, $dropbox);
         defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!";  # zero is OK, check defined
+        
+        # fix fine days
+        my $debardate = _FixFineDaysOnReturn( $borrower, $item, $issue->{date_due} );
+        $messages->{'Debarred'} = $debardate if ($debardate);
     }
 
     # find reserves.....
     # if we don't have a reserve with the status W, we launch the Checkreserves routine
-    my ($resfound, $resrec) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
+    my ($resfound, $resrec, undef) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
     if ($resfound) {
           $resrec->{'ResFound'} = $resfound;
         $messages->{'ResFound'} = $resrec;
@@ -1639,7 +1624,7 @@ sub AddReturn {
     # update stats?
     # Record the fact that this book was returned.
     UpdateStats(
-        $branch, 'return', '0', '',
+        $branch, $stat_type, '0', '',
         $item->{'itemnumber'},
         $biblio->{'itemtype'},
         $borrowernumber
@@ -1749,6 +1734,61 @@ sub MarkIssueReturned {
     $sth_del->execute($borrowernumber, $itemnumber);
 }
 
+=head2 _FixFineDaysOnReturn
+
+    &_FixFineDaysOnReturn($borrower, $item, $datedue);
+
+C<$borrower> borrower hashref
+
+C<$item> item hashref
+
+C<$datedue> date due
+
+Internal function, called only by AddReturn that calculate and update the user fine days, and debars him
+
+=cut
+
+sub _FixFineDaysOnReturn {
+    my ( $borrower, $item, $datedue ) = @_;
+
+    if ($datedue) {
+        $datedue = C4::Dates->new( $datedue, "iso" );
+    } else {
+        return;
+    }
+
+    my $branchcode = _GetCircControlBranch( $item, $borrower );
+    my $calendar = C4::Calendar->new( branchcode => $branchcode );
+    my $today = C4::Dates->new();
+
+    my $deltadays = $calendar->daysBetween( $datedue, C4::Dates->new() );
+
+    my $circcontrol = C4::Context::preference('CircControl');
+    my $issuingrule = GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode );
+    my $finedays    = $issuingrule->{finedays};
+
+    # exit if no finedays defined
+    return unless $finedays;
+    my $grace = $issuingrule->{firstremind};
+
+    if ( $deltadays - $grace > 0 ) {
+        my @newdate = Add_Delta_Days( Today(), $deltadays * $finedays );
+        my $isonewdate = join( '-', @newdate );
+        my ( $deby, $debm, $debd ) = split( /-/, $borrower->{debarred} );
+        if ( check_date( $deby, $debm, $debd ) ) {
+            my @olddate = split( /-/, $borrower->{debarred} );
+
+            if ( Delta_Days( @olddate, @newdate ) > 0 ) {
+                C4::Members::DebarMember( $borrower->{borrowernumber}, $isonewdate );
+                return $isonewdate;
+            }
+        } else {
+            C4::Members::DebarMember( $borrower->{borrowernumber}, $isonewdate );
+            return $isonewdate;
+        }
+    }
+}
+
 =head2 _FixOverduesOnReturn
 
    &_FixOverduesOnReturn($brn,$itm, $exemptfine, $dropboxmode);
@@ -1832,10 +1872,11 @@ sub _FixAccountForLostAndReturned {
     my $item_id        = @_ ? shift : $itemnumber;  # Send the barcode if you want that logged in the description
     my $dbh = C4::Context->dbh;
     # check for charge made for lost book
-    my $sth = $dbh->prepare("SELECT * FROM accountlines WHERE (itemnumber = ?) AND (accounttype='L' OR accounttype='Rep') ORDER BY date DESC");
+    my $sth = $dbh->prepare("SELECT * FROM accountlines WHERE itemnumber = ? AND accounttype IN ('L', 'Rep', 'W') ORDER BY date DESC, accountno DESC");
     $sth->execute($itemnumber);
     my $data = $sth->fetchrow_hashref;
     $data or return;    # bail if there is nothing to do
+    $data->{accounttype} eq 'W' and return;    # Written off
 
     # writeoff this amount
     my $offset;
@@ -1924,7 +1965,7 @@ sub _GetCircControlBranch {
     my $branch;
 
     if ($circcontrol eq 'PickupLibrary') {
-        $branch= C4::Context->userenv->{'branch'};
+        $branch= C4::Context->userenv->{'branch'} if C4::Context->userenv;
     } elsif ($circcontrol eq 'PatronLibrary') {
         $branch=$borrower->{branchcode};
     } else {
@@ -2093,9 +2134,10 @@ sub GetUpcomingDueIssues {
     my $dbh = C4::Context->dbh;
 
     my $statement = <<END_SQL;
-SELECT issues.*, items.itype as itemtype, items.homebranch, TO_DAYS( date_due )-TO_DAYS( NOW() ) as days_until_due
+SELECT issues.*, items.itype as itemtype, items.homebranch, TO_DAYS( date_due )-TO_DAYS( NOW() ) as days_until_due, branches.branchemail
 FROM issues 
 LEFT JOIN items USING (itemnumber)
+LEFT OUTER JOIN branches USING (branchcode)
 WhERE returndate is NULL
 AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) < ?
 END_SQL
@@ -2192,7 +2234,7 @@ sub CanBookBeRenewed {
                        $error="too_many";
                }
                
-        my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber);
+        my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
         if ($resfound) {
             $renewokay = 0;
                        $error="on_reserve"
@@ -2254,8 +2296,8 @@ sub AddRenewal {
     # based on the value of the RenewalPeriodBase syspref.
     unless ($datedue) {
 
-        my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef;
-        my $itemtype = (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ,
+        my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber ) or return undef;
+        my $itemtype = (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'};
 
         $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
                                         C4::Dates->new($issuedata->{date_due}, 'iso') :
@@ -2282,16 +2324,15 @@ sub AddRenewal {
     if ( $charge > 0 ) {
         my $accountno = getnextacctno( $borrowernumber );
         my $item = GetBiblioFromItemNumber($itemnumber);
+        my $manager_id = 0;
+        $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv; 
         $sth = $dbh->prepare(
                 "INSERT INTO accountlines
-                    (date,
-                                       borrowernumber, accountno, amount,
-                    description,
-                                       accounttype, amountoutstanding, itemnumber
-                                       )
-                    VALUES (now(),?,?,?,?,?,?,?)"
+                    (date, borrowernumber, accountno, amount, manager_id,
+                    description,accounttype, amountoutstanding, itemnumber)
+                    VALUES (now(),?,?,?,?,?,?,?,?)"
         );
-        $sth->execute( $borrowernumber, $accountno, $charge,
+        $sth->execute( $borrowernumber, $accountno, $charge, $manager_id,
             "Renewal of Rental Item $item->{'title'} $item->{'barcode'}",
             'Rent', $charge, $itemnumber );
         $sth->finish;
@@ -2309,7 +2350,7 @@ sub GetRenewCount {
     my $renewsallowed = 0;
     my $renewsleft    = 0;
 
-    my $borrower = C4::Members::GetMemberDetails($bornum);
+    my $borrower = C4::Members::GetMember( borrowernumber => $bornum);
     my $item     = GetItem($itemno); 
 
     # Look in the issues table for this item, lent to this borrower,
@@ -2444,15 +2485,17 @@ sub AddIssuingCharge {
     my ( $itemnumber, $borrowernumber, $charge ) = @_;
     my $dbh = C4::Context->dbh;
     my $nextaccntno = getnextacctno( $borrowernumber );
+    my $manager_id = 0;
+    $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
     my $query ="
         INSERT INTO accountlines
             (borrowernumber, itemnumber, accountno,
             date, amount, description, accounttype,
-            amountoutstanding)
-        VALUES (?, ?, ?,now(), ?, 'Rental', 'Rent',?)
+            amountoutstanding, manager_id)
+        VALUES (?, ?, ?,now(), ?, 'Rental', 'Rent',?,?)
     ";
     my $sth = $dbh->prepare($query);
-    $sth->execute( $borrowernumber, $itemnumber, $nextaccntno, $charge, $charge );
+    $sth->execute( $borrowernumber, $itemnumber, $nextaccntno, $charge, $charge, $manager_id );
     $sth->finish;
 }
 
@@ -2613,8 +2656,8 @@ sub SendCirculationAlert {
     my ($type, $item, $borrower, $branch) =
         ($opts->{type}, $opts->{item}, $opts->{borrower}, $opts->{branch});
     my %message_name = (
-        CHECKIN  => 'Item Check-in',
-        CHECKOUT => 'Item Checkout',
+        CHECKIN  => 'Item_Check_in',
+        CHECKOUT => 'Item_Checkout',
     );
     my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences({
         borrowernumber => $borrower->{borrowernumber},
@@ -2941,8 +2984,44 @@ sub DeleteBranchTransferLimits {
    $sth->execute();
 }
 
+sub ReturnLostItem{
+    my ( $borrowernumber, $itemnum ) = @_;
+
+    MarkIssueReturned( $borrowernumber, $itemnum );
+    my $borrower = C4::Members::GetMember( 'borrowernumber'=>$borrowernumber );
+    my @datearr = localtime(time);
+    my $date = ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
+    my $bor = "$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
+    ModItem({ paidfor =>  "Paid for by $bor $date" }, undef, $itemnum);
+}
+
+
+sub LostItem{
+    my ($itemnumber, $mark_returned, $charge_fee) = @_;
+
+    my $dbh = C4::Context->dbh();
+    my $sth=$dbh->prepare("SELECT issues.*,items.*,biblio.title 
+                           FROM issues 
+                           JOIN items USING (itemnumber) 
+                           JOIN biblio USING (biblionumber)
+                           WHERE issues.itemnumber=?");
+    $sth->execute($itemnumber);
+    my $issues=$sth->fetchrow_hashref();
+    $sth->finish;
+
+    # if a borrower lost the item, add a replacement cost to the their record
+    if ( my $borrowernumber = $issues->{borrowernumber} ){
+
+        C4::Accounts::chargelostitem($borrowernumber, $itemnumber, $issues->{'replacementprice'}, "Lost Item $issues->{'title'} $issues->{'barcode'}")
+          if $charge_fee;
+        #FIXME : Should probably have a way to distinguish this from an item that really was returned.
+        #warn " $issues->{'borrowernumber'}  /  $itemnumber ";
+        MarkIssueReturned($borrowernumber,$itemnumber) if $mark_returned;
+    }
+}
+
 
-  1;
+1;
 
 __END__