X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FCirculation.pm;h=41d60dd6f062e00e2e18332f0e06101157c214d0;hb=aa114f53499b9cffde0571fe7e08622f9c9a332a;hp=58941543a6ef7f952069cc49f5e035f8d93ecebf;hpb=098ac933153193868775fbd1e6516c01d31c7081;p=koha.git diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 58941543a6..41d60dd6f0 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -21,6 +21,7 @@ package C4::Circulation; use strict; #use warnings; FIXME - Bug 2505 +use DateTime; use C4::Context; use C4::Stats; use C4::Reserves; @@ -28,28 +29,18 @@ use C4::Biblio; use C4::Items; use C4::Members; use C4::Dates; -use C4::Calendar; +use C4::Dates qw(format_date); use C4::Accounts; use C4::ItemCirculationAlertPreference; -use C4::Dates qw(format_date); use C4::Message; use C4::Debug; -use Date::Calc qw( - Today - Today_and_Now - Add_Delta_YM - Add_Delta_DHMS - Date_to_Days - Day_of_Week - Add_Delta_Days - check_date - Delta_Days -); -use POSIX qw(strftime); use C4::Branch; # GetBranches use C4::Log; # logaction use Data::Dumper; +use Koha::DateUtils; +use Koha::Calendar; +use Carp; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @@ -99,7 +90,17 @@ BEGIN { &IsBranchTransferAllowed &CreateBranchTransferLimit &DeleteBranchTransferLimits + &TransferSlip ); + + # subs to deal with offline circulation + push @EXPORT, qw( + &GetOfflineOperations + &GetOfflineOperation + &AddOfflineOperation + &DeleteOfflineOperation + &ProcessOfflineOperation + ); } =head1 NAME @@ -432,7 +433,7 @@ sub TooMany { my $branch_borrower_circ_rule = GetBranchBorrowerCircRule($branch, $cat_borrower); if (defined($branch_borrower_circ_rule->{maxissueqty})) { my @bind_params = (); - my $branch_count_query = "SELECT COUNT(*) FROM issues + my $branch_count_query = "SELECT COUNT(*) FROM issues JOIN items USING (itemnumber) WHERE borrowernumber = ? "; push @bind_params, $borrower->{borrowernumber}; @@ -571,7 +572,7 @@ sub itemissues { =head2 CanBookBeIssued ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $borrower, - $barcode, $duedatespec, $inprocess ); + $barcode, $duedatespec, $inprocess, $ignore_reserves ); Check if a book can be issued. @@ -585,7 +586,8 @@ C<$issuingimpossible> and C<$needsconfirmation> are some hashref. =item C<$duedatespec> is a C4::Dates object. -=item C<$inprocess> +=item C<$inprocess> boolean switch +=item C<$ignore_reserves> boolean switch =back @@ -662,7 +664,7 @@ if the borrower borrows to much things =cut sub CanBookBeIssued { - my ( $borrower, $barcode, $duedate, $inprocess ) = @_; + my ( $borrower, $barcode, $duedate, $inprocess, $ignore_reserves ) = @_; my %needsconfirmation; # filled with problems that needs confirmations my %issuingimpossible; # filled with problems that causes the issue to be IMPOSSIBLE my $item = GetItem(GetItemnumberFromBarcode( $barcode )); @@ -680,21 +682,28 @@ sub CanBookBeIssued { # # DUE DATE is OK ? -- should already have checked. # + if ($duedate && ref $duedate ne 'DateTime') { + $duedate = dt_from_string($duedate); + } + my $now = DateTime->now( time_zone => C4::Context->tz() ); unless ( $duedate ) { - my $issuedate = strftime( "%Y-%m-%d", localtime ); + my $issuedate = $now->clone(); my $branch = _GetCircControlBranch($item,$borrower); my $itype = ( C4::Context->preference('item-level_itypes') ) ? $item->{'itype'} : $biblioitem->{'itemtype'}; - $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $itype, $branch, $borrower ); + $duedate = CalcDateDue( $issuedate, $itype, $branch, $borrower ); # Offline circ calls AddIssue directly, doesn't run through here # So issuingimpossible should be ok. } if ($duedate) { - $needsconfirmation{INVALID_DATE} = $duedate->output('syspref') - unless $duedate->output('iso') ge C4::Dates->today('iso'); + my $today = $now->clone(); + $today->truncate( to => 'minutes'); + if (DateTime->compare($duedate,$today) == -1 ) { # duedate cannot be before now + $needsconfirmation{INVALID_DATE} = output_pref($duedate); + } } else { - $issuingimpossible{INVALID_DATE} = $duedate->output('syspref'); + $issuingimpossible{INVALID_DATE} = output_pref($duedate); } # @@ -715,13 +724,25 @@ sub CanBookBeIssued { if ( $borrower->{flags}->{'DBARRED'} ) { $issuingimpossible{DEBARRED} = 1; } - if ( $borrower->{'dateexpiry'} eq '0000-00-00') { + if ( !defined $borrower->{dateexpiry} || $borrower->{'dateexpiry'} eq '0000-00-00') { $issuingimpossible{EXPIRED} = 1; } else { - my @expirydate= split /-/,$borrower->{'dateexpiry'}; - if($expirydate[0]==0 || $expirydate[1]==0|| $expirydate[2]==0 || - Date_to_Days(Today) > Date_to_Days( @expirydate )) { - $issuingimpossible{EXPIRED} = 1; + my ($y, $m, $d) = split /-/,$borrower->{'dateexpiry'}; + if ($y && $m && $d) { # are we really writing oinvalid dates to borrs + my $expiry_dt = DateTime->new( + year => $y, + month => $m, + day => $d, + time_zone => C4::Context->tz, + ); + $expiry_dt->truncate( to => 'days'); + my $today = $now->clone()->truncate(to => 'days'); + if (DateTime->compare($today, $expiry_dt) == 1) { + $issuingimpossible{EXPIRED} = 1; + } + } else { + carp("Invalid expity date in borr"); + $issuingimpossible{EXPIRED} = 1; } } # @@ -730,7 +751,7 @@ sub CanBookBeIssued { # DEBTS my ($amount) = - C4::Members::GetMemberAccountRecords( $borrower->{'borrowernumber'}, '' && $duedate->output('iso') ); + C4::Members::GetMemberAccountRecords( $borrower->{'borrowernumber'}, '' && $duedate->ymd() ); my $amountlimit = C4::Context->preference("noissuescharge"); my $allowfineoverride = C4::Context->preference("AllowFineOverride"); my $allfinesneedoverride = C4::Context->preference("AllFinesNeedOverride"); @@ -869,37 +890,40 @@ sub CanBookBeIssued { $needsconfirmation{issued_borrowernumber} = $currborinfo->{'borrowernumber'}; } - # See if the item is on reserve. - my ( $restype, $res, undef ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} ); - if ($restype) { - my $resbor = $res->{'borrowernumber'}; - my ( $resborrower ) = C4::Members::GetMember( borrowernumber => $resbor ); - my $branches = GetBranches(); - my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'}; - if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" ) - { - # The item is on reserve and waiting, but has been - # reserved by some other patron. - $needsconfirmation{RESERVE_WAITING} = 1; - $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'}; - $needsconfirmation{'ressurname'} = $resborrower->{'surname'}; - $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'}; - $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'}; - $needsconfirmation{'resbranchname'} = $branchname; - $needsconfirmation{'reswaitingdate'} = format_date($res->{'waitingdate'}); - } - elsif ( $restype eq "Reserved" ) { - # The item is on reserve for someone else. - $needsconfirmation{RESERVED} = 1; - $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'}; - $needsconfirmation{'ressurname'} = $resborrower->{'surname'}; - $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'}; - $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'}; - $needsconfirmation{'resbranchname'} = $branchname; - $needsconfirmation{'resreservedate'} = format_date($res->{'reservedate'}); + unless ( $ignore_reserves ) { + # See if the item is on reserve. + my ( $restype, $res ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} ); + if ($restype) { + my $resbor = $res->{'borrowernumber'}; + if ( $resbor ne $borrower->{'borrowernumber'} ) { + my ( $resborrower ) = C4::Members::GetMember( borrowernumber => $resbor ); + my $branchname = GetBranchName( $res->{'branchcode'} ); + if ( $restype eq "Waiting" ) + { + # The item is on reserve and waiting, but has been + # reserved by some other patron. + $needsconfirmation{RESERVE_WAITING} = 1; + $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'}; + $needsconfirmation{'ressurname'} = $resborrower->{'surname'}; + $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'}; + $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'}; + $needsconfirmation{'resbranchname'} = $branchname; + $needsconfirmation{'reswaitingdate'} = format_date($res->{'waitingdate'}); + } + elsif ( $restype eq "Reserved" ) { + # The item is on reserve for someone else. + $needsconfirmation{RESERVED} = 1; + $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'}; + $needsconfirmation{'ressurname'} = $resborrower->{'surname'}; + $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'}; + $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'}; + $needsconfirmation{'resbranchname'} = $branchname; + $needsconfirmation{'resreservedate'} = format_date($res->{'reservedate'}); + } + } } } - return ( \%issuingimpossible, \%needsconfirmation ); + return ( \%issuingimpossible, \%needsconfirmation ); } =head2 AddIssue @@ -944,13 +968,20 @@ sub AddIssue { my ( $borrower, $barcode, $datedue, $cancelreserve, $issuedate, $sipmode) = @_; my $dbh = C4::Context->dbh; my $barcodecheck=CheckValidBarcode($barcode); + if ($datedue && ref $datedue ne 'DateTime') { + $datedue = dt_from_string($datedue); + } # $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. + $issuedate = DateTime->now(time_zone => C4::Context->tz()); + } + else { + if ( ref $issuedate ne 'DateTime') { + $issuedate = dt_from_string($issuedate); + + } } - if ($borrower and $barcode and $barcodecheck ne '0'){ + if ($borrower and $barcode and $barcodecheck ne '0'){#??? wtf # find which item we issue my $item = GetItem('', $barcode) or return undef; # if we don't get an Item, abort. my $branch = _GetCircControlBranch($item,$borrower); @@ -1004,23 +1035,23 @@ sub AddIssue { # Record in the database the fact that the book was issued. my $sth = $dbh->prepare( - "INSERT INTO issues + "INSERT INTO issues (borrowernumber, itemnumber,issuedate, date_due, branchcode) VALUES (?,?,?,?,?)" ); unless ($datedue) { my $itype = ( C4::Context->preference('item-level_itypes') ) ? $biblio->{'itype'} : $biblio->{'itemtype'}; - $datedue = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $itype, $branch, $borrower ); + $datedue = CalcDateDue( $issuedate, $itype, $branch, $borrower ); } + $datedue->truncate( to => 'minutes'); $sth->execute( $borrower->{'borrowernumber'}, # borrowernumber $item->{'itemnumber'}, # itemnumber - $issuedate, # issuedate - $datedue->output('iso'), # date_due + $issuedate->strftime('%Y-%m-%d %H:%M:00'), # issuedate + $datedue->strftime('%Y-%m-%d %H:%M:00'), # date_due C4::Context->userenv->{'branch'} # branchcode ); - $sth->finish; if ( C4::Context->preference('ReturnToShelvingCart') ) { ## ReturnToShelvingCart is on, anything issued should be taken off the cart. CartToShelf( $item->{'itemnumber'} ); } @@ -1034,8 +1065,8 @@ sub AddIssue { ModItem({ issues => $item->{'issues'}, holdingbranch => C4::Context->userenv->{'branch'}, itemlost => 0, - datelastborrowed => C4::Dates->new()->output('iso'), - onloan => $datedue->output('iso'), + datelastborrowed => DateTime->now(time_zone => C4::Context->tz())->ymd(), + onloan => $datedue->ymd(), }, $item->{'biblionumber'}, $item->{'itemnumber'}); ModDateLastSeen( $item->{'itemnumber'} ); @@ -1097,53 +1128,57 @@ sub GetLoanLength { my $dbh = C4::Context->dbh; my $sth = $dbh->prepare( -"select issuelength from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null" +'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 $sth->execute( $borrowertype, $itemtype, $branchcode ); my $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( $borrowertype, "*", $branchcode ); + $sth->execute( $borrowertype, '*', $branchcode ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( "*", $itemtype, $branchcode ); + $sth->execute( '*', $itemtype, $branchcode ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( "*", "*", $branchcode ); + $sth->execute( '*', '*', $branchcode ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( $borrowertype, $itemtype, "*" ); + $sth->execute( $borrowertype, $itemtype, '*' ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( $borrowertype, "*", "*" ); + $sth->execute( $borrowertype, '*', '*' ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( "*", $itemtype, "*" ); + $sth->execute( '*', $itemtype, '*' ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; - $sth->execute( "*", "*", "*" ); + $sth->execute( '*', '*', '*' ); $loanlength = $sth->fetchrow_hashref; - return $loanlength->{issuelength} - if defined($loanlength) && $loanlength->{issuelength} ne 'NULL'; + return $loanlength + if defined($loanlength) && $loanlength->{issuelength}; # if no rule is set => 21 days (hardcoded) - return 21; + return { + issuelength => 21, + lengthunit => 'days', + }; + } @@ -1164,43 +1199,43 @@ sub GetHardDueDate { ); $sth->execute( $borrowertype, $itemtype, $branchcode ); my $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( $borrowertype, "*", $branchcode ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( "*", $itemtype, $branchcode ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( "*", "*", $branchcode ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( $borrowertype, $itemtype, "*" ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( $borrowertype, "*", "*" ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( "*", $itemtype, "*" ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; $sth->execute( "*", "*", "*" ); $results = $sth->fetchrow_hashref; - return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) - if defined($results) && $results->{hardduedate} ne 'NULL'; + return (dt_from_string($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) + if defined($results) && $results->{hardduedate}; # if no rule is set => return undefined return (undef, undef); @@ -1559,7 +1594,8 @@ sub AddReturn { # define circControlBranch only if dropbox mode is set # don't allow dropbox mode to create an invalid entry in issues (issuedate > today) # FIXME: check issuedate > returndate, factoring in holidays - $circControlBranch = _GetCircControlBranch($item,$borrower) unless ( $item->{'issuedate'} eq C4::Dates->today('iso') );; + #$circControlBranch = _GetCircControlBranch($item,$borrower) unless ( $item->{'issuedate'} eq C4::Dates->today('iso') );; + $circControlBranch = _GetCircControlBranch($item,$borrower); } if ($borrowernumber) { @@ -1696,27 +1732,27 @@ routine in C. sub MarkIssueReturned { my ( $borrowernumber, $itemnumber, $dropbox_branch, $returndate, $privacy ) = @_; my $dbh = C4::Context->dbh; - my $query = "UPDATE issues SET returndate="; + my $query = 'UPDATE issues SET returndate='; my @bind; if ($dropbox_branch) { - my $calendar = C4::Calendar->new( branchcode => $dropbox_branch ); - my $dropboxdate = $calendar->addDate( C4::Dates->new(), -1 ); - $query .= " ? "; - push @bind, $dropboxdate->output('iso'); + my $calendar = Koha->new( branchcode => $dropbox_branch ); + my $dropboxdate = $calendar->addDate( DateTime->now( time_zone => C4::Context->tz), -1 ); + $query .= ' ? '; + push @bind, $dropboxdate->strftimei('%Y-%m-%d %H:%M'); } elsif ($returndate) { - $query .= " ? "; + $query .= ' ? '; push @bind, $returndate; } else { - $query .= " now() "; + $query .= ' now() '; } - $query .= " WHERE borrowernumber = ? AND itemnumber = ?"; + $query .= ' WHERE borrowernumber = ? AND itemnumber = ?'; push @bind, $borrowernumber, $itemnumber; # FIXME transaction my $sth_upd = $dbh->prepare($query); $sth_upd->execute(@bind); - my $sth_copy = $dbh->prepare("INSERT INTO old_issues SELECT * FROM issues + my $sth_copy = $dbh->prepare('INSERT INTO old_issues SELECT * FROM issues WHERE borrowernumber = ? - AND itemnumber = ?"); + AND itemnumber = ?'); $sth_copy->execute($borrowernumber, $itemnumber); # anonymise patron checkout immediately if $privacy set to 2 and AnonymousPatron is set to a valid borrowernumber if ( $privacy == 2) { @@ -1964,8 +2000,8 @@ sub _GetCircControlBranch { my $circcontrol = C4::Context->preference('CircControl'); my $branch; - if ($circcontrol eq 'PickupLibrary') { - $branch= C4::Context->userenv->{'branch'} if C4::Context->userenv; + if ($circcontrol eq 'PickupLibrary' and (C4::Context->userenv and C4::Context->userenv->{'branch'}) ) { + $branch= C4::Context->userenv->{'branch'}; } elsif ($circcontrol eq 'PatronLibrary') { $branch=$borrower->{branchcode}; } else { @@ -2002,14 +2038,19 @@ sub GetItemIssue { return unless $itemnumber; my $sth = C4::Context->dbh->prepare( "SELECT * - FROM issues + FROM issues LEFT JOIN items ON issues.itemnumber=items.itemnumber WHERE issues.itemnumber=?"); $sth->execute($itemnumber); my $data = $sth->fetchrow_hashref; return unless $data; - $data->{'overdue'} = ($data->{'date_due'} lt C4::Dates->today('iso')) ? 1 : 0; - return ($data); + $data->{issuedate} = dt_from_string($data->{issuedate}, 'sql'); + $data->{issuedate}->truncate(to => 'minutes'); + $data->{date_due} = dt_from_string($data->{date_due}, 'sql'); + $data->{date_due}->truncate(to => 'minutes'); + my $dt = DateTime->now( time_zone => C4::Context->tz)->truncate( to => 'minutes'); + $data->{'overdue'} = DateTime->compare($data->{'date_due'}, $dt ) == -1 ? 1 : 0; + return $data; } =head2 GetOpenIssue @@ -2051,14 +2092,15 @@ Returns reference to an array of hashes sub GetItemIssues { my ( $itemnumber, $history ) = @_; - my $today = C4::Dates->today('iso'); # get today date - my $sql = "SELECT * FROM issues + my $today = DateTime->now( time_zome => C4::Context->tz); # get today date + $today->truncate( to => 'minutes' ); + my $sql = "SELECT * FROM issues JOIN borrowers USING (borrowernumber) JOIN items USING (itemnumber) WHERE issues.itemnumber = ? "; if ($history) { $sql .= "UNION ALL - SELECT * FROM old_issues + SELECT * FROM old_issues LEFT JOIN borrowers USING (borrowernumber) JOIN items USING (itemnumber) WHERE old_issues.itemnumber = ? "; @@ -2072,7 +2114,10 @@ sub GetItemIssues { } my $results = $sth->fetchall_arrayref({}); foreach (@$results) { - $_->{'overdue'} = ($_->{'date_due'} lt $today) ? 1 : 0; + my $date_due = dt_from_string($_->{date_due},'sql'); + $date_due->truncate( to => 'minutes' ); + + $_->{overdue} = (DateTime->compare($date_due, $today) == -1) ? 1 : 0; } return $results; } @@ -2202,7 +2247,7 @@ sub CanBookBeRenewed { SELECT borrowers.categorycode, biblioitems.itemtype, issues.renewals, renewalsallowed, $controlbranch FROM issuingrules, - issues + issues LEFT JOIN items USING (itemnumber) LEFT JOIN borrowers USING (borrowernumber) LEFT JOIN biblioitems USING (biblioitemnumber) @@ -2273,7 +2318,7 @@ sub AddRenewal { my $itemnumber = shift or return undef; my $branch = shift; my $datedue = shift; - my $lastreneweddate = shift || C4::Dates->new()->output('iso'); + my $lastreneweddate = shift || DateTime->now(time_zone => C4::Context->tz)->ymd(); my $item = GetItem($itemnumber) or return undef; my $biblio = GetBiblioFromItemNumber($itemnumber) or return undef; @@ -2287,9 +2332,9 @@ sub AddRenewal { $sth->execute( $borrowernumber, $itemnumber ); my $issuedata = $sth->fetchrow_hashref; $sth->finish; - if($datedue && ! $datedue->output('iso')){ - warn "Invalid date passed to AddRenewal."; - return undef; + if(defined $datedue && ref $datedue ne 'DateTime' ) { + carp 'Invalid date passed to AddRenewal.'; + return; } # 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 @@ -2300,8 +2345,8 @@ sub AddRenewal { 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') : - C4::Dates->new(); + $issuedata->{date_due} : + DateTime->now( time_zone => C4::Context->tz()); $datedue = CalcDateDue($datedue,$itemtype,$issuedata->{'branchcode'},$borrower); } @@ -2312,12 +2357,13 @@ sub AddRenewal { WHERE borrowernumber=? AND itemnumber=?" ); - $sth->execute( $datedue->output('iso'), $renews, $lastreneweddate, $borrowernumber, $itemnumber ); + + $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $lastreneweddate, $borrowernumber, $itemnumber ); $sth->finish; # Update the renewal count on the item, and tell zebra to reindex $renews = $biblio->{'renewals'} + 1; - ModItem({ renewals => $renews, onloan => $datedue->output('iso') }, $biblio->{'biblionumber'}, $itemnumber); + ModItem({ renewals => $renews, onloan => $datedue->strftime('%Y-%m-%d %H:%M')}, $biblio->{'biblionumber'}, $itemnumber); # Charge a new rental fee, if applicable? my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber ); @@ -2335,7 +2381,6 @@ sub AddRenewal { $sth->execute( $borrowernumber, $accountno, $charge, $manager_id, "Renewal of Rental Item $item->{'title'} $item->{'barcode'}", 'Rent', $charge, $itemnumber ); - $sth->finish; } # Log the renewal UpdateStats( $branch, 'renew', $charge, '', $itemnumber, $item->{itype}, $borrowernumber); @@ -2663,11 +2708,18 @@ sub SendCirculationAlert { borrowernumber => $borrower->{borrowernumber}, message_name => $message_name{$type}, }); - my $letter = C4::Letters::getletter('circulation', $type); - C4::Letters::parseletter($letter, 'biblio', $item->{biblionumber}); - C4::Letters::parseletter($letter, 'biblioitems', $item->{biblionumber}); - C4::Letters::parseletter($letter, 'borrowers', $borrower->{borrowernumber}); - C4::Letters::parseletter($letter, 'branches', $branch); + my $letter = C4::Letters::GetPreparedLetter ( + module => 'circulation', + letter_code => $type, + branchcode => $branch, + tables => { + 'biblio' => $item->{biblionumber}, + 'biblioitems' => $item->{biblionumber}, + 'borrowers' => $borrower, + 'branches' => $branch, + } + ) or return; + my @transports = @{ $borrower_preferences->{transports} }; # warn "no transports" unless @transports; for (@transports) { @@ -2682,7 +2734,8 @@ sub SendCirculationAlert { $message->update; } } - $letter; + + return $letter; } =head2 updateWrongTransfer @@ -2737,88 +2790,85 @@ C<$borrower> = Borrower object =cut -sub CalcDateDue { - my ($startdate,$itemtype,$branch,$borrower) = @_; - my $datedue; - my $loanlength = GetLoanLength($borrower->{'categorycode'},$itemtype, $branch); +sub CalcDateDue { + my ( $startdate, $itemtype, $branch, $borrower ) = @_; - # if globalDueDate ON the datedue is set to that date - if ( C4::Context->preference('globalDueDate') - && ( C4::Context->preference('globalDueDate') =~ C4::Dates->regexp('syspref') ) ) { - $datedue = C4::Dates->new( C4::Context->preference('globalDueDate') ); - } else { - # otherwise, calculate the datedue as normal - if(C4::Context->preference('useDaysMode') eq 'Days') { # ignoring calendar - my $timedue = time + ($loanlength) * 86400; - #FIXME - assumes now even though we take a startdate - 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 ); - $datedue = $calendar->addDate($startdate, $loanlength); - } - } + # loanlength now a href + my $loanlength = + GetLoanLength( $borrower->{'categorycode'}, $itemtype, $branch ); - # if Hard Due Dates are used, retreive them and apply as necessary - my ($hardduedate, $hardduedatecompare) = GetHardDueDate($borrower->{'categorycode'},$itemtype, $branch); - if ( $hardduedate && $hardduedate->output('iso') && $hardduedate->output('iso') ne '0000-00-00') { - # if the calculated due date is after the 'before' Hard Due Date (ceiling), override - if ( $datedue->output( 'iso' ) gt $hardduedate->output( 'iso' ) && $hardduedatecompare == -1) { - $datedue = $hardduedate; - # if the calculated date is before the 'after' Hard Due Date (floor), override - } elsif ( $datedue->output( 'iso' ) lt $hardduedate->output( 'iso' ) && $hardduedatecompare == 1) { - $datedue = $hardduedate; - # if the hard due date is set to 'exactly', overrride - } elsif ( $hardduedatecompare == 0) { - $datedue = $hardduedate; - } - # in all other cases, keep the date due as it is - } + my $datedue; - # 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' ); - } - - return $datedue; -} - -=head2 CheckValidDatedue - - $newdatedue = CheckValidDatedue($date_due,$itemnumber,$branchcode); + # 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') + ); + } else { -This function does not account for holiday exceptions nor does it handle the 'useDaysMode' syspref . -To be replaced by CalcDateDue() once C4::Calendar use is tested. + # 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} ); + return $dt; + } else { # days + $dt->add( days => $loanlength->{issuelength} ); + $dt->set_hour(23); + $dt->set_minute(59); + 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} ); + } + } -this function validates the loan length against the holidays calendar, and adjusts the due date as per the 'useDaysMode' syspref. -C<$date_due> = returndate calculate with no day check -C<$itemnumber> = itemnumber -C<$branchcode> = location of issue (affected by 'CircControl' syspref) -C<$loanlength> = loan length prior to adjustment + # if Hard Due Dates are used, retreive them and apply as necessary + my ( $hardduedate, $hardduedatecompare ) = + GetHardDueDate( $borrower->{'categorycode'}, $itemtype, $branch ); + if ($hardduedate) { # hardduedates are currently dates + $hardduedate->truncate( to => 'minute' ); + $hardduedate->set_hour(23); + $hardduedate->set_minute(59); + my $cmp = DateTime->compare( $hardduedate, $datedue ); + +# if the calculated due date is after the 'before' Hard Due Date (ceiling), override +# if the calculated date is before the 'after' Hard Due Date (floor), override +# if the hard due date is set to 'exactly', overrride + if ( $hardduedatecompare == 0 || $hardduedatecompare == $cmp ) { + $datedue = $hardduedate->clone; + } -=cut + # in all other cases, keep the date due as it is + } -sub CheckValidDatedue { -my ($date_due,$itemnumber,$branchcode)=@_; -my @datedue=split('-',$date_due->output('iso')); -my $years=$datedue[0]; -my $month=$datedue[1]; -my $day=$datedue[2]; -# die "Item# $itemnumber ($branchcode) due: " . ${date_due}->output() . "\n(Y,M,D) = ($years,$month,$day)": -my $dow; -for (my $i=0;$i<2;$i++){ - $dow=Day_of_Week($years,$month,$day); - ($dow=0) if ($dow>6); - my $result=CheckRepeatableHolidays($itemnumber,$dow,$branchcode); - my $countspecial=CheckSpecialHolidays($years,$month,$day,$itemnumber,$branchcode); - my $countspecialrepeatable=CheckRepeatableSpecialHolidays($month,$day,$itemnumber,$branchcode); - if (($result ne '0') or ($countspecial ne '0') or ($countspecialrepeatable ne '0') ){ - $i=0; - (($years,$month,$day) = Add_Delta_Days($years,$month,$day, 1))if ($i ne '1'); + # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate + if ( C4::Context->preference('ReturnBeforeExpiry') ) { + my $expiry_dt = dt_from_string( $borrower->{dateexpiry}, 'iso' ); + if ( DateTime->compare( $datedue, $expiry_dt ) == 1 ) { + $datedue = $expiry_dt->clone; } } - my $newdatedue=C4::Dates->new(sprintf("%04d-%02d-%02d",$years,$month,$day),'iso'); -return $newdatedue; + + return $datedue; } @@ -3023,6 +3073,145 @@ sub LostItem{ } } +sub GetOfflineOperations { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE branchcode=? ORDER BY timestamp"); + $sth->execute(C4::Context->userenv->{'branch'}); + my $results = $sth->fetchall_arrayref({}); + $sth->finish; + return $results; +} + +sub GetOfflineOperation { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE operationid=?"); + $sth->execute( shift ); + my $result = $sth->fetchrow_hashref; + $sth->finish; + return $result; +} + +sub AddOfflineOperation { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber) VALUES(?,?,?,?,?,?)"); + $sth->execute( @_ ); + return "Added."; +} + +sub DeleteOfflineOperation { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("DELETE FROM pending_offline_operations WHERE operationid=?"); + $sth->execute( shift ); + return "Deleted."; +} + +sub ProcessOfflineOperation { + my $operation = shift; + + my $report; + if ( $operation->{action} eq 'return' ) { + $report = ProcessOfflineReturn( $operation ); + } elsif ( $operation->{action} eq 'issue' ) { + $report = ProcessOfflineIssue( $operation ); + } + + DeleteOfflineOperation( $operation->{operationid} ) if $operation->{operationid}; + + return $report; +} + +sub ProcessOfflineReturn { + my $operation = shift; + + my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} ); + + if ( $itemnumber ) { + my $issue = GetOpenIssue( $itemnumber ); + if ( $issue ) { + MarkIssueReturned( + $issue->{borrowernumber}, + $itemnumber, + undef, + $operation->{timestamp}, + ); + ModItem( + { renewals => 0, onloan => undef }, + $issue->{'biblionumber'}, + $itemnumber + ); + return "Success."; + } else { + return "Item not issued."; + } + } else { + return "Item not found."; + } +} + +sub ProcessOfflineIssue { + my $operation = shift; + + my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber + + if ( $borrower->{borrowernumber} ) { + my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} ); + unless ($itemnumber) { + return "Barcode not found."; + } + my $issue = GetOpenIssue( $itemnumber ); + + if ( $issue and ( $issue->{borrowernumber} ne $borrower->{borrowernumber} ) ) { # Item already issued to another borrower, mark it returned + MarkIssueReturned( + $issue->{borrowernumber}, + $itemnumber, + undef, + $operation->{timestamp}, + ); + } + AddIssue( + $borrower, + $operation->{'barcode'}, + undef, + 1, + $operation->{timestamp}, + undef, + ); + return "Success."; + } else { + return "Borrower not found."; + } +} + + + +=head2 TransferSlip + + TransferSlip($user_branch, $itemnumber, $to_branch) + + Returns letter hash ( see C4::Letters::GetPreparedLetter ) or undef + +=cut + +sub TransferSlip { + my ($branch, $itemnumber, $to_branch) = @_; + + my $item = GetItem( $itemnumber ) + or return; + + my $pulldate = C4::Dates->new(); + + return C4::Letters::GetPreparedLetter ( + module => 'circulation', + letter_code => 'TRANSFERSLIP', + branchcode => $branch, + tables => { + 'branches' => $to_branch, + 'biblio' => $item->{biblionumber}, + 'items' => $item, + }, + ); +} + 1;