use strict;
#use warnings; FIXME - Bug 2505
+use DateTime;
use C4::Context;
use C4::Stats;
use C4::Reserves;
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);
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};
#
# 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);
}
#
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;
}
}
#
# 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");
#
my ($current_loan_count, $max_loans_allowed) = TooMany( $borrower, $item->{biblionumber}, $item );
# if TooMany max_loans_allowed returns 0 the user doesn't have permission to check out this book
- if ($max_loans_allowed eq 0) {
+ if (defined $max_loans_allowed && $max_loans_allowed == 0) {
$needsconfirmation{PATRON_CANT} = 1;
} else {
if($max_loans_allowed){
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());
}
- if ($borrower and $barcode and $barcodecheck ne '0'){
+ else {
+ if ( ref $issuedate ne 'DateTime') {
+ $issuedate = dt_from_string($issuedate);
+
+ }
+ }
+ 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);
# check if we just renew the issue.
#
if ($actualissue->{borrowernumber} eq $borrower->{'borrowernumber'}) {
- $datedue = AddRenewal(
- $borrower->{'borrowernumber'},
- $item->{'itemnumber'},
- $branch,
- $datedue,
- $issuedate, # here interpreted as the renewal date
+ $datedue = AddRenewal(
+ $borrower->{'borrowernumber'},
+ $item->{'itemnumber'},
+ $branch,
+ $datedue,
+ $issuedate, # here interpreted as the renewal date
);
}
else {
}
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'});
if ($datesent) {
# 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'} );
}
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'} );
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',
+ };
+
}
);
$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);
# 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) {
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::Calendar->new( branchcode => $dropbox_branch );
+ my $dropboxdate = $calendar->addDate( DateTime->now( time_zone => C4::Context->tz), -1 );
+ $query .= ' ? ';
+ push @bind, $dropboxdate->strftime('%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) {
sub _FixFineDaysOnReturn {
my ( $borrower, $item, $datedue ) = @_;
-
- if ($datedue) {
- $datedue = C4::Dates->new( $datedue, "iso" );
- } else {
- return;
- }
+ return unless ($datedue);
+
+ my $dt_due = dt_from_string( $datedue );
+ my $dt_today = DateTime->now( time_zone => C4::Context->tz() );
my $branchcode = _GetCircControlBranch( $item, $borrower );
- my $calendar = C4::Calendar->new( branchcode => $branchcode );
- my $today = C4::Dates->new();
+ my $calendar = Koha::Calendar->new( branchcode => $branchcode );
- my $deltadays = $calendar->daysBetween( $datedue, C4::Dates->new() );
+ # $deltadays is a DateTime::Duration object
+ my $deltadays = $calendar->days_between( $dt_due, $dt_today );
my $circcontrol = C4::Context::preference('CircControl');
my $issuingrule = GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode );
# 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;
+ my $grace = DateTime::Duration->new( days => $issuingrule->{firstremind} );
+
+ if ( ( $deltadays - $grace )->is_positive ) { # you can't compare DateTime::Durations with logical operators
+ my $new_debar_dt = $dt_today->clone()->add_duration( $deltadays * $finedays );
+ my $borrower_debar_dt = dt_from_string( $borrower->{debarred} );
+ # check to see if the current debar date is a valid date
+ if ( $borrower->{debarred} && $borrower_debar_dt ) {
+ # if so, is it before the new date? update only if true
+ if ( DateTime->compare( $borrower_debar_dt, $new_debar_dt ) == -1 ) {
+ C4::Members::DebarMember( $borrower->{borrowernumber}, $new_debar_dt->ymd() );
+ return $new_debar_dt->ymd();
}
+ # if the borrower's debar date is not set or valid, debar them
} else {
- C4::Members::DebarMember( $borrower->{borrowernumber}, $isonewdate );
- return $isonewdate;
+ C4::Members::DebarMember( $borrower->{borrowernumber}, $new_debar_dt->ymd() );
+ return $new_debar_dt->ymd();
}
}
}
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
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 = ? ";
}
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;
}
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)
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;
$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
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);
}
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 );
$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);
=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} );
+ if ($loanlength->{lengthunit} eq 'days') {
+ $datedue->set_hour(23);
+ $datedue->set_minute(59);
+ }
+ }
+ }
-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;
}
use C4::Boolean;
use C4::Debug;
use POSIX ();
+use DateTime::TimeZone;
=head1 NAME
$self->{"userenv"} = undef; # User env
$self->{"activeuser"} = undef; # current active user
$self->{"shelves"} = undef;
+ $self->{tz} = undef; # local timezone object
bless $self, $class;
return $self;
}
+=head2 tz
+
+ C4::Context->tz
+
+ Returns a DateTime::TimeZone object for the system timezone
+
+=cut
+
+sub tz {
+ my $self = shift;
+ if (!defined $context->{tz}) {
+ $context->{tz} = DateTime::TimeZone->new(name => 'local');
+ }
+ return $context->{tz};
+}
+
+
+
1;
__END__
use XML::Simple;
use HTML::Entities;
use CGI;
+use DateTime;
=head1 NAME
# Hashref building
my $out;
$out->{'renewals'} = $issue->{'renewals'};
- $out->{'date_due'} = $issue->{'date_due'};
+ $out->{date_due} = $issue->{date_due}->strftime('%Y-%m-%d %H:%S');
$out->{'success'} = $renewal[0];
$out->{'error'} = $renewal[1];
'DateTime' => {
'usage' => 'Core',
'required' => '1',
- 'min_ver' => '0.51'
+ 'min_ver' => '0.58'
+ },
+ 'DateTime::TimeZone' => {
+ 'usage' => 'Core',
+ 'required' => '1',
+ 'min_ver' => '1.26'
+ },
+ 'DateTime::Format::DateParse' => {
+ 'usage' => 'Core',
+ 'required' => '1',
+ 'min_ver' => '0.04'
+ },
+ 'DateTime::Set' => {
+ 'usage' => 'Core',
+ 'required' => '1',
+ 'min_ver' => '0.28'
+ },
+ 'DateTime::Event::ICal' => {
+ 'usage' => 'Core',
+ 'required' => '1',
+ 'min_ver' => '0.08'
+ },
+ 'Readonly' => {
+ 'usage' => 'Core',
+ 'required' => '1',
+ 'min_ver' => '1.03'
+ },
+ 'Readonly::XS' => {
+ 'usage' => 'Core',
+ 'required' => '0',
+ 'min_ver' => '1.02'
},
'Graphics::Magick' => {
'usage' => 'Patron Card Creator Feature',
use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable);
use C4::Members::Attributes qw(SearchIdMatchingAttribute);
use C4::NewsChannels; #get slip news
+use DateTime;
+use DateTime::Format::DateParse;
+use Koha::DateUtils;
our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
"SELECT COUNT(*) as latedocs
FROM issues
WHERE borrowernumber = ?
- AND date_due < curdate()"
+ AND date_due < now()"
);
$sth->execute($borrowernumber);
my $latedocs = $sth->fetchrow_hashref->{'latedocs'};
$sth = $dbh->prepare(
"SELECT COUNT(*) FROM issues
WHERE borrowernumber = ?
- AND date_due < curdate()"
+ AND date_due < now()"
);
$sth->execute($borrowernumber);
my $overdue_count = $sth->fetchrow_arrayref->[0];
my $sth = C4::Context->dbh->prepare($query);
$sth->execute(@borrowernumbers);
my $data = $sth->fetchall_arrayref({});
- my $today = C4::Dates->new->output('iso');
+ my $tz = C4::Context->tz();
+ my $today = DateTime->now( time_zone => $tz);
foreach (@{$data}) {
- if ($_->{date_due} and $_->{date_due} lt $today) {
+ if ($_->{issuedate}) {
+ $_->{issuedate} = dt_from_string($_->{issuedate}, 'sql');
+ }
+ $_->{date_due} or next;
+ $_->{date_due} = DateTime::Format::DateParse->parse_datetime($_->{date_due}, $tz->name());
+ if ( DateTime->compare($_->{date_due}, $today) == -1 ) {
$_->{overdue} = 1;
}
}
SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode
FROM issues
LEFT JOIN items USING (itemnumber)
- WHERE date_due < CURDATE()
+ WHERE date_due < NOW()
";
} else {
$statement = "
FROM issues
LEFT JOIN items USING (itemnumber)
LEFT JOIN biblioitems USING (biblioitemnumber)
- WHERE date_due < CURDATE()
+ WHERE date_due < NOW()
";
}
LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
WHERE issues.borrowernumber = ?
- AND issues.date_due < CURDATE()"
+ AND issues.date_due < NOW()"
);
# FIXME: SELECT * across 4 tables? do we really need the marc AND marcxml blobs??
$sth->execute($borrowernumber);
=head2 CalcFine
- ($amount, $chargename, $daycount, $daycounttotal) = &CalcFine($item,
- $categorycode, $branch, $days_overdue,
- $description, $start_date, $end_date );
+ ($amount, $chargename, $daycounttotal) = &CalcFine($item,
+ $categorycode, $branch,
+ $start_dt, $end_dt );
Calculates the fine for a book.
C<$branchcode> is the library (string) whose issuingrules govern this transaction.
-C<$days_overdue> is the number of days elapsed since the book's due date.
- NOTE: supplying days_overdue is deprecated.
-
-C<$start_date> & C<$end_date> are C4::Dates objects
+C<$start_date> & C<$end_date> are DateTime objects
defining the date range over which to determine the fine.
-Note that if these are defined, we ignore C<$difference> and C<$dues> ,
-but retain these for backwards-comptibility with extant fines scripts.
Fines scripts should just supply the date range over which to calculate the fine.
C<$daycount> is the number of days between start and end dates, Calendar adjusted (where needed),
minus any applicable grace period.
-C<$daycounttotal> is C<$daycount> without consideration of grace period.
-
FIXME - What is chargename supposed to be ?
FIXME: previously attempted to return C<$message> as a text message, either "First Notice", "Second Notice",
=cut
sub CalcFine {
- my ( $item, $bortype, $branchcode, $difference ,$dues , $start_date, $end_date ) = @_;
- $debug and warn sprintf("CalcFine(%s, %s, %s, %s, %s, %s, %s)",
- ($item ? '{item}' : 'UNDEF'),
- ($bortype || 'UNDEF'),
- ($branchcode || 'UNDEF'),
- ($difference || 'UNDEF'),
- ($dues || 'UNDEF'),
- ($start_date ? ($start_date->output('iso') || 'Not a C4::Dates object') : 'UNDEF'),
- ( $end_date ? ( $end_date->output('iso') || 'Not a C4::Dates object') : 'UNDEF')
- );
+ my ( $item, $bortype, $branchcode, $due_dt, $end_date ) = @_;
+ my $start_date = $due_dt->clone();
my $dbh = C4::Context->dbh;
my $amount = 0;
- my $daystocharge;
- # get issuingrules (fines part will be used)
- $debug and warn sprintf("CalcFine calling GetIssuingRule(%s, %s, %s)", $bortype, $item->{'itemtype'}, $branchcode);
- my $data = C4::Circulation::GetIssuingRule($bortype, $item->{'itemtype'}, $branchcode);
- if($difference) {
- # if $difference is supplied, the difference has already been calculated, but we still need to adjust for the calendar.
- # use copy-pasted functions from calendar module. (deprecated -- these functions will be removed from C4::Overdues ).
- my $countspecialday = &GetSpecialHolidays($dues,$item->{itemnumber});
- my $countrepeatableday = &GetRepeatableHolidays($dues,$item->{itemnumber},$difference);
- my $countalldayclosed = $countspecialday + $countrepeatableday;
- $daystocharge = $difference - $countalldayclosed;
- } else {
- # if $difference is not supplied, we have C4::Dates objects giving us the date range, and we use the calendar module.
- if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
- my $calendar = C4::Calendar->new( branchcode => $branchcode );
- $daystocharge = $calendar->daysBetween( $start_date, $end_date );
- } else {
- $daystocharge = Date_to_Days(split('-',$end_date->output('iso'))) - Date_to_Days(split('-',$start_date->output('iso')));
- }
- }
- # correct for grace period.
- my $days_minus_grace = $daystocharge - $data->{'firstremind'};
- if ($data->{'chargeperiod'} > 0 && $days_minus_grace > 0 ) {
- $amount = int($daystocharge / $data->{'chargeperiod'}) * $data->{'fine'};
+ my $charge_duration;
+ # get issuingrules (fines part will be used)
+ my $data = C4::Circulation::GetIssuingRule($bortype, $item->{itemtype}, $branchcode);
+ if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
+ my $calendar = Koha::Calendar->new( branchcode => $branchcode );
+ $charge_duration = $calendar->days_between( $start_date, $end_date );
+ } else {
+ $charge_duration = $end_date - $start_date;
+ }
+ # correct for grace period.
+ my $fine_unit = $data->{lengthunit};
+ $fine_unit ||= 'days';
+ my $chargeable_units;
+ if ($fine_unit eq 'hours') {
+ $chargeable_units = $charge_duration->hours(); # TODO closed times???
+ }
+ else {
+ $chargeable_units = $charge_duration->days;
+ }
+ my $days_minus_grace = $chargeable_units - $data->{firstremind};
+ if ($data->{'chargeperiod'} && $days_minus_grace ) {
+ $amount = int($chargeable_units / $data->{'chargeperiod'}) * $data->{'fine'};# TODO fine calc should be in cents
} else {
# a zero (or null) chargeperiod means no charge.
}
- $amount = C4::Context->preference('maxFine') if(C4::Context->preference('maxFine') && ( $amount > C4::Context->preference('maxFine')));
- $debug and warn sprintf("CalcFine returning (%s, %s, %s, %s)", $amount, $data->{'chargename'}, $days_minus_grace, $daystocharge);
- return ($amount, $data->{'chargename'}, $days_minus_grace, $daystocharge);
+ if(C4::Context->preference('maxFine') && ( $amount > C4::Context->preference('maxFine'))) {
+ $amount = C4::Context->preference('maxFine');
+ }
+ return ($amount, $data->{chargename}, $days_minus_grace);
# FIXME: chargename is NEVER populated anywhere.
}
WHERE (accountlines.amountoutstanding != '0.000000')
AND (accountlines.accounttype = 'FU' )
AND (issues.branchcode = ? )
- AND (issues.date_due < CURDATE())
+ AND (issues.date_due < NOW())
";
my @getoverdues;
my $i = 0;
use C4::Debug;
use C4::Context;
-# use C4::Dates;
use C4::Koha;
use C4::Members;
use C4::Reserves;
$debug and warn "do_checkout: calling AddIssue(\$borrower,$barcode, undef, 0)\n"
# . "w/ \$borrower: " . Dumper($borrower)
. "w/ C4::Context->userenv: " . Dumper(C4::Context->userenv);
- my $c4due = AddIssue($borrower, $barcode, undef, 0);
- my $due = $c4due->output('iso') || undef;
- $debug and warn "Item due: $due";
- $self->{'due'} = $due;
- $self->{item}->due_date($due);
+ my $due_dt = AddIssue($borrower, $barcode, undef, 0);
+ if ($due_dt) {
+ $self->{due} = $due_dt->clone();
+ } else {
+ $self->{due} = undef;
+ }
+
+ #$self->{item}->due_date($due);
$self->ok(1);
return $self;
}
my $borrower = shift;
my ($renewokay,$renewerror) = CanBookBeRenewed($borrower->{borrowernumber},$self->{item}->{itemnumber});
if ($renewokay){
- my $datedue = AddIssue( $borrower, $self->{item}->id, undef, 0 );
- $self->{due} = $datedue;
+ $self->{due} = AddIssue( $borrower, $self->{item}->id, undef, 0 );
$self->renewal_ok(1);
} else {
$self->screen_msg(($self->screen_msg || '') . " " . $renewerror);
$self->{item} = $item;
$self->do_renew_for($borrower);
if ($self->ok) {
- $item->{due_date} = $self->{due};
- push @{$self->{renewed} }, $item_id;
+ $item->{due_date} = $self->{due}->clone();
+ push @{$self->renewed }, $item_id;
} else {
push @{$self->{unrenewed}}, $item_id;
}
sub timestamp {
my $time = $_[0] || time();
- if ($time=~m/^(\d{4})\-(\d{2})\-(\d{2})/) {
+ if ( ref $time eq 'DateTime') {
+ return $time->strftime(SIP_DATETIME);
+ } elsif ($time=~m/^(\d{4})\-(\d{2})\-(\d{2})/) {
# passing a db returned date as is + bogus time
return sprintf( '%04d%02d%02d 235900', $1, $2, $3);
}
--- /dev/null
+package Koha::Calendar;
+use strict;
+use warnings;
+use 5.010;
+
+use DateTime;
+use DateTime::Set;
+use DateTime::Duration;
+use C4::Context;
+use Carp;
+use Readonly;
+
+sub new {
+ my ( $classname, %options ) = @_;
+ my $self = {};
+ bless $self, $classname;
+ for my $o_name ( keys %options ) {
+ my $o = lc $o_name;
+ $self->{$o} = $options{$o_name};
+ }
+ if ( exists $options{TEST_MODE} ) {
+ $self->_mockinit();
+ return $self;
+ }
+ if ( !defined $self->{branchcode} ) {
+ croak 'No branchcode argument passed to Koha::Calendar->new';
+ }
+ $self->_init();
+ return $self;
+}
+
+sub _init {
+ my $self = shift;
+ my $branch = $self->{branchcode};
+ my $dbh = C4::Context->dbh();
+ my $repeat_sth = $dbh->prepare(
+'SELECT * from repeatable_holidays WHERE branchcode = ? AND ISNULL(weekday) = ?'
+ );
+ $repeat_sth->execute( $branch, 0 );
+ $self->{weekly_closed_days} = [ 0, 0, 0, 0, 0, 0, 0 ];
+ Readonly::Scalar my $sunday => 7;
+ while ( my $tuple = $repeat_sth->fetchrow_hashref ) {
+ $self->{weekly_closed_days}->[ $tuple->{weekday} ] = 1;
+ }
+ $repeat_sth->execute( $branch, 1 );
+ $self->{day_month_closed_days} = {};
+ while ( my $tuple = $repeat_sth->fetchrow_hashref ) {
+ $self->{day_month_closed_days}->{ $tuple->{day} }->{ $tuple->{month} } =
+ 1;
+ }
+ my $special = $dbh->prepare(
+'SELECT day, month, year, title, description FROM special_holidays WHERE ( branchcode = ? ) AND (isexception = ?)'
+ );
+ $special->execute( $branch, 1 );
+ my $dates = [];
+ while ( my ( $day, $month, $year, $title, $description ) =
+ $special->fetchrow ) {
+ push @{$dates},
+ DateTime->new(
+ day => $day,
+ month => $month,
+ year => $year,
+ time_zone => C4::Context->tz()
+ )->truncate( to => 'day' );
+ }
+ $self->{exception_holidays} =
+ DateTime::Set->from_datetimes( dates => $dates );
+ $special->execute( $branch, 1 );
+ $dates = [];
+ while ( my ( $day, $month, $year, $title, $description ) =
+ $special->fetchrow ) {
+ push @{$dates},
+ DateTime->new(
+ day => $day,
+ month => $month,
+ year => $year,
+ time_zone => C4::Context->tz()
+ )->truncate( to => 'day' );
+ }
+ $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates );
+ $self->{days_mode} = C4::Context->preference('useDaysMode');
+ return;
+}
+
+sub addDate {
+ my ( $self, $startdate, $add_duration, $unit ) = @_;
+ my $base_date = $startdate->clone();
+ if ( ref $add_duration ne 'DateTime::Duration' ) {
+ $add_duration = DateTime::Duration->new( days => $add_duration );
+ }
+ $unit ||= q{}; # default days ?
+ my $days_mode = $self->{days_mode};
+ Readonly::Scalar my $return_by_hour => 10;
+ my $day_dur = DateTime::Duration->new( days => 1 );
+ if ( $add_duration->is_negative() ) {
+ $day_dur->inverse();
+ }
+ if ( $days_mode eq 'Datedue' ) {
+
+ my $dt = $base_date + $add_duration;
+ while ( $self->is_holiday($dt) ) {
+
+ # TODOP if hours set to 10 am
+ $dt->add_duration($day_dur);
+ if ( $unit eq 'hours' ) {
+ $dt->set_hour($return_by_hour); # Staffs specific
+ }
+ }
+ return $dt;
+ } elsif ( $days_mode eq 'Calendar' ) {
+ if ( $unit eq 'hours' ) {
+ $base_date->add_duration($add_duration);
+ while ( $self->is_holiday($base_date) ) {
+ $base_date->add_duration($day_dur);
+
+ }
+
+ } else {
+ my $days = abs $add_duration->in_units('days');
+ while ($days) {
+ $base_date->add_duration($day_dur);
+ if ( $self->is_holiday($base_date) ) {
+ next;
+ } else {
+ --$days;
+ }
+ }
+ }
+ if ( $unit eq 'hours' ) {
+ my $dt = $base_date->clone()->subtract( days => 1 );
+ if ( $self->is_holiday($dt) ) {
+ $base_date->set_hour($return_by_hour); # Staffs specific
+ }
+ }
+ return $base_date;
+ } else { # Days
+ return $base_date + $add_duration;
+ }
+}
+
+sub is_holiday {
+ my ( $self, $dt ) = @_;
+ my $dow = $dt->day_of_week;
+ if ( $dow == 7 ) {
+ $dow = 0;
+ }
+ if ( $self->{weekly_closed_days}->[$dow] == 1 ) {
+ return 1;
+ }
+ $dt->truncate( to => 'days' );
+ my $day = $dt->day;
+ my $month = $dt->month;
+ if ( exists $self->{day_month_closed_days}->{$month}->{$day} ) {
+ return 1;
+ }
+ if ( $self->{exception_holidays}->contains($dt) ) {
+ return 1;
+ }
+ if ( $self->{single_holidays}->contains($dt) ) {
+ return 1;
+ }
+
+ # damn have to go to work after all
+ return 0;
+}
+
+sub days_between {
+ my $self = shift;
+ my $start_dt = shift;
+ my $end_dt = shift;
+ $start_dt->truncate( to => 'hours' );
+ $end_dt->truncate( to => 'hours' );
+
+ # start and end should not be closed days
+ my $duration = $end_dt - $start_dt;
+ $start_dt->truncate( to => 'days' );
+ $end_dt->truncate( to => 'days' );
+ while ( DateTime->compare( $start_dt, $end_dt ) == -1 ) {
+ $start_dt->add( days => 1 );
+ if ( $self->is_holiday($start_dt) ) {
+ $duration->subtract( days => 1 );
+ }
+ }
+ return $duration;
+
+}
+
+sub _mockinit {
+ my $self = shift;
+ $self->{weekly_closed_days} = [ 1, 0, 0, 0, 0, 0, 0 ]; # Sunday only
+ $self->{day_month_closed_days} = { 6 => { 16 => 1, } };
+ my $dates = [];
+ $self->{exception_holidays} =
+ DateTime::Set->from_datetimes( dates => $dates );
+ my $special = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 1,
+ time_zone => 'Europe/London',
+ );
+ push @{$dates}, $special;
+ $self->{single_holidays} = DateTime::Set->from_datetimes( dates => $dates );
+ $self->{days_mode} = 'Calendar';
+ return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Koha::Calendar - Object containing a branches calendar
+
+=head1 VERSION
+
+This documentation refers to Koha::Calendar version 0.0.1
+
+=head1 SYNOPSIS
+
+ use Koha::Calendat
+
+ my $c = Koha::Calender->new( branchcode => 'MAIN' );
+ my $dt = DateTime->now();
+
+ # are we open
+ $open = $c->is_holiday($dt);
+ # when will item be due if loan period = $dur (a DateTime::Duration object)
+ $duedate = $c->addDate($dt,$dur,'days');
+
+
+=head1 DESCRIPTION
+
+ Implements those features of C4::Calendar needed for Staffs Rolling Loans
+
+=head1 METHODS
+
+=head2 new : Create a calendar object
+
+my $calendar = Koha::Calendar->new( branchcode => 'MAIN' );
+
+The option branchcode is required
+
+
+=head2 addDate
+
+ my $dt = $calendar->addDate($date, $dur, $unit)
+
+C<$date> is a DateTime object representing the starting date of the interval.
+
+C<$offset> is a DateTime::Duration to add to it
+
+C<$unit> is a string value 'days' or 'hours' toflag granularity of duration
+
+Currently unit is only used to invoke Staffs return Monday at 10 am rule this
+parameter will be removed when issuingrules properly cope with that
+
+
+=head2 is_holiday
+
+$yesno = $calendar->is_holiday($dt);
+
+passed at DateTime object returns 1 if it is a closed day
+0 if not according to the calendar
+
+=head2 days_between
+
+$duration = $calendar->days_between($start_dt, $end_dt);
+
+Passed two dates returns a DateTime::Duration object measuring the length between them
+ignoring closed days
+
+=head1 DIAGNOSTICS
+
+Will croak if not passed a branchcode in new
+
+=head1 BUGS AND LIMITATIONS
+
+This only contains a limited subset of the functionality in C4::Calendar
+Only enough to support Staffs Rolling loans
+
+=head1 AUTHOR
+
+Colin Campbell colin.campbell@ptfs-europe.com
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright (c) 2011 PTFS-Europe Ltd All rights reserved
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
--- /dev/null
+package Koha::DateUtils;
+
+# Copyright (c) 2011 PTFS-Europe Ltd.
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA 02111-1307 USA
+
+use strict;
+use warnings;
+use 5.010;
+use DateTime;
+use DateTime::Format::DateParse;
+use C4::Context;
+
+use base 'Exporter';
+use version; our $VERSION = qv('1.0.0');
+
+our @EXPORT = (
+ qw( dt_from_string output_pref format_sqldatetime output_pref_due format_sqlduedatetime)
+);
+
+=head1 DateUtils
+
+Koha::DateUtils - Transitional wrappers to ease use of DateTime
+
+=head1 DESCRIPTION
+
+Koha has historically only used dates not datetimes and been content to
+handle these as strings. It also has confused formatting with actual dates
+this is a temporary module for wrappers to hide the complexity of switch to DateTime
+
+=cut
+
+=head2 dt_ftom_string
+
+$dt = dt_from_string($date_string, [$format, $timezone ]);
+
+Passed a date string returns a DateTime object format and timezone default
+to the system preferences. If the date string is empty DateTime->now is returned
+
+=cut
+
+sub dt_from_string {
+ my ( $date_string, $date_format, $tz ) = @_;
+ if ( !$tz ) {
+ $tz = C4::Context->tz;
+ }
+ if ( !$date_format ) {
+ $date_format = C4::Context->preference('dateformat');
+ }
+ if ($date_string) {
+ if ( ref($date_string) eq 'DateTime' ) { # already a dt return it
+ return $date_string;
+ }
+
+ if ( $date_format eq 'metric' ) {
+ $date_string =~ s#-#/#g;
+ $date_string =~ s/^00/01/; # system allows the 0th of the month
+ $date_string =~ s#^(\d{1,2})/(\d{1,2})#$2/$1#;
+ } else {
+ if ( $date_format eq 'iso' ) {
+ $date_string =~ s/-00/-01/;
+ if ( $date_string =~ m/^0000-0/ ) {
+ return; # invalid date in db
+ }
+ } elsif ( $date_format eq 'us' ) {
+ $date_string =~ s#-#/#g;
+ $date_string =~ s[/00/][/01/];
+ } elsif ( $date_format eq 'sql' ) {
+ $date_string =~
+s/(\d{4})(\d{2})(\d{2})\s+(\d{2})(\d{2})(\d{2})/$1-$2-$3T$4:$5:$6/;
+ $date_string =~ s/00T/01T/;
+ }
+ }
+ return DateTime::Format::DateParse->parse_datetime( $date_string,
+ $tz->name() );
+ }
+ return DateTime->now( time_zone => $tz );
+
+}
+
+=head2 output_pref
+
+$date_string = output_pref($dt, [$format] );
+
+Returns a string containing the time & date formatted as per the C4::Context setting
+
+A second parameter allows overriding of the syspref value. This is for testing only
+In usage use the DateTime objects own methods for non standard formatting
+
+=cut
+
+sub output_pref {
+ my $dt = shift;
+ my $force_pref = shift; # if testing we want to override Context
+ my $pref =
+ defined $force_pref ? $force_pref : C4::Context->preference('dateformat');
+ given ($pref) {
+ when (/^iso/) {
+ return $dt->strftime('%Y-%m-%d %H:%M');
+ }
+ when (/^metric/) {
+ return $dt->strftime('%d/%m/%Y %H:%M');
+ }
+ when (/^us/) {
+ return $dt->strftime('%m/%d/%Y %H:%M');
+ }
+ default {
+ return $dt->strftime('%Y-%m-%d %H:%M');
+ }
+
+ }
+ return;
+}
+
+=head2 output_pref_due
+
+$date_string = output_pref_due($dt, [$format] );
+
+Returns a string containing the time & date formatted as per the C4::Context setting
+
+A second parameter allows overriding of the syspref value. This is for testing only
+In usage use the DateTime objects own methods for non standard formatting
+
+This is effectivelyt a wrapper around output_pref for due dates
+the time portion is stripped if it is '23:59'
+
+=cut
+
+sub output_pref_due {
+ my $disp_str = output_pref(@_);
+ $disp_str =~ s/ 23:59//;
+ return $disp_str;
+}
+
+=head2 format_sqldatetime
+
+$string = format_sqldatetime( $string_as_returned_from_db );
+
+a convenience routine for calling dt_from_string and formatting the result
+with output_pref as it is a frequent activity in scripts
+
+=cut
+
+sub format_sqldatetime {
+ my $str = shift;
+ my $force_pref = shift; # if testing we want to override Context
+ if ( defined $str && $str =~ m/^\d{4}-\d{2}-\d{2}/ ) {
+ my $dt = dt_from_string( $str, 'sql' );
+ $dt->truncate( to => 'minutes' );
+ return output_pref( $dt, $force_pref );
+ }
+ return q{};
+}
+
+=head2 format_sqlduedatetime
+
+$string = format_sqldatetime( $string_as_returned_from_db );
+
+a convenience routine for calling dt_from_string and formatting the result
+with output_pref_due as it is a frequent activity in scripts
+
+=cut
+
+sub format_sqlduedatetime {
+ my $str = shift;
+ my $force_pref = shift; # if testing we want to override Context
+ if ( defined $str && $str =~ m/^\d{4}-\d{2}-\d{2}/ ) {
+ my $dt = dt_from_string( $str, 'sql' );
+ $dt->truncate( to => 'minutes' );
+ return output_pref_due( $dt, $force_pref );
+ }
+ return q{};
+}
+
+1;
}
# save the values entered
elsif ($op eq 'add') {
- my $sth_search = $dbh->prepare("SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?");
- my $sth_insert = $dbh->prepare("INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, reservesallowed, issuelength, hardduedate, hardduedatecompare, fine, finedays, firstremind, chargeperiod,rentaldiscount) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
- my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, reservesallowed=?, issuelength=?, hardduedate=?, hardduedatecompare=?, rentaldiscount=? WHERE branchcode=? AND categorycode=? AND itemtype=?");
+ my $sth_search = $dbh->prepare('SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?');
+ my $sth_insert = $dbh->prepare('INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, reservesallowed, issuelength, lengthunit, hardduedate, hardduedatecompare, fine, finedays, firstremind, chargeperiod,rentaldiscount) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
+ my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, reservesallowed=?, issuelength=?, lengthunit = ?, hardduedate=?, hardduedatecompare=?, rentaldiscount=? WHERE branchcode=? AND categorycode=? AND itemtype=?");
my $br = $branch; # branch
my $bor = $input->param('categorycode'); # borrower category
$maxissueqty =~ s/\s//g;
$maxissueqty = undef if $maxissueqty !~ /^\d+/;
my $issuelength = $input->param('issuelength');
+ my $lengthunit = $input->param('lengthunit');
my $hardduedate = $input->param('hardduedate');
$hardduedate = format_date_in_iso($hardduedate);
my $hardduedatecompare = $input->param('hardduedatecompare');
$sth_search->execute($br,$bor,$cat);
my $res = $sth_search->fetchrow_hashref();
if ($res->{total}) {
- $sth_update->execute($fine, $finedays,$firstremind, $chargeperiod, $maxissueqty, $renewalsallowed,$reservesallowed, $issuelength,$hardduedate,$hardduedatecompare,$rentaldiscount, $br,$bor,$cat);
+ $sth_update->execute($fine, $finedays,$firstremind, $chargeperiod, $maxissueqty, $renewalsallowed,$reservesallowed, $issuelength,$lengthunit, $hardduedate,$hardduedatecompare,$rentaldiscount, $br,$bor,$cat);
} else {
- $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed,$reservesallowed,$issuelength,$hardduedate,$hardduedatecompare,$fine,$finedays,$firstremind,$chargeperiod,$rentaldiscount);
+ $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed,$reservesallowed,$issuelength,$lengthunit,$hardduedate,$hardduedatecompare,$fine,$finedays,$firstremind,$chargeperiod,$rentaldiscount);
}
}
elsif ($op eq "set-branch-defaults") {
use C4::VirtualShelves;
use C4::XSLT;
use C4::Images;
+use Koha::DateUtils;
# use Smart::Comments;
$item->{imageurl} = defined $item->{itype} ? getitemtypeimagelocation('intranet', $itemtypes->{ $item->{itype} }{imageurl})
: '';
- foreach (qw(datedue datelastseen onloan)) {
+ foreach (qw(datelastseen onloan)) {
$item->{$_} = format_date($item->{$_});
- }
+ }
+ $item->{datedue} = format_sqldatetime($item->{datedue});
# item damaged, lost, withdrawn loops
$item->{itemlostloop} = GetAuthorisedValues($authvalcode_items_itemlost, $item->{itemlost}) if $authvalcode_items_itemlost;
if ($item->{damaged}) {
use C4::Circulation; # GetBiblioIssues
use C4::Biblio; # GetBiblio GetBiblioFromItemNumber
-use C4::Dates qw/format_date/;
use C4::Search; # enabled_staff_search_views
+use Koha::DateUtils;
my $query = new CGI;
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
%{$biblio[0]},
);
}
-foreach (@$issues){
- $_->{date_due} = format_date($_->{date_due});
- $_->{issuedate} = format_date($_->{issuedate});
- $_->{returndate} = format_date($_->{returndate});
- $_->{lastreneweddate} = format_date($_->{lastreneweddate});
+foreach (@{$issues}){
+ $_->{date_due} = format_sqldatetime($_->{date_due});
+ $_->{issuedate} = format_sqldatetime($_->{issuedate});
+ $_->{returndate} = format_sqldatetime($_->{returndate});
+ $_->{lastreneweddate} = format_sqldatetime($_->{lastreneweddate});
}
$template->param(
total => scalar @$issues,
use C4::Members; # to use GetMember
use C4::Search; # enabled_staff_search_views
use C4::Members qw/GetHideLostItemsPreference/;
+use Koha::DateUtils;
my $query=new CGI;
use CGI;
use C4::Output;
use C4::Auth;
-use C4::Dates qw/format_date/;
use C4::Overdues; # AddNotifyLine
use C4::Biblio;
use C4::Koha;
use C4::Debug;
use C4::Branch;
+use Data::Dumper;
=head1 branchoverdues.pl
my @overduesloop;
my @getoverdues = GetOverduesForBranch( $default, $location );
-use Data::Dumper;
$debug and warn "HERE : $default / $location" . Dumper(@getoverdues);
# search for location authorised value
my ($tag,$subfield) = GetMarcFromKohaField('items.location','');
if ($record){
$overdueforbranch{'subtitle'} = GetRecordValue('subtitle',$record,'')->[0]->{subfield};
}
- $overdueforbranch{'date_due'} = format_date( $num->{'date_due'} );
+ my $dt = dt_from_string($num->{date_due}, 'sql');
+ $overdueforbranch{'date_due'} = output_pref($dt);
$overdueforbranch{'title'} = $num->{'title'};
$overdueforbranch{'description'} = $num->{'description'};
$overdueforbranch{'barcode'} = $num->{'barcode'};
# initiate the templates for the overdueloop
$template->param(
overduesloop => \@overduesloop,
- show_date => format_date(C4::Dates->today('iso')),
location => $location,
);
# Copyright 2000-2002 Katipo Communications
# copyright 2010 BibLibre
+# Copyright 2011 PTFS-Europe Ltd.
#
# This file is part of Koha.
#
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use strict;
-#use warnings; FIXME - Bug 2505
+use warnings;
use CGI;
use C4::Output;
use C4::Auth qw/:DEFAULT get_session/;
use C4::Context;
use CGI::Session;
use C4::Members::Attributes qw(GetBorrowerAttributes);
+use Koha::DateUtils;
use Date::Calc qw(
Today
if($duedatespec_allow){
if ($duedatespec) {
if ($duedatespec =~ C4::Dates->regexp('syspref')) {
- my $tempdate = C4::Dates->new($duedatespec);
-# if ($tempdate and $tempdate->output('iso') gt C4::Dates->new()->output('iso')) {
-# # i.e., it has to be later than today/now
- $datedue = $tempdate;
-# } else {
-# $invalidduedate = 1;
-# $template->param(IMPOSSIBLE=>1, INVALID_DATE=>$duedatespec);
-# }
+ $datedue = dt_from_string($duedatespec);
+ $datedue->set_hour(23);
+ $datedue->set_minute(59);
} else {
$invalidduedate = 1;
$template->param(IMPOSSIBLE=>1, INVALID_DATE=>$duedatespec);
$totalprice += $it->{'replacementprice'};
$it->{'itemtype'} = $itemtypeinfo->{'description'};
$it->{'itemtype_image'} = $itemtypeinfo->{'imageurl'};
- $it->{'dd'} = format_date($it->{'date_due'});
- $it->{'displaydate'} = format_date($it->{'issuedate'});
- $it->{'od'} = ( $it->{'date_due'} lt $todaysdate ) ? 1 : 0 ;
+ $it->{'dd'} = output_pref($it->{'date_due'});
+ $it->{'displaydate'} = output_pref($it->{'issuedate'});
+ #$it->{'od'} = ( $it->{'date_due'} lt $todaysdate ) ? 1 : 0 ;
+ $it->{'od'} = $it->{'overdue'};
($it->{'author'} eq '') and $it->{'author'} = ' ';
$it->{'renew_failed'} = $renew_failed{$it->{'itemnumber'}};
use C4::Branch;
use C4::Debug;
use C4::Dates qw/format_date format_date_in_iso/;
-use Date::Calc qw/Today/;
use Text::CSV_XS;
+use Koha::DateUtils;
+use DateTime;
my $input = new CGI;
my $order = $input->param('order') || '';
my $op = $input->param('op') || '';
my $dateduefrom = format_date_in_iso($input->param( 'dateduefrom' )) || '';
my $datedueto = format_date_in_iso($input->param( 'datedueto' )) || '';
+# FIXME This is a kludge to include times
+if ($datedueto) {
+ $datedueto .= ' 23:59';
+}
+if ($dateduefrom) {
+ $dateduefrom .= ' 00:00';
+}
+# kludge end
my $isfiltered = $op =~ /apply/i && $op =~ /filter/i;
my $noreport = C4::Context->preference('FilterBeforeOverdueReport') && ! $isfiltered && $op ne "csv";
# FIX 2: ensure there are indexes for columns participating in the WHERE clauses, where feasible/reasonable
- my $todaysdate = sprintf("%-04.4d-%-02.2d-%02.2d", Today());
+ my $today_dt = DateTime->now(time_zone => C4::Context->tz);
+ $today_dt->truncate(to => 'minutes');
+ my $todaysdate = $today_dt->strftime('%Y-%m-%d %H:%M');
$bornamefilter =~s/\*/\%/g;
$bornamefilter =~s/\?/\_/g;
my @displayvalues = map { $_->[1] } @{ $pattrs->{$pattr_filter->{code}} }; # grab second value from each subarray
push @patron_attr_value_loop, { value => join(', ', sort { lc $a cmp lc $b } @displayvalues) };
}
+ my $dt = dt_from_string($data->{date_due}, 'sql');
push @overduedata, {
- duedate => format_date($data->{date_due}),
+ duedate => output_pref($dt),
borrowernumber => $data->{borrowernumber},
barcode => $data->{barcode},
itemnum => $data->{itemnumber},
$template->param(
csv_param_string => $csv_param_string,
- todaysdate => format_date($todaysdate),
+ todaysdate => output_pref($today_dt),
overdueloop => \@overduedata,
nnoverdue => scalar(@overduedata),
noverdue_is_plural => scalar(@overduedata) != 1,
# 2006 SAN-OP
# 2007-2010 BibLibre, Paul POULAIN
# 2010 Catalyst IT
+# 2011 PTFS-Europe Ltd.
#
# This file is part of Koha.
#
#use warnings; FIXME - Bug 2505
use CGI;
+use DateTime;
use C4::Context;
use C4::Auth qw/:DEFAULT get_session/;
use C4::Output;
use C4::Circulation;
-use C4::Dates qw/format_date/;
-use Date::Calc qw/Add_Delta_Days/;
-use C4::Calendar;
use C4::Print;
use C4::Reserves;
use C4::Biblio;
use C4::Branch; # GetBranches GetBranchName
use C4::Koha; # FIXME : is it still useful ?
use C4::RotatingCollections;
+use Koha::DateUtils;
+use Koha::Calendar;
my $query = new CGI;
my $dotransfer = $query->param('dotransfer');
my $canceltransfer = $query->param('canceltransfer');
my $dest = $query->param('dest');
-my $calendar = C4::Calendar->new( branchcode => $userenv_branch );
+my $calendar = Koha::Calendar->new( branchcode => $userenv_branch );
#dropbox: get last open day (today - 1)
-my $today = C4::Dates->new();
-my $today_iso = $today->output('iso');
+my $today = DateTime->now( time_zone => C4::Context->tz());
my $dropboxdate = $calendar->addDate($today, -1);
if ($dotransfer){
# An item has been returned to a branch other than the homebranch, and the librarian has chosen to initiate a transfer
);
if ($returned) {
- my $duedate = $issueinformation->{'date_due'};
+ my $time_now = DateTime->now( time_zone => C4::Context->tz )->truncate( to => 'minutes');
+ my $duedate = $issueinformation->{date_due}->strftime('%Y-%m-%d %H:%M');
$returneditems{0} = $barcode;
$riborrowernumber{0} = $borrower->{'borrowernumber'};
$riduedate{0} = $duedate;
$input{borrowernumber} = $borrower->{'borrowernumber'};
$input{duedate} = $duedate;
- $input{return_overdue} = 1 if ($duedate and $duedate lt $today->output('iso'));
+ $input{return_overdue} = 1 if (DateTime->compare($issueinformation->{date_due}, $time_now) == -1);
push( @inputloop, \%input );
if ( C4::Context->preference("FineNotifyAtCheckin") ) {
elsif ( $code eq 'Wrongbranch' ) {
}
elsif ( $code eq 'Debarred' ) {
- $err{debarred} = format_date( $messages->{'Debarred'} );
+ $err{debarred} = $messages->{'Debarred'};
$err{debarcardnumber} = $borrower->{cardnumber};
$err{debarborrowernumber} = $borrower->{borrowernumber};
$err{debarname} = "$borrower->{firstname} $borrower->{surname}";
{
my $biblio = GetBiblioFromItemNumber( $item->{'itemnumber'});
push @itemloop, {
- duedate => format_date($item->{'date_due'}),
+ duedate => format_sqldatetime($item->{date_due}),
biblionum => $biblio->{'biblionumber'},
barcode => $biblio->{'barcode'},
title => $biblio->{'title'},
riborfirstname => $borrower->{'firstname'}
);
}
-
#set up so only the last 8 returned items display (make for faster loading pages)
my $returned_counter = ( C4::Context->preference('numReturnedItemsToShow') ) ? C4::Context->preference('numReturnedItemsToShow') : 8;
my $count = 0;
my %ri;
if ( $count++ < $returned_counter ) {
my $bar_code = $returneditems{$_};
- my $duedate = $riduedate{$_};
- if ($duedate) {
- my @tempdate = split( /-/, $duedate );
- $ri{year} = $tempdate[0];
- $ri{month} = $tempdate[1];
- $ri{day} = $tempdate[2];
- $ri{duedate} = format_date($duedate);
+ if ($riduedate{$_}) {
+ my $duedate = dt_from_string( $riduedate{$_}, 'sql');
+ $ri{year} = $duedate->year();
+ $ri{month} = $duedate->month();
+ $ri{day} = $duedate->day();
+ $ri{hour} = $duedate->hour();
+ $ri{minute} = $duedate->minute();
+ $ri{duedate} = output_pref($duedate);
my ($b) = GetMemberDetails( $riborrowernumber{$_}, 0 );
- $ri{return_overdue} = 1 if ($duedate lt $today->output('iso'));
+ $ri{return_overdue} = 1 if (DateTime->compare($duedate, DateTime->now()) == -1 );
$ri{borrowernumber} = $b->{'borrowernumber'};
$ri{borcnum} = $b->{'cardnumber'};
$ri{borfirstname} = $b->{'firstname'};
}
push @riloop, \%ri;
}
-
$template->param(
riloop => \@riloop,
genbrname => $branches->{$userenv_branch}->{'branchname'},
errmsgloop => \@errmsgloop,
exemptfine => $exemptfine,
dropboxmode => $dropboxmode,
- dropboxdate => $dropboxdate->output(),
+ dropboxdate => output_pref($dropboxdate),
overduecharges => $overduecharges,
soundon => C4::Context->preference("SoundOn"),
);
--- /dev/null
+alter table issuingrules add column lengthunit varchar(10) default 'days' after issuelength;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use C4::Context;
+
+my $dbh = C4::Context->dbh;
+
+$dbh->do("ALTER TABLE issues CHANGE date_due date_due datetime");
+$dbh->do("ALTER TABLE issues CHANGE returndate returndate datetime");
+$dbh->do("ALTER TABLE issues CHANGE lastreneweddate lastreneweddate datetime");
+$dbh->do("ALTER TABLE issues CHANGE issuedate issuedate datetime");
+$dbh->do("ALTER TABLE old_issues CHANGE date_due date_due datetime");
+$dbh->do("ALTER TABLE old_issues CHANGE returndate returndate datetime");
+$dbh->do("ALTER TABLE old_issues CHANGE lastreneweddate lastreneweddate datetime");
+$dbh->do("ALTER TABLE old_issues CHANGE issuedate issuedate datetime");
+$dbh->do(q{update issues set date_due = addtime(date_due, '0 23:0:0') where hour(date_due) = 0});
CREATE TABLE `issues` ( -- information related to check outs or issues
`borrowernumber` int(11), -- foreign key, linking this to the borrowers table for the patron this item was checked out to
`itemnumber` int(11), -- foreign key, linking this to the items table for the item that was checked out
- `date_due` date default NULL, -- date the item is due (yyyy-mm-dd)
+ `date_due` datetime default NULL, -- datetime the item is due (yyyy-mm-dd hh:mm::ss)
`branchcode` varchar(10) default NULL, -- foreign key, linking to the branches table for the location the item was checked out
`issuingbranch` varchar(18) default NULL,
- `returndate` date default NULL, -- date the item was returned, will be NULL until moved to old_issues
- `lastreneweddate` date default NULL, -- date the item was last renewed
+ `returndate` datetime default NULL, -- date the item was returned, will be NULL until moved to old_issues
+ `lastreneweddate` datetime default NULL, -- date the item was last renewed
`return` varchar(4) default NULL,
`renewals` tinyint(4) default NULL, -- lists the number of times the item was renewed
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, -- the date and time this record was last touched
- `issuedate` date default NULL, -- date the item was checked out or issued
+ `issuedate` datetime default NULL, -- date the item was checked out or issued
KEY `issuesborridx` (`borrowernumber`),
KEY `bordate` (`borrowernumber`,`timestamp`),
CONSTRAINT `issues_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE RESTRICT ON UPDATE CASCADE,
CREATE TABLE `old_issues` ( -- lists items that were checked out and have been returned
`borrowernumber` int(11) default NULL, -- foreign key, linking this to the borrowers table for the patron this item was checked out to
`itemnumber` int(11) default NULL, -- foreign key, linking this to the items table for the item that was checked out
- `date_due` date default NULL, -- date the item is due (yyyy-mm-dd)
+ `date_due` datetime default NULL, -- date the item is due (yyyy-mm-dd)
`branchcode` varchar(10) default NULL, -- foreign key, linking to the branches table for the location the item was checked out
`issuingbranch` varchar(18) default NULL,
- `returndate` date default NULL, -- date the item was returned
- `lastreneweddate` date default NULL, -- date the item was last renewed
+ `returndate` datetime default NULL, -- date the item was returned
+ `lastreneweddate` datetime default NULL, -- date the item was last renewed
`return` varchar(4) default NULL,
`renewals` tinyint(4) default NULL, -- lists the number of times the item was renewed
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, -- the date and time this record was last touched
- `issuedate` date default NULL, -- date the item was checked out or issued
+ `issuedate` datetime default NULL, -- date the item was checked out or issued
KEY `old_issuesborridx` (`borrowernumber`),
KEY `old_issuesitemidx` (`itemnumber`),
KEY `old_bordate` (`borrowernumber`,`timestamp`),
SetVersion($DBversion);
}
-
$DBversion = "3.07.00.029";
if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
my $installer = C4::Installer->new();
SetVersion($DBversion);
}
+$DBversion = "XXX";
+if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
+ $dbh->do("ALTER TABLE issues CHANGE date_due date_due datetime");
+ $dbh->do("ALTER TABLE issues CHANGE returndate returndate datetime");
+ $dbh->do("ALTER TABLE issues CHANGE lastreneweddate lastreneweddate datetime");
+ $dbh->do("ALTER TABLE issues CHANGE issuedate issuedate datetime");
+ $dbh->do("ALTER TABLE old_issues CHANGE date_due date_due datetime");
+ $dbh->do("ALTER TABLE old_issues CHANGE returndate returndate datetime");
+ $dbh->do("ALTER TABLE old_issues CHANGE lastreneweddate lastreneweddate datetime");
+ $dbh->do("ALTER TABLE old_issues CHANGE issuedate issuedate datetime");
+ print "Upgrade to $DBversion done (Setting up issues tables for hourly loans)\n";
+ SetVersion($DBversion);
+}
+
=head1 FUNCTIONS
=head2 DropAllForeignKeys($table)
- Only allow patrons to renew their own books on the OPAC if they have less than
- pref: OPACFineNoRenewals
class: currency
- - '[% local_currency %] in fines (set a large value to always allow renewal).'
+ - '[% local_currency %] in fines (leave blank to disable).'
-
- pref: OPACViewOthersSuggestions
choices:
<th>Patron category</th>
<th>Item type</th>
<th>Current checkouts allowed</th>
- <th>Loan period (day)</th>
+ <th>Loan period</th>
+ <th>Unit</th>
<th>Hard due date</th>
<th>Fine amount</th>
<th>Fine charging interval</th>
<th>Suspension in days (day)</th>
<th>Renewals allowed (count)</th>
<th>Holds allowed (count)</th>
- <th>Rental discount (%)</th>
+ <th>Rental discount (%)</th>
<th> </th>
</tr>
[% FOREACH rule IN rules %]
[% END %]
</td>
<td>[% rule.issuelength %]</td>
+ <td>
+ [% rule.lengthunit %]
+ </td>
<td>[% IF ( rule.hardduedate ) %]
[% IF ( rule.hardduedatebefore ) %]before [% rule.hardduedate %]</td>
[% ELSE %][% IF ( rule.hardduedateexact ) %]on [% rule.hardduedate %]</td>
</td>
<td><input name="maxissueqty" size="3" /></td>
<td><input name="issuelength" size="3" /> </td>
+ <td>
+ <select name="lengthunit">
+ <option value="days" selected>Days</option>
+ <option value="hours">Hours</option>
+ </select>
+ </td>
<td><select name="hardduedatecompare">
<option value="-1">Before</option>
<option value="0">Exactly on</option>
+[% USE KohaDates %]
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha › Circulation › Check In [% title |html %]</title>
[% INCLUDE 'doc-head-close.inc' %]
<p class="problem">Item is withdrawn.</p>
[% END %]
[% IF ( errmsgloo.debarred ) %]
- <p class="problem"><a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% errmsgloo.debarborrowernumber %]">[% errmsgloo.debarname %]([% errmsgloo.debarcardnumber %])</a> is now debarred until [% errmsgloo.debarred %] </p>
+ <p class="problem"><a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% errmsgloo.debarborrowernumber %]">[% errmsgloo.debarname %]([% errmsgloo.debarcardnumber %])</a> is now debarred until [% errmsgloo.debarred | $KohaDates %] </p>
[% END %]
[% END %]
[% IF ( soundon ) %]
<div id="finesholdsissues" class="toptabs">
<ul>
- <li><a href="/cgi-bin/koha/members/moremember.pl#checkedout">[% issuecount %] Checkout(s)</a></li>
- [% IF ( relissuecount ) %]
+ <li><a href="/cgi-bin/koha/members/moremember.pl#checkedout">[% issueloop.size %] Checkout(s)</a></li>
+ [% IF relissueloop.size %]
<li><a href="/cgi-bin/koha/members/moremember.pl#relissues">Relatives' Checkouts</a></li>
[% END %]
<li><a href="/cgi-bin/koha/members/moremember.pl#finesandcharges">Fines & Charges</a></li>
</div>
-[% IF ( relissuecount ) %]
+[% IF relissueloop %]
<div id="relissues">
<table id="relissuest">
<thead>
#use Smart::Comments;
#use Data::Dumper;
+use DateTime;
+use Koha::DateUtils;
use vars qw($debug);
my $dbh = C4::Context->dbh;
-my $input = new CGI;
+my $input = CGI->new;
$debug or $debug = $input->param('debug') || 0;
my $print = $input->param('print');
my $override_limit = $input->param("override_limit") || 0;
if ( @borrowernumbers ) {
$relissue = GetPendingIssues(@borrowernumbers);
}
-my $issuecount = @{$issue};
-my $relissuecount = @{$relissue};
my $roaddetails = &GetRoadTypeDetails( $data->{'streettype'} );
-my $today = POSIX::strftime("%Y-%m-%d", localtime); # iso format
-my @issuedata;
+my $today = DateTime->now( time_zone => C4::Context->tz);
+$today->truncate(to => 'days');
my @borrowers_with_issues;
my $overdues_exist = 0;
my $totalprice = 0;
-my @issuedata = build_issue_data($issue, $issuecount);
-my @relissuedata = build_issue_data($relissue, $relissuecount);
-
-sub build_issue_data {
- my $issue = shift;
- my $issuecount = shift;
-
- my $localissue;
-
- for ( my $i = 0 ; $i < $issuecount ; $i++ ) {
- my $datedue = $issue->[$i]{'date_due'};
- my $issuedate = $issue->[$i]{'issuedate'};
- $issue->[$i]{'date_due'} = C4::Dates->new($issue->[$i]{'date_due'}, 'iso')->output('syspref');
- $issue->[$i]{'issuedate'} = C4::Dates->new($issue->[$i]{'issuedate'},'iso')->output('syspref');
- my $biblionumber = $issue->[$i]{'biblionumber'};
- $issue->[$i]{'issuingbranchname'} = GetBranchName($issue->[$i]{'branchcode'});
- my %row = %{ $issue->[$i] };
- $totalprice += $issue->[$i]{'replacementprice'};
- $row{'replacementprice'} = $issue->[$i]{'replacementprice'};
- # item lost, damaged loops
- if ($row{'itemlost'}) {
- my $fw = GetFrameworkCode($issue->[$i]{'biblionumber'});
- my $category = GetAuthValCode('items.itemlost',$fw);
- my $lostdbh = C4::Context->dbh;
- my $sth = $lostdbh->prepare("select lib from authorised_values where category=? and authorised_value =? ");
- $sth->execute($category, $row{'itemlost'});
- my $loststat = $sth->fetchrow;
- if ($loststat) {
- $row{'itemlost'} = $loststat;
- }
- }
- if ($row{'damaged'}) {
- my $fw = GetFrameworkCode($issue->[$i]{'biblionumber'});
- my $category = GetAuthValCode('items.damaged',$fw);
- my $damageddbh = C4::Context->dbh;
- my $sth = $damageddbh->prepare("select lib from authorised_values where category=? and authorised_value =? ");
- $sth->execute($category, $row{'damaged'});
- my $damagedstat = $sth->fetchrow;
- if ($damagedstat) {
- $row{'itemdamaged'} = $damagedstat;
- }
- }
- # end lost, damaged
- if ( $datedue lt $today ) {
- $overdues_exist = 1;
- $row{'red'} = 1;
- }
- if ( $issuedate eq $today ) {
- $row{'today'} = 1;
- }
-
- #find the charge for an item
- my ( $charge, $itemtype ) =
- GetIssuingCharges( $issue->[$i]{'itemnumber'}, $issue->[$i]{'borrowernumber'} );
-
- my $itemtypeinfo = getitemtypeinfo($itemtype);
- $row{'itemtype_description'} = $itemtypeinfo->{description};
- $row{'itemtype_image'} = $itemtypeinfo->{imageurl};
-
- $row{'charge'} = sprintf( "%.2f", $charge );
-
- my ( $renewokay,$renewerror ) = CanBookBeRenewed( $issue->[$i]{'borrowernumber'}, $issue->[$i]{'itemnumber'}, $override_limit );
- $row{'norenew'} = !$renewokay;
- $row{'can_confirm'} = ( !$renewokay && $renewerror ne 'on_reserve' );
- $row{"norenew_reason_$renewerror"} = 1 if $renewerror;
- $row{'renew_failed'} = $renew_failed{ $issue->[$i]{'itemnumber'} };
- $row{'return_failed'} = $return_failed{$issue->[$i]{'barcode'}};
- push( @$localissue, \%row );
- }
- return $localissue;
-}
+my @issuedata = build_issue_data($issue);
+my @relissuedata = build_issue_data($relissue);
### ###############################################################################
totaldue_raw => $total,
issueloop => @issuedata,
relissueloop => @relissuedata,
- issuecount => $issuecount,
- relissuecount => $relissuecount,
overdues_exist => $overdues_exist,
error => $error,
$error => 1,
);
output_html_with_http_headers $input, $cookie, $template->output;
+
+sub build_issue_data {
+ my $issues = shift;
+
+ my $localissue;
+
+ foreach my $issue ( @{$issues} ) {
+
+ # Getting borrower details
+ my $memberdetails = GetMemberDetails( $issue->{borrowernumber} );
+ $issue->{borrowername} =
+ $memberdetails->{firstname} . ' ' . $memberdetails->{surname};
+ $issue->{cardnumber} = $memberdetails->{cardnumber};
+ my $issuedate;
+ if ($issue->{issuedate} ) {
+ $issuedate = $issue->{issuedate}->clone();
+ }
+
+ $issue->{date_due} = output_pref( $issue->{date_due} );
+ $issue->{issuedate} = output_pref( $issue->{issuedate} ) if defined $issue->{issuedate};
+ my $biblionumber = $issue->{biblionumber};
+ my %row = %{$issue};
+ $totalprice += $issue->{replacementprice};
+
+ # item lost, damaged loops
+ if ( $row{'itemlost'} ) {
+ my $fw = GetFrameworkCode( $issue->{biblionumber} );
+ my $category = GetAuthValCode( 'items.itemlost', $fw );
+ my $lostdbh = C4::Context->dbh;
+ my $sth = $lostdbh->prepare(
+"select lib from authorised_values where category=? and authorised_value =? "
+ );
+ $sth->execute( $category, $row{'itemlost'} );
+ my $loststat = $sth->fetchrow;
+ if ($loststat) {
+ $row{'itemlost'} = $loststat;
+ }
+ }
+ if ( $row{'damaged'} ) {
+ my $fw = GetFrameworkCode( $issue->{biblionumber} );
+ my $category = GetAuthValCode( 'items.damaged', $fw );
+ my $damageddbh = C4::Context->dbh;
+ my $sth = $damageddbh->prepare(
+"select lib from authorised_values where category=? and authorised_value =? "
+ );
+ $sth->execute( $category, $row{'damaged'} );
+ my $damagedstat = $sth->fetchrow;
+ if ($damagedstat) {
+ $row{'itemdamaged'} = $damagedstat;
+ }
+ }
+
+ # end lost, damaged
+ if ( $issue->{overdue} ) {
+ $overdues_exist = 1;
+ $row{red} = 1;
+ }
+ if ($issuedate) {
+ $issuedate->truncate( to => 'days' );
+ if ( DateTime->compare( $issuedate, $today ) == 0 ) {
+ $row{today} = 1;
+ }
+ }
+
+ #find the charge for an item
+ my ( $charge, $itemtype ) =
+ GetIssuingCharges( $issue->{itemnumber}, $borrowernumber );
+
+ my $itemtypeinfo = getitemtypeinfo($itemtype);
+ $row{'itemtype_description'} = $itemtypeinfo->{description};
+ $row{'itemtype_image'} = $itemtypeinfo->{imageurl};
+
+ $row{'charge'} = sprintf( "%.2f", $charge );
+
+ my ( $renewokay, $renewerror ) =
+ CanBookBeRenewed( $borrowernumber, $issue->{itemnumber},
+ $override_limit );
+ $row{'norenew'} = !$renewokay;
+ $row{'can_confirm'} = ( !$renewokay && $renewerror ne 'on_reserve' );
+ $row{"norenew_reason_$renewerror"} = 1 if $renewerror;
+ $row{renew_failed} = $renew_failed{ $issue->{itemnumber} };
+ $row{return_failed} = $return_failed{ $issue->{barcode} };
+ push( @{$localissue}, \%row );
+ }
+ return $localissue;
+}
use C4::Members;
use C4::Branch;
use List::MoreUtils qw/any uniq/;
+use Koha::DateUtils;
use C4::Dates qw/format_date/;
use C4::Members::Attributes qw(GetBorrowerAttributes);
$line{title} = $issue->{'title'};
$line{author} = $issue->{'author'};
$line{classification} = $issue->{'classification'} || $issue->{'itemcallnumber'};
- $line{date_due} = format_date($issue->{'date_due'});
- $line{returndate} = format_date($issue->{'returndate'});
- $line{issuedate} = format_date($issue->{'issuedate'});
+ $line{date_due} = format_sqldatetime($issue->{date_due});
+ $line{returndate} = format_sqldatetime($issue->{returndate});
+ $line{issuedate} = format_sqldatetime($issue->{issuedate});
$line{issuingbranch} = GetBranchName($issue->{'branchcode'});
$line{renewals} = $issue->{'renewals'};
$line{barcode} = $issue->{'barcode'};
use C4::Members;
use C4::Members::Messaging;
use C4::Overdues;
-use C4::Dates qw/format_date/;
+use Koha::DateUtils;
# These are defaults for command line options.
);
}
+sub format_date {
+ my $date_string = shift;
+ my $dt=dt_from_string($date_string);
+ return output_pref($dt);
+}
+
1;
__END__
# This script is meant to be run nightly out of cron.
# Copyright 2000-2002 Katipo Communications
+# Copyright 2011 PTFS-Europe Ltd
#
# This file is part of Koha.
#
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-# FIXME: use FinesMode as described or change syspref description
use strict;
-#use warnings; FIXME - Bug 2505
-
-BEGIN {
- # find Koha's Perl modules
- # test carefully before changing this
- use FindBin;
- eval { require "$FindBin::Bin/kohalib.pl" };
-}
-
-use Date::Calc qw/Date_to_Days/;
+use warnings;
+use 5.010;
use C4::Context;
-use C4::Circulation;
use C4::Overdues;
-use C4::Calendar qw(); # don't need any exports from Calendar
-use C4::Biblio;
-use C4::Debug; # supplying $debug and $cgi_debug
use Getopt::Long;
+use Carp;
+use File::Spec;
+
+use Koha::Calendar;
+use Koha::DateUtils;
-my $help = 0;
-my $verbose = 0;
+my $help;
+my $verbose;
my $output_dir;
-GetOptions( 'h|help' => \$help,
- 'v|verbose' => \$verbose,
- 'o|out:s' => \$output_dir,
- );
+GetOptions(
+ 'h|help' => \$help,
+ 'v|verbose' => \$verbose,
+ 'o|out:s' => \$output_dir,
+);
my $usage = << 'ENDUSAGE';
This script calculates and charges overdue fines
ENDUSAGE
-die $usage if $help;
-
-use vars qw(@borrower_fields @item_fields @other_fields);
-use vars qw($fldir $libname $control $mode $delim $dbname $today $today_iso $today_days);
-use vars qw($filename);
-
-CHECK {
- @borrower_fields = qw(cardnumber categorycode surname firstname email phone address citystate);
- @item_fields = qw(itemnumber barcode date_due);
- @other_fields = qw(type days_overdue fine);
- $libname = C4::Context->preference('LibraryName');
- $control = C4::Context->preference('CircControl');
- $mode = C4::Context->preference('finesMode');
- $dbname = C4::Context->config('database');
- $delim = "\t"; # ? C4::Context->preference('delimiter') || "\t";
-
+if ($help) {
+ print $usage;
+ exit;
}
-INIT {
- $debug and print "Each line will contain the following fields:\n",
- "From borrowers : ", join(', ', @borrower_fields), "\n",
- "From items : ", join(', ', @item_fields), "\n",
- "Per overdue: ", join(', ', @other_fields), "\n",
- "Delimiter: '$delim'\n";
-}
+my @borrower_fields =
+ qw(cardnumber categorycode surname firstname email phone address citystate);
+my @item_fields = qw(itemnumber barcode date_due);
+my @other_fields = qw(type days_overdue fine);
+my $libname = C4::Context->preference('LibraryName');
+my $control = C4::Context->preference('CircControl');
+my $mode = C4::Context->preference('finesMode');
+my $delim = "\t"; # ? C4::Context->preference('delimiter') || "\t";
+
+my %is_holiday;
+my $today = DateTime->now( time_zone => C4::Context->tz() );
+my $filename = get_filename($output_dir);
+
+open my $fh, '>>', $filename or croak "Cannot write file $filename: $!";
+print {$fh} join $delim, ( @borrower_fields, @item_fields, @other_fields );
+print {$fh} "\n";
+
+my $counted = 0;
+my $overdues = Getoverdues();
+for my $overdue ( @{$overdues} ) {
+ if ( !defined $overdue->{borrowernumber} ) {
+ carp
+"ERROR in Getoverdues : issues.borrowernumber IS NULL. Repair 'issues' table now! Skipping record.\n";
+ next;
+ }
+ my $borrower = BorType( $overdue->{borrowernumber} );
+ my $branchcode =
+ ( $control eq 'ItemHomeLibrary' ) ? $overdue->{homebranch}
+ : ( $control eq 'PatronLibrary' ) ? $borrower->{branchcode}
+ : $overdue->{branchcode};
+
+# In final case, CircControl must be PickupLibrary. (branchcode comes from issues table here).
+ if ( !exists $is_holiday{$branchcode} ) {
+ $is_holiday{$branchcode} = set_holiday( $branchcode, $today );
+ }
-my $data = Getoverdues();
-my $overdueItemsCounted = 0;
-my %calendars = ();
-$today = C4::Dates->new();
-$today_iso = $today->output('iso');
-$today_days = Date_to_Days(split(/-/,$today_iso));
-if($output_dir){
- $fldir = $output_dir if( -d $output_dir );
-} else {
- $fldir = $ENV{TMPDIR} || "/tmp";
-}
-if (!-d $fldir) {
- warn "Could not write to $fldir ... does not exist!";
-}
-$filename = $dbname;
-$filename =~ s/\W//;
-$filename = $fldir . '/'. $filename . '_' . $today_iso . ".log";
-print "writing to $filename\n" if $verbose;
-open (FILE, ">$filename") or die "Cannot write file $filename: $!";
-print FILE join $delim, (@borrower_fields, @item_fields, @other_fields);
-print FILE "\n";
-
-for (my $i=0; $i<scalar(@$data); $i++) {
- my $datedue = C4::Dates->new($data->[$i]->{'date_due'},'iso');
- my $datedue_days = Date_to_Days(split(/-/,$datedue->output('iso')));
- my $due_str = $datedue->output();
- unless (defined $data->[$i]->{'borrowernumber'}) {
- print STDERR "ERROR in Getoverdues line $i: issues.borrowernumber IS NULL. Repair 'issues' table now! Skipping record.\n";
- next; # Note: this doesn't solve everything. After NULL borrowernumber, multiple issues w/ real borrowernumbers can pile up.
+ my $datedue = dt_from_string( $overdue->{date_due} );
+ if ( DateTime->compare( $datedue, $today ) == 1 ) {
+ next; # not overdue
}
- my $borrower = BorType($data->[$i]->{'borrowernumber'});
- my $branchcode = ($control eq 'ItemHomeLibrary') ? $data->[$i]->{homebranch} :
- ($control eq 'PatronLibrary' ) ? $borrower->{branchcode} :
- $data->[$i]->{branchcode} ;
- # In final case, CircControl must be PickupLibrary. (branchcode comes from issues table here).
- my $calendar;
- unless (defined ($calendars{$branchcode})) {
- $calendars{$branchcode} = C4::Calendar->new(branchcode => $branchcode);
+ ++$counted;
+
+ my ( $amount, $type, $daycounttotal ) =
+ CalcFine( $overdue, $borrower->{categorycode},
+ $branchcode, $datedue, $today );
+
+ $type ||= q{};
+
+ # Don't update the fine if today is a holiday.
+ # This ensures that dropbox mode will remove the correct amount of fine.
+ if ( $mode eq 'production' && !$is_holiday{$branchcode} ) {
+ if ( $amount > 0 ) {
+ UpdateFine(
+ $overdue->{itemnumber},
+ $overdue->{borrowernumber},
+ $amount, $type, output_pref($datedue)
+ );
+ }
}
- $calendar = $calendars{$branchcode};
- my $isHoliday = $calendar->isHoliday(split '/', $today->output('metric'));
-
- ($datedue_days <= $today_days) or next; # or it's not overdue, right?
-
- $overdueItemsCounted++;
- my ($amount,$type,$daycounttotal,$daycount)=
- CalcFine($data->[$i], $borrower->{'categorycode'}, $branchcode,undef,undef, $datedue, $today);
- # FIXME: $type NEVER gets populated by anything.
- (defined $type) or $type = '';
- # Don't update the fine if today is a holiday.
- # This ensures that dropbox mode will remove the correct amount of fine.
- if ($mode eq 'production' and ! $isHoliday) {
- UpdateFine($data->[$i]->{'itemnumber'},$data->[$i]->{'borrowernumber'},$amount,$type,$due_str) if( $amount > 0 ) ;
- }
- my @cells = ();
- push @cells, map {$borrower->{$_}} @borrower_fields;
- push @cells, map {$data->[$i]->{$_}} @item_fields;
+ my @cells;
+ push @cells,
+ map { defined $borrower->{$_} ? $borrower->{$_} : q{} } @borrower_fields;
+ push @cells, map { $overdue->{$_} } @item_fields;
push @cells, $type, $daycounttotal, $amount;
- print FILE join($delim, @cells), "\n";
+ say {$fh} join $delim, @cells;
}
+close $fh;
-my $numOverdueItems = scalar(@$data);
if ($verbose) {
- print <<EOM;
-Fines assessment -- $today_iso -- Saved to $filename
+ my $overdue_items = @{$overdues};
+ print <<'EOM';
+Fines assessment -- $today->ymd() -- Saved to $filename
Number of Overdue Items:
- counted $overdueItemsCounted
- reported $numOverdueItems
+ counted $overdue_items
+ reported $counted
EOM
}
-close FILE;
+sub set_holiday {
+ my ( $branch, $dt ) = @_;
+
+ my $calendar = Koha::Calendar->new( branchcode => $branch );
+ return $calendar->is_holiday($dt);
+}
+
+sub get_filename {
+ my $directory = shift;
+ if ( !$directory ) {
+ $directory = File::Spec->tmpdir();
+ }
+ if ( !-d $directory ) {
+ carp "Could not write to $directory ... does not exist!";
+ }
+ my $name = C4::Context->config('database');
+ $name =~ s/\W//;
+ $name .= join q{}, q{_}, $today->ymd(), '.log';
+ $name = File::Spec->catfile( $directory, $name );
+ if ($verbose) {
+ say "writing to $name";
+ }
+ return $name;
+}
# these are the fields that will be substituted into <<item.content>>
my @item_content_fields = split( /,/, $itemscontent );
-binmode STDOUT, ':encoding(UTF-8)';
+binmode( STDOUT, ':encoding(UTF-8)' );
our $csv; # the Text::CSV_XS object
if ( $htmlfilename eq '' ) {
$html_fh = *STDOUT;
} else {
- my $today = C4::Dates->new();
- open $html_fh, ">",File::Spec->catdir ($htmlfilename,"notices-".$today->output('iso').".html");
+ my $today = DateTime->now(time_zone => C4::Context->tz );
+ open $html_fh, ">",File::Spec->catdir ($htmlfilename,"notices-".$today->ymd().".html");
}
print $html_fh "<html>\n";
use MARC::Field;
use List::MoreUtils qw/any none/;
use C4::Images;
+use Koha::DateUtils;
BEGIN {
if (C4::Context->preference('BakerTaylorEnabled')) {
# I can't actually find any case in which this is defined. --amoore 2008-12-09
$itm->{ $itm->{'publictype'} } = 1;
}
- $itm->{datedue} = format_date($itm->{datedue});
+ $itm->{datedue} = format_sqlduedatetime($itm->{datedue});
$itm->{datelastseen} = format_date($itm->{datelastseen});
# get collection code description, too
use DateTime;
use DateTime::Format::ICal;
use Date::Calc qw (Parse_Date);
+use DateTime;
+use DateTime::Event::ICal;
use C4::Auth;
use C4::Koha;
my $calendar = Data::ICal->new();
# get issued items ....
-my ($issues) = GetPendingIssues($borrowernumber);
+my $issues = GetPendingIssues($borrowernumber);
foreach my $issue ( @$issues ) {
my $vevent = Data::ICal::Entry::Event->new();
use C4::Koha;
use C4::Biblio;
use C4::Circulation;
-use C4::Dates qw/format_date/;
use C4::Members;
+use Koha::DateUtils;
use C4::Output;
$line{title} = $issue->{'title'};
$line{author} = $issue->{'author'};
$line{itemcallnumber} = $issue->{'itemcallnumber'};
- $line{date_due} = format_date( $issue->{'date_due'} );
- $line{returndate} = format_date( $issue->{'returndate'} );
+ $line{date_due} = format_sqlduedatetime( $issue->{date_due} );
+ $line{returndate} = format_sqldatetime( $issue->{returndate} );
$line{volumeddesc} = $issue->{'volumeddesc'};
$issue->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $issue->{'itype'} : $issue->{'itemtype'};
if($issue->{'itemtype'}) {
use C4::Branch; # GetBranches
use C4::Overdues;
use C4::Debug;
+use Koha::DateUtils;
# use Data::Dumper;
my $MAXIMUM_NUMBER_OF_RESERVES = C4::Context->preference("maxreserves");
# change the background color.
my $issues= GetItemIssue($itemNum);
if ( $issues->{'date_due'} ) {
- $itemLoopIter->{dateDue} = format_date($issues->{'date_due'});
+ $itemLoopIter->{dateDue} = format_sqlduedatetime($issues->{date_due});
$itemLoopIter->{backgroundcolor} = 'onloan';
}
use C4::Dates qw/format_date/;
use C4::Letters;
use C4::Branch; # GetBranches
+use Koha::DateUtils;
use constant ATTRIBUTE_SHOW_BARCODE => 'SHOW_BCODE';
my @overdues;
my @issuedat;
my $itemtypes = GetItemTypes();
-my ($issues) = GetPendingIssues($borrowernumber);
+my $issues = GetPendingIssues($borrowernumber);
if ($issues){
- foreach my $issue ( sort { $b->{'date_due'} cmp $a->{'date_due'} } @$issues ) {
+ foreach my $issue ( sort { $b->{date_due}->datetime() cmp $a->{date_due}->datetime() } @{$issues} ) {
# check for reserves
my ( $restype, $res, undef ) = CheckReserves( $issue->{'itemnumber'} );
if ( $restype ) {
$issue->{'imageurl'} = getitemtypeimagelocation( 'opac', $itemtypes->{$itemtype}->{'imageurl'} );
$issue->{'description'} = $itemtypes->{$itemtype}->{'description'};
}
- $issue->{date_due} = format_date($issue->{date_due});
+ $issue->{date_due} = output_pref_due($issue->{date_due});
push @issuedat, $issue;
$count++;
use C4::Circulation;
use C4::Auth;
use URI::Escape;
-use C4::Dates qw/format_date_in_iso/;
+use Koha::DateUtils;
my $input = new CGI;
#Set Up User_env
my $branch=$input->param('branch');
my $datedue;
if ($input->param('newduedate')){
- $datedue=C4::Dates->new($input->param('newduedate'));
+ $datedue = dt_from_string($input->param('newduedate'));
}
# warn "barcodes : @barcodes";
use C4::Dates qw/format_date/;
use C4::Members;
use C4::Search; # enabled_staff_search_views
+use Koha::DateUtils;
my $dbh = C4::Context->dbh;
my $sth;
# change the background color
my $issues= GetItemIssue($itemnumber);
if ( $issues->{'date_due'} ) {
- $item->{date_due} = format_date($issues->{'date_due'});
+ $item->{date_due} = format_sqldatetime($issues->{date_due});
$item->{backgroundcolor} = 'onloan';
}
my @all_koha_dirs = qw( acqui admin authorities basket C4 catalogue cataloguing circ debian errors
labels members misc offline_circ opac patroncards reports reserve reviews rotating_collections
-serials sms suggestion t tags test tools virtualshelves);
+serials sms suggestion t tags test tools virtualshelves Koha);
my @dirs = qw( acqui admin authorities basket catalogue cataloguing circ debian errors labels
- offline_circ reserve reviews rotating_collections serials sms virtualshelves );
+ offline_circ reserve reviews rotating_collections serials sms virtualshelves Koha);
if ( not $ENV{TEST_QA} ) {
my $msg = 'Author test. Set $ENV{TEST_QA} to a true value to run';
--- /dev/null
+use strict;
+use warnings;
+use 5.010;
+use DateTime;
+use DateTime::TimeZone;
+
+use C4::Context;
+use Test::More tests => 25;
+
+BEGIN { use_ok('Koha::DateUtils'); }
+
+my $tz = C4::Context->tz;
+
+isa_ok( $tz, 'DateTime::TimeZone', 'Context returns timezone object' );
+
+my $testdate_iso = '2011-06-16'; # Bloomsday 2011
+my $dt = dt_from_string( $testdate_iso, 'iso' );
+
+isa_ok( $dt, 'DateTime', 'dt_from_string returns a DateTime object' );
+
+cmp_ok( $dt->ymd(), 'eq', $testdate_iso, 'Returned object matches input' );
+
+$dt->set_hour(12);
+$dt->set_minute(0);
+
+my $date_string = output_pref( $dt, 'iso' );
+cmp_ok $date_string, 'eq', '2011-06-16 12:00', 'iso output';
+
+$date_string = output_pref( $dt, 'us' );
+cmp_ok $date_string, 'eq', '06/16/2011 12:00', 'us output';
+
+# metric should return the French Revolutionary Calendar Really
+$date_string = output_pref( $dt, 'metric' );
+cmp_ok $date_string, 'eq', '16/06/2011 12:00', 'metric output';
+
+$date_string = output_pref_due( $dt, 'metric' );
+cmp_ok $date_string, 'eq', '16/06/2011 12:00',
+ 'output_pref_due preserves non midnight HH:SS';
+
+$dt->set_hour(23);
+$dt->set_minute(59);
+$date_string = output_pref_due( $dt, 'metric' );
+cmp_ok $date_string, 'eq', '16/06/2011',
+ 'output_pref_due truncates HH:SS at midnight';
+
+my $dear_dirty_dublin = DateTime::TimeZone->new( name => 'Europe/Dublin' );
+my $new_dt = dt_from_string( '16/06/2011', 'metric', $dear_dirty_dublin );
+isa_ok( $new_dt, 'DateTime', 'Create DateTime with different timezone' );
+cmp_ok( $new_dt->ymd(), 'eq', $testdate_iso,
+ 'Returned Dublin object matches input' );
+
+$new_dt = dt_from_string( '2011-06-16 12:00', 'sql' );
+isa_ok( $new_dt, 'DateTime', 'Create DateTime from (mysql) sql' );
+cmp_ok( $new_dt->ymd(), 'eq', $testdate_iso, 'sql returns correct date' );
+
+$new_dt = dt_from_string( $dt, 'iso' );
+isa_ok( $new_dt, 'DateTime', 'Passed a DateTime dt_from_string returns it' );
+
+# C4::Dates allowed 00th of the month
+
+my $ymd = '2012-01-01';
+my $dt0 = dt_from_string( '00/01/2012', 'metric' );
+isa_ok( $dt0, 'DateTime',
+ 'dt_from_string returns a DateTime object passed a zero metric day' );
+cmp_ok( $dt0->ymd(), 'eq', $ymd, 'Returned object corrects metric day 0' );
+
+$dt0 = dt_from_string( '01/00/2012', 'us' );
+isa_ok( $dt0, 'DateTime',
+ 'dt_from_string returns a DateTime object passed a zero us day' );
+cmp_ok( $dt0->ymd(), 'eq', $ymd, 'Returned object corrects us day 0' );
+
+$dt0 = dt_from_string( '2012-01-00', 'iso' );
+isa_ok( $dt0, 'DateTime',
+ 'dt_from_string returns a DateTime object passed a zero iso day' );
+cmp_ok( $dt0->ymd(), 'eq', $ymd, 'Returned object corrects iso day 0' );
+
+# Return undef if passed mysql 0 dates
+$dt0 = dt_from_string( '0000-00-00', 'iso' );
+is( $dt0, undef, "undefined returned for 0 iso date" );
+
+my $formatted = format_sqldatetime( '2011-06-16 12:00:07', 'metric' );
+cmp_ok( $formatted, 'eq', '16/06/2011 12:00', 'format_sqldatetime conversion' );
+
+$formatted = format_sqldatetime( undef, 'metric' );
+cmp_ok( $formatted, 'eq', q{},
+ 'format_sqldatetime formats undef as empty string' );
+
+$formatted = format_sqlduedatetime( '2011-06-16 12:00:07', 'metric' );
+cmp_ok(
+ $formatted, 'eq',
+ '16/06/2011 12:00',
+ 'format_sqlduedatetime conversion for hourly loans'
+);
+
+$formatted = format_sqlduedatetime( '2011-06-16 23:59:07', 'metric' );
+cmp_ok( $formatted, 'eq', '16/06/2011',
+ 'format_sqlduedatetime conversion for daily loans' );
--- /dev/null
+use strict;
+use warnings;
+use 5.010;
+use DateTime;
+use DateTime::TimeZone;
+
+use C4::Context;
+use Test::More tests => 9;
+
+BEGIN { use_ok('Koha::Calendar'); }
+
+my $cal = Koha::Calendar->new( TEST_MODE => 1 );
+
+isa_ok( $cal, 'Koha::Calendar', 'Calendar class returned' );
+
+my $saturday = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 25,
+ time_zone => 'Europe/London',
+);
+my $sunday = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 26,
+ time_zone => 'Europe/London',
+);
+my $monday = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 27,
+ time_zone => 'Europe/London',
+);
+my $bloomsday = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 16,
+ time_zone => 'Europe/London',
+); # should be a holiday
+my $special = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 1,
+ time_zone => 'Europe/London',
+); # should be a holiday
+my $notspecial = DateTime->new(
+ year => 2011,
+ month => 6,
+ day => 2,
+ time_zone => 'Europe/London',
+); # should NOT be a holiday
+is( $cal->is_holiday($sunday), 1, 'Sunday is a closed day' ); # wee free test;
+is( $cal->is_holiday($monday), 0, 'Monday is not a closed day' ); # alas
+is( $cal->is_holiday($bloomsday), 1, 'month/day closed day test' );
+is( $cal->is_holiday($special), 1, 'special closed day test' );
+is( $cal->is_holiday($notspecial), 0, 'open day test' );
+
+my $dt = $cal->addDate( $saturday, 1, 'days' );
+is( $dt->day_of_week, 1, 'addDate skips closed Sunday' );
+
+$dt = $cal->addDate( $bloomsday, -1 );
+cmp_ok( $dt->ymd(), 'cmp', '2011-06-15', 'Negative call to addDate' );
--- /dev/null
+
+use strict;
+use warnings;
+use 5.010;
+use C4::Context;
+use C4::Circulation;
+use C4::Members;
+
+use Test::More tests => 8;
+C4::Context->_new_userenv(1234567);
+C4::Context->set_userenv(91, 'CLIstaff', '23529001223661', 'CPL',
+ 'CPL', 'CPL', '', 'cc@cscnet.co.uk');
+
+
+my $test_patron = '23529001223651';
+my $test_item_fic = '502326000402';
+my $test_item_24 = '502326000404';
+my $test_item_48 = '502326000403';
+
+for my $item_barcode ( $test_item_fic, $test_item_24, $test_item_48) {
+ my $duedate = try_issue($test_patron, $item_barcode);
+ isa_ok($duedate, 'DateTime');
+ if ($item_barcode eq $test_item_fic) {
+ is($duedate->hour(), 23, "daily loan hours = 23");
+ is($duedate->minute(), 59, "daily loan mins = 59");
+ }
+ my $ret_ok = try_return($item_barcode);
+ is($ret_ok, 1, 'Return succeeded');
+}
+
+
+sub try_issue {
+ my ($cardnumber, $item ) = @_;
+ my $issuedate = '2011-05-16';
+ my $borrower = GetMemberDetails(0, $cardnumber);
+ my ($issuingimpossible,$needsconfirmation) = CanBookBeIssued( $borrower, $item );
+ my $due_date = AddIssue($borrower, $item, undef, 0, $issuedate);
+ return $due_date;
+}
+
+sub try_return {
+ my $barcode = shift;
+ my ($ret, $messages, $iteminformation, $borrower) = AddReturn($barcode);
+ return $ret;
+}