use C4::Items;
use C4::Members;
use C4::Dates;
+use C4::Calendar;
+use C4::Accounts;
use Date::Calc qw(
Today
Today_and_Now
# FIXME subs that should probably be elsewhere
push @EXPORT, qw(
&FixOverduesOnReturn
- &cuecatbarcodedecode
+ &barcodedecode
);
# subs to deal with issuing a book
# subs to deal with returns
push @EXPORT, qw(
&AddReturn
+ &MarkIssueReturned
);
# subs to deal with transfers
=over 4
-=item Decodes a segment of a string emitted by a CueCat barcode scanner and
-returns it.
+=item Generic filter function for barcode string.
=back
=cut
-# FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
# FIXME From Paul : i don't understand what this sub does & why it has to be called on every circ. Speak of this with chris maybe ?
+# FIXME -- the &decode fcn below should be wrapped into this one.
-sub cuecatbarcodedecode {
+sub barcodedecode {
my ($barcode) = @_;
- chomp($barcode);
- my @fields = split( /\./, $barcode );
- my @results = map( decode($_), @fields[ 1 .. $#fields ] );
- if ( $#results == 2 ) {
- return $results[2];
- }
- else {
- return $barcode;
- }
+ my $filter = C4::Context->preference('itemBarcodeInputFilter');
+ if($filter eq 'whitespace') {
+ $barcode =~ s/\s//g;
+ return $barcode;
+ } elsif($filter eq 'cuecat') {
+ chomp($barcode);
+ my @fields = split( /\./, $barcode );
+ my @results = map( decode($_), @fields[ 1 .. $#fields ] );
+ if ( $#results == 2 ) {
+ return $results[2];
+ }
+ else {
+ return $barcode;
+ }
+ } elsif($filter eq 'T-prefix') {
+ my $num = ( $barcode =~ /^[Tt] /) ? substr($barcode,2) + 0 : $barcode;
+ return sprintf( "T%07d",$num);
+ }
}
=head2 decode
my $dbh = C4::Context->dbh;
my $branch;
# Get which branchcode we need
- if (C4::Context->preference('CircControl') eq 'PickupLibary'){
- $branch = C4::Context->userenv->{'branchcode'};
+ if (C4::Context->preference('CircControl') eq 'PickupLibrary'){
+ $branch = C4::Context->userenv->{'branch'};
}
- elsif (C4::Context->preference('CircControl') eq 'PatronLibary'){
+ elsif (C4::Context->preference('CircControl') eq 'PatronLibrary'){
$branch = $borrower->{'branchcode'};
}
else {
my $query2 = "SELECT COUNT(*) FROM issues i, biblioitems s1, items s2
WHERE i.borrowernumber = ?
- AND i.returndate IS NULL
AND i.itemnumber = s2.itemnumber
AND s1.biblioitemnumber = s2.biblioitemnumber";
if (C4::Context->preference('item-level_itypes')){
my $sth3 =
$dbh->prepare(
'SELECT COUNT(*) FROM issues
- WHERE borrowernumber = ?
- AND returndate IS NULL'
+ WHERE borrowernumber = ?'
);
my $alreadyissued;
"SELECT * FROM issues
LEFT JOIN borrowers ON issues.borrowernumber = borrowers.borrowernumber
WHERE itemnumber = ?
- AND returndate IS NULL
"
);
# Find the last 3 people who borrowed this item.
$sth2 = $dbh->prepare(
- "SELECT * FROM issues
+ "SELECT * FROM old_issues
LEFT JOIN borrowers ON issues.borrowernumber = borrowers.borrowernumber
WHERE itemnumber = ?
- AND returndate IS NOT NULL
ORDER BY returndate DESC,timestamp DESC"
);
my $item = GetItem(GetItemnumberFromBarcode( $barcode ));
my $issue = GetItemIssue($item->{itemnumber});
my $biblioitem = GetBiblioItemData($item->{biblioitemnumber});
- $item->{'itemtype'}=$biblioitem->{'itemtype'};
+ $item->{'itemtype'}=$item->{'itype'};
my $dbh = C4::Context->dbh;
#
#
# BORROWER STATUS
#
+ if ( $borrower->{'category_type'} eq 'X' && ( $item->{barcode} )) {
+ # stats only borrower -- add entry to statistics table, and return issuingimpossible{STATS} = 1 .
+ &UpdateStats(C4::Context->userenv->{'branch'},'localuse','','',$item->{'itemnumber'},$item->{'itemtype'},$borrower->{'borrowernumber'});
+ return( { STATS => 1 }, {});
+ }
if ( $borrower->{flags}->{GNA} ) {
$issuingimpossible{GNA} = 1;
}
my $userenv = C4::Context->userenv;
if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
$issuingimpossible{NOTSAMEBRANCH} = 1
- if ( $item->{C4::Context->preference("HomeOrHoldingbranch")} ne $userenv->{branch} );
+ if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} );
}
}
my $branch;
# Get which branchcode we need
- if (C4::Context->preference('CircControl') eq 'PickupLibary'){
- $branch = C4::Context->userenv->{'branchcode'};
+ if (C4::Context->preference('CircControl') eq 'PickupLibrary'){
+ $branch = C4::Context->userenv->{'branch'};
}
- elsif (C4::Context->preference('CircControl') eq 'PatronLibary'){
+ elsif (C4::Context->preference('CircControl') eq 'PatronLibrary'){
$branch = $borrower->{'branchcode'};
}
else {
"UPDATE branchtransfers
SET datearrived = now(),
tobranch = ?,
- comments = 'Forced branchtransfert'
+ comments = 'Forced branchtransfer'
WHERE itemnumber= ? AND datearrived IS NULL"
);
$sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'});
$itype,
$branch
);
- $datedue = time + ($loanlength) * 86400;
- my @datearr = localtime($datedue);
- $dateduef = C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso');
- $dateduef=CheckValidDatedue($dateduef,$item->{'itemnumber'},C4::Context->userenv->{'branch'});
-
+ $dateduef = CalcDateDue(C4::Dates->new(),$loanlength,$branch);
# if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
if ( C4::Context->preference('ReturnBeforeExpiry') && $dateduef->output('iso') gt $borrower->{dateexpiry} ) {
$dateduef = C4::Dates->new($borrower->{dateexpiry},'iso');
);
}
- &logaction(C4::Context->userenv->{'number'},"CIRCULATION","ISSUE",$borrower->{'borrowernumber'},$biblio->{'biblionumber'})
+ logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'biblionumber'})
if C4::Context->preference("IssueLog");
return ($datedue);
}
# find the borrower
if ( ( not $iteminformation->{borrowernumber} ) && $doreturn ) {
$messages->{'NotIssued'} = $barcode;
+ # even though item is not on loan, it may still
+ # be transferred; therefore, get current branch information
+ my $curr_iteminfo = GetItem($iteminformation->{'itemnumber'});
+ $iteminformation->{'homebranch'} = $curr_iteminfo->{'homebranch'};
+ $iteminformation->{'holdingbranch'} = $curr_iteminfo->{'holdingbranch'};
$doreturn = 0;
}
# case of a return of document (deal with issues and holdingbranch)
if ($doreturn) {
- my $sth =
- $dbh->prepare(
- "UPDATE issues SET returndate = now() WHERE (borrowernumber = ?) AND (itemnumber = ?) AND (returndate IS NULL)"
- );
- $sth->execute( $borrower->{'borrowernumber'},
- $iteminformation->{'itemnumber'} );
+ MarkIssueReturned($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
$messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
}
$borrower->{'borrowernumber'}
);
- &logaction(C4::Context->userenv->{'number'},"CIRCULATION","RETURN",$iteminformation->{borrowernumber},$iteminformation->{'biblionumber'})
+ logaction("CIRCULATION", "RETURN", $iteminformation->{borrowernumber}, $iteminformation->{'biblionumber'})
if C4::Context->preference("ReturnLog");
#adding message if holdingbranch is non equal a userenv branch to return the document to homebranch
return ( $doreturn, $messages, $iteminformation, $borrower );
}
+=head2 MarkIssueReturned
+
+=over 4
+
+MarkIssueReturned($borrowernumber, $itemnumber);
+
+=back
+
+Unconditionally marks an issue as being returned by
+moving the C<issues> row to C<old_issues> and
+setting C<returndate> to the current date.
+
+Ideally, this function would be internal to C<C4::Circulation>,
+not exported, but it is currently needed by one
+routine in C<C4::Accounts>.
+
+=cut
+
+sub MarkIssueReturned {
+ my ($borrowernumber, $itemnumber) = @_;
+
+ my $dbh = C4::Context->dbh;
+ # FIXME transaction
+ my $sth_upd = $dbh->prepare("UPDATE issues SET returndate = now()
+ WHERE borrowernumber = ?
+ AND itemnumber = ?");
+ $sth_upd->execute($borrowernumber, $itemnumber);
+ my $sth_copy = $dbh->prepare("INSERT INTO old_issues SELECT * FROM issues
+ WHERE borrowernumber = ?
+ AND itemnumber = ?");
+ $sth_copy->execute($borrowernumber, $itemnumber);
+ my $sth_del = $dbh->prepare("DELETE FROM issues
+ WHERE borrowernumber = ?
+ AND itemnumber = ?");
+ $sth_del->execute($borrowernumber, $itemnumber);
+}
+
=head2 FixOverduesOnReturn
&FixOverduesOnReturn($brn,$itm, $exemptfine);
"SELECT * FROM issues
LEFT JOIN items ON issues.itemnumber=items.itemnumber
WHERE
- issues.itemnumber=? AND returndate IS NULL ");
+ issues.itemnumber=?");
$sth->execute($itemnumber);
my $data = $sth->fetchrow_hashref;
my $datedue = $data->{'date_due'};
# get today date
my $today = POSIX::strftime("%Y%m%d", localtime);
- my $sth = $dbh->prepare(
- "SELECT * FROM issues
- LEFT JOIN borrowers ON borrowers.borrowernumber
- LEFT JOIN items ON items.itemnumber=issues.itemnumber
- WHERE
- issues.itemnumber=?".($history?"":" AND returndate IS NULL ").
- "ORDER BY issues.date_due DESC"
- );
- $sth->execute($itemnumber);
+ 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
+ LEFT JOIN borrowers USING (borrowernumber)
+ JOIN items USING (itemnumber)
+ WHERE old_issues.itemnumber = ? ";
+ }
+ $sql .= "ORDER BY date_due DESC";
+ my $sth = $dbh->prepare($sql);
+ if ($history) {
+ $sth->execute($itemnumber, $itemnumber);
+ } else {
+ $sth->execute($itemnumber);
+ }
while ( my $data = $sth->fetchrow_hashref ) {
my $datedue = $data->{'date_due'};
$datedue =~ s/-//g;
LEFT JOIN biblioitems ON items.itemnumber = biblioitems.biblioitemnumber
LEFT JOIN biblio ON biblio.biblionumber = items.biblioitemnumber
WHERE biblio.biblionumber = ?
- ORDER BY issues.timestamp
+ UNION ALL
+ SELECT old_issues.*,items.barcode,biblio.biblionumber,biblio.title, biblio.author,borrowers.cardnumber,borrowers.surname,borrowers.firstname
+ FROM old_issues
+ LEFT JOIN borrowers ON borrowers.borrowernumber = old_issues.borrowernumber
+ LEFT JOIN items ON old_issues.itemnumber = items.itemnumber
+ LEFT JOIN biblioitems ON items.itemnumber = biblioitems.biblioitemnumber
+ LEFT JOIN biblio ON biblio.biblionumber = items.biblioitemnumber
+ WHERE biblio.biblionumber = ?
+ ORDER BY timestamp
";
my $sth = $dbh->prepare($query);
- $sth->execute($biblionumber);
+ $sth->execute($biblionumber, $biblionumber);
my @issues;
while ( my $data = $sth->fetchrow_hashref ) {
my $sth1 = $dbh->prepare(
"SELECT * FROM issues
WHERE borrowernumber = ?
- AND itemnumber = ?
- AND returndate IS NULL"
+ AND itemnumber = ?"
);
$sth1->execute( $borrowernumber, $itemnumber );
if ( my $data1 = $sth1->fetchrow_hashref ) {
# because it's a bit messy: given the item number, we need to find
# the biblioitem, which gives us the itemtype, which tells us
# whether it may be renewed.
- my $sth2 = $dbh->prepare(
- "SELECT renewalsallowed FROM items
- LEFT JOIN biblioitems on items.biblioitemnumber = biblioitems.biblioitemnumber
- LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
- WHERE items.itemnumber = ?
- "
- );
+ my $query = "SELECT renewalsallowed FROM items ";
+ $query .= (C4::Context->preference('item-level_itypes'))
+ ? "LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype "
+ : "LEFT JOIN biblioitems on items.biblioitemnumber = biblioitems.biblioitemnumber
+ LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype ";
+ $query .= "WHERE items.itemnumber = ?";
+ my $sth2 = $dbh->prepare($query);
$sth2->execute($itemnumber);
if ( my $data2 = $sth2->fetchrow_hashref ) {
$renews = $data2->{'renewalsallowed'};
my ( $borrowernumber, $itemnumber, $branch ,$datedue ) = @_;
my $dbh = C4::Context->dbh;
-
- my $biblio = GetBiblioFromItemNumber($itemnumber);
+ my $biblio = GetBiblioFromItemNumber($itemnumber);
# If the due date wasn't specified, calculate it by adding the
# book's loan length to today's date.
unless ( $datedue ) {
(C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ,
$borrower->{'branchcode'}
);
- #FIXME -- choose issuer or borrower branch.
- #FIXME -- where's the calendar ?
+ #FIXME -- choose issuer or borrower branch -- use circControl.
+
#FIXME -- $debug-ify the (0)
- my @darray = Add_Delta_DHMS( Today_and_Now(), $loanlength, 0, 0, 0 );
- $datedue = C4::Dates->new( sprintf("%04d-%02d-%02d",@darray[0..2]), 'iso');
- (0) and print STDERR "C4::Dates->new->output = " . C4::Dates->new()->output()
- . "\ndatedue->output = " . $datedue->output()
- . "\n(Y,M,D) = " . join ',', @darray;
- $datedue=CheckValidDatedue($datedue,$itemnumber,$branch);
+ #my @darray = Add_Delta_DHMS( Today_and_Now(), $loanlength, 0, 0, 0 );
+ #$datedue = C4::Dates->new( sprintf("%04d-%02d-%02d",@darray[0..2]), 'iso');
+ #(0) and print STDERR "C4::Dates->new->output = " . C4::Dates->new()->output()
+ # . "\ndatedue->output = " . $datedue->output()
+ # . "\n(Y,M,D) = " . join ',', @darray;
+ #$datedue=CheckValidDatedue($datedue,$itemnumber,$branch,$loanlength);
+ $datedue = CalcDateDue(C4::Dates->new(),$loanlength,$branch);
}
# Find the issues record for this book
my $sth =
$dbh->prepare("SELECT * FROM issues
WHERE borrowernumber=?
- AND itemnumber=?
- AND returndate IS NULL"
+ AND itemnumber=?"
);
$sth->execute( $borrowernumber, $itemnumber );
my $issuedata = $sth->fetchrow_hashref;
my $renews = $issuedata->{'renewals'} + 1;
$sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?
WHERE borrowernumber=?
- AND itemnumber=?
- AND returndate IS NULL"
+ AND itemnumber=?"
);
$sth->execute( $datedue->output('iso'), $renews, $borrowernumber, $itemnumber );
$sth->finish;
# FIXME - I think this function could be redone to use only one SQL call.
my $sth = $dbh->prepare("select * from issues
where (borrowernumber = ?)
- and (itemnumber = ?)
- and returndate is null");
+ and (itemnumber = ?)");
$sth->execute($bornum,$itemno);
- my $data = $sth->fetchrow_hashref;
- $renewcount = $data->{'renewals'} if $data->{'renewals'};
- my $sth2 = $dbh->prepare("select renewalsallowed from items,biblioitems,itemtypes
- where (items.itemnumber = ?)
- and (items.biblioitemnumber = biblioitems.biblioitemnumber)
- and (biblioitems.itemtype = itemtypes.itemtype)");
+ my $data = $sth->fetchrow_hashref;
+ $renewcount = $data->{'renewals'} if $data->{'renewals'};
+ $sth->finish;
+ my $query = "SELECT renewalsallowed FROM items ";
+ $query .= (C4::Context->preference('item-level_itypes'))
+ ? "LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype "
+ : "LEFT JOIN biblioitems on items.biblioitemnumber = biblioitems.biblioitemnumber
+ LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype ";
+ $query .= "WHERE items.itemnumber = ?";
+ my $sth2 = $dbh->prepare($query);
$sth2->execute($itemno);
- my $data2 = $sth2->fetchrow_hashref();
- $renewsallowed = $data2->{'renewalsallowed'};
- $renewsleft = $renewsallowed - $renewcount;
-# warn "Renewcount:$renewcount RenewsAll:$renewsallowed RenewLeft:$renewsleft";
- return ($renewcount,$renewsallowed,$renewsleft);
+ my $data2 = $sth2->fetchrow_hashref();
+ $renewsallowed = $data2->{'renewalsallowed'};
+ $renewsleft = $renewsallowed - $renewcount;
+ return ($renewcount,$renewsallowed,$renewsleft);
}
+
=head2 GetIssuingCharges
($charge, $item_type) = &GetIssuingCharges($itemnumber, $borrowernumber);
my $borrowernumber = shift;
my $dbh = C4::Context->dbh;
my $query = "
- UPDATE issues
+ UPDATE old_issues
SET borrowernumber = NULL
WHERE returndate < '".$date."'
AND borrowernumber IS NOT NULL
ModItem({ holdingbranch => $branch }, undef, $itemnumber);
}
+=head2 CalcDateDue
+
+$newdatedue = CalcDateDue($startdate,$loanlength,$branchcode);
+this function calculates the due date given the loan length ,
+checking against the holidays calendar as per the 'useDaysMode' syspref.
+C<$startdate> = C4::Dates object representing start date of loan period (assumed to be today)
+C<$branch> = location whose calendar to use
+C<$loanlength> = loan length prior to adjustment
+=cut
+
+sub CalcDateDue {
+ my ($startdate,$loanlength,$branch) = @_;
+ if(C4::Context->preference('useDaysMode') eq 'Days') { # ignoring calendar
+ my $datedue = time + ($loanlength) * 86400;
+ #FIXME - assumes now even though we take a startdate
+ my @datearr = localtime($datedue);
+ return C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso');
+ } else {
+ my $calendar = C4::Calendar->new( branchcode => $branch );
+ my $datedue = $calendar->addDate($startdate, $loanlength);
+ return $datedue;
+ }
+}
+
=head2 CheckValidDatedue
+ 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.
$newdatedue = CheckValidDatedue($date_due,$itemnumber,$branchcode);
-this function return a new date due after checked if it's a repeatable or special holiday
+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> = localisation of issue
-
+C<$branchcode> = location of issue (affected by 'CircControl' syspref)
+C<$loanlength> = loan length prior to adjustment
=cut
-# Why not create calendar object? -
-# TODO add 'duedate' option to useDaysMode .
-sub CheckValidDatedue {
+sub CheckValidDatedue {
my ($date_due,$itemnumber,$branchcode)=@_;
my @datedue=split('-',$date_due->output('iso'));
my $years=$datedue[0];
# 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');
- }
- }
- my $newdatedue=C4::Dates->new(sprintf("%04d-%02d-%02d",$years,$month,$day),'iso');
+ $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');
+ }
+ }
+ my $newdatedue=C4::Dates->new(sprintf("%04d-%02d-%02d",$years,$month,$day),'iso');
return $newdatedue;
}
+
=head2 CheckRepeatableHolidays
$countrepeatable = CheckRepeatableHoliday($itemnumber,$week_day,$branchcode);
-this function check if the date due is a repeatable holiday
+this function checks if the date due is a repeatable holiday
C<$date_due> = returndate calculate with no day check
C<$itemnumber> = itemnumber
C<$branchcode> = localisation of issue