&CanBookBeIssued
&CanBookBeRenewed
&AddIssue
+ &ForceIssue
&AddRenewal
+ &ForceRenewal
&GetRenewCount
&GetItemIssue
+ &GetOpenIssue
&GetItemIssues
&GetBorrowerIssues
&GetIssuingCharges
&GetIssuingRule
+ &GetBranchBorrowerCircRule
&GetBiblioIssues
&AnonymiseIssueHistory
);
# subs to deal with returns
push @EXPORT, qw(
&AddReturn
+ &ForceReturn
&MarkIssueReturned
);
=head1 FUNCTIONS
-=head2 decode
+=head2 barcodedecode
-=head3 $str = &decode($chunk);
+=head3 $str = &barcodedecode($barcode);
=over 4
=item Generic filter function for barcode string.
+Called on every circ if the System Pref itemBarcodeInputFilter is set.
+Will do some manipulation of the barcode for systems that deliver a barcode
+to circulation.pl that differs from the barcode stored for the item.
+For proper functioning of this filter, calling the function on the
+correct barcode string (items.barcode) should return an unaltered barcode.
=back
=cut
-# 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.
-
+# FIXME -- these plugins should be moved out of Circulation.pm
+#
sub barcodedecode {
my ($barcode) = @_;
my $filter = C4::Context->preference('itemBarcodeInputFilter');
return $barcode;
}
} elsif($filter eq 'T-prefix') {
- my $num = ( $barcode =~ /^[Tt] /) ? substr($barcode,2) + 0 : $barcode;
- return sprintf( "T%07d",$num);
+ if ( $barcode =~ /^[Tt]/) {
+ if (substr($barcode,1,1) eq '0') {
+ return $barcode;
+ } else {
+ $barcode = substr($barcode,2) + 0 ;
+ }
+ }
+ return sprintf( "T%07d",$barcode);
}
}
my $type = (C4::Context->preference('item-level_itypes'))
? $item->{'itype'} # item-level
: $item->{'itemtype'}; # biblio-level
-
- my $sth =
- $dbh->prepare(
- 'SELECT * FROM issuingrules
- WHERE categorycode = ?
- AND itemtype = ?
- AND branchcode = ?'
- );
-
- my $query2 = "SELECT COUNT(*) FROM issues i, biblioitems s1, items s2
- WHERE i.borrowernumber = ?
- AND i.itemnumber = s2.itemnumber
- AND s1.biblioitemnumber = s2.biblioitemnumber";
- if (C4::Context->preference('item-level_itypes')){
- $query2.=" AND s2.itype=? ";
- } else {
- $query2.=" AND s1.itemtype= ? ";
- }
- my $sth2= $dbh->prepare($query2);
- my $sth3 =
- $dbh->prepare(
- 'SELECT COUNT(*) FROM issues
- WHERE borrowernumber = ?'
- );
- my $alreadyissued;
-
- # check the 3 parameters (branch / itemtype / category code
- $sth->execute( $cat_borrower, $type, $branch );
- my $result = $sth->fetchrow_hashref;
-# warn "$cat_borrower, $type, $branch = ".Data::Dumper::Dumper($result);
-
- if ( $result->{maxissueqty} ne '' ) {
-# warn "checking on everything set";
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch/category/itemtype failed)" );
- }
- # now checking for total
- $sth->execute( $cat_borrower, '*', $branch );
- my $result = $sth->fetchrow_hashref;
- if ( $result->{maxissueqty} ne '' ) {
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch/category/total failed)" );
+
+ # given branch, patron category, and item type, determine
+ # applicable issuing rule
+ my $issuing_rule = GetIssuingRule($cat_borrower, $type, $branch);
+
+ # if a rule is found and has a loan limit set, count
+ # how many loans the patron already has that meet that
+ # rule
+ if (defined($issuing_rule) and defined($issuing_rule->{'maxissueqty'})) {
+ my @bind_params;
+ my $count_query = "SELECT COUNT(*) FROM issues
+ JOIN items USING (itemnumber) ";
+
+ my $rule_itemtype = $issuing_rule->{itemtype};
+ if ($rule_itemtype eq "*") {
+ # matching rule has the default item type, so count only
+ # those existing loans that don't fall under a more
+ # specific rule
+ if (C4::Context->preference('item-level_itypes')) {
+ $count_query .= " WHERE items.itype NOT IN (
+ SELECT itemtype FROM issuingrules
+ WHERE branchcode = ?
+ AND (categorycode = ? OR categorycode = ?)
+ AND itemtype <> '*'
+ ) ";
+ } else {
+ $count_query .= " JOIN biblioitems USING (biblionumber)
+ WHERE biblioitems.itemtype NOT IN (
+ SELECT itemtype FROM issuingrules
+ WHERE branchcode = ?
+ AND (categorycode = ? OR categorycode = ?)
+ AND itemtype <> '*'
+ ) ";
}
+ push @bind_params, $issuing_rule->{branchcode};
+ push @bind_params, $issuing_rule->{categorycode};
+ push @bind_params, $cat_borrower;
+ } else {
+ # rule has specific item type, so count loans of that
+ # specific item type
+ if (C4::Context->preference('item-level_itypes')) {
+ $count_query .= " WHERE items.itype = ? ";
+ } else {
+ $count_query .= " JOIN biblioitems USING (biblionumber)
+ WHERE biblioitems.itemtype= ? ";
+ }
+ push @bind_params, $type;
}
- }
- # check the 2 parameters (branch / itemtype / default categorycode
- $sth->execute( '*', $type, $branch );
- $result = $sth->fetchrow_hashref;
-# warn "*, $type, $branch = ".Data::Dumper::Dumper($result);
-
- if ( $result->{maxissueqty} ne '' ) {
-# warn "checking on 2 parameters (default categorycode)";
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch / default category / itemtype failed)" );
- }
- # now checking for total
- $sth->execute( '*', '*', $branch );
- my $result = $sth->fetchrow_hashref;
- if ( $result->{maxissueqty} ne '' ) {
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch / default category / total failed)" );
+ $count_query .= " AND borrowernumber = ? ";
+ push @bind_params, $borrower->{'borrowernumber'};
+ my $rule_branch = $issuing_rule->{branchcode};
+ if ($rule_branch ne "*") {
+ if (C4::Context->preference('CircControl') eq 'PickupLibrary') {
+ $count_query .= " AND issues.branchcode = ? ";
+ push @bind_params, $branch;
+ } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
+ ; # if branch is the patron's home branch, then count all loans by patron
+ } else {
+ $count_query .= " AND items.homebranch = ? ";
+ push @bind_params, $branch;
}
}
- }
-
- # check the 1 parameters (default branch / itemtype / categorycode
- $sth->execute( $cat_borrower, $type, '*' );
- $result = $sth->fetchrow_hashref;
-# warn "$cat_borrower, $type, * = ".Data::Dumper::Dumper($result);
-
- if ( $result->{maxissueqty} ne '' ) {
-# warn "checking on 1 parameter (default branch + categorycode)";
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch/category/itemtype failed)" );
- }
- # now checking for total
- $sth->execute( $cat_borrower, '*', '*' );
- my $result = $sth->fetchrow_hashref;
- if ( $result->{maxissueqty} ne '' ) {
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch / category / total failed)" );
- }
+
+ my $count_sth = $dbh->prepare($count_query);
+ $count_sth->execute(@bind_params);
+ my ($current_loan_count) = $count_sth->fetchrow_array;
+
+ my $max_loans_allowed = $issuing_rule->{'maxissueqty'};
+ if ($current_loan_count >= $max_loans_allowed) {
+ return "$current_loan_count / $max_loans_allowed";
}
}
- # check the 0 parameters (default branch / itemtype / default categorycode
- $sth->execute( '*', $type, '*' );
- $result = $sth->fetchrow_hashref;
-# warn "*, $type, * = ".Data::Dumper::Dumper($result);
-
- if ( $result->{maxissueqty} ne '' ) {
-# warn "checking on default branch and default categorycode";
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch / default category / itemtype failed)" );
+ # Now count total loans against the limit for the branch
+ 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
+ JOIN items USING (itemnumber)
+ WHERE borrowernumber = ? ";
+ push @bind_params, $borrower->{borrowernumber};
+
+ if (C4::Context->preference('CircControl') eq 'PickupLibrary') {
+ $branch_count_query .= " AND issues.branchcode = ? ";
+ push @bind_params, $branch;
+ } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
+ ; # if branch is the patron's home branch, then count all loans by patron
+ } else {
+ $branch_count_query .= " AND items.homebranch = ? ";
+ push @bind_params, $branch;
}
- }
- # now checking for total
- $sth->execute( '*', '*', '*' );
- $result = $sth->fetchrow_hashref;
- if ( $result->{maxissueqty} ne '' ) {
- warn "checking total";
- $sth2->execute( $borrower->{'borrowernumber'}, $type );
- my $alreadyissued = $sth2->fetchrow;
- if ( $result->{'maxissueqty'} <= $alreadyissued ) {
- return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch / default category / total failed)" );
- }
- }
+ my $branch_count_sth = $dbh->prepare($branch_count_query);
+ $branch_count_sth->execute(@bind_params);
+ my ($current_loan_count) = $branch_count_sth->fetchrow_array;
+
+ my $max_loans_allowed = $branch_borrower_circ_rule->{maxissueqty};
+ if ($current_loan_count >= $max_loans_allowed) {
+ return "$current_loan_count / $max_loans_allowed";
+ }
+ }
# OK, the patron can issue !!!
return;
}
else {
if ( $amount > 0 ) {
- $needsconfirmation{DEBT} = $amount;
+ $needsconfirmation{DEBT} = sprintf( "%.2f", $amount );
}
}
my ( $restype, $res ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
if ($restype) {
my $resbor = $res->{'borrowernumber'};
- my ( $resborrower, $flags ) = GetMemberDetails( $resbor, 0 );
+ my ( $resborrower ) = GetMemberDetails( $resbor, 0 );
my $branches = GetBranches();
my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" )
my $barcodecheck=CheckValidBarcode($barcode);
if ($borrower and $barcode and $barcodecheck ne '0'){
# find which item we issue
- my $item = GetItem('', $barcode);
+ my $item = GetItem('', $barcode) or return undef; # if we don't get an Item, abort.
my $datedue;
-
my $branch;
# Get which branchcode we need
if (C4::Context->preference('CircControl') eq 'PickupLibrary'){
# warn "Waiting";
# The item is on reserve and waiting, but has been
# reserved by some other patron.
- my ( $resborrower, $flags ) = GetMemberDetails( $resbor, 0 );
- my $branches = GetBranches();
- my $branchname =
- $branches->{ $res->{'branchcode'} }->{'branchname'};
}
elsif ( $restype eq "Reserved" ) {
# warn "Reserved";
# The item is reserved by someone else.
- my ( $resborrower, $flags ) =
- GetMemberDetails( $resbor, 0 );
- my $branches = GetBranches();
- my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
if ($cancelreserve) { # cancel reserves on this item
CancelReserve( 0, $res->{'itemnumber'},
$res->{'borrowernumber'} );
C4::Context->userenv->{'branch'},
'issue', $charge,
'', $item->{'itemnumber'},
- $item->{'itemtype'}, $borrower->{'borrowernumber'}
+ $item->{'itype'}, $borrower->{'borrowernumber'}
);
}
}
}
+=head2 ForceIssue
+
+ForceIssue()
+
+Issues an item to a member, ignoring any problems that would normally dissallow the issue.
+
+=cut
+
+sub ForceIssue {
+ my ( $borrowernumber, $itemnumber, $date_due, $branchcode, $date ) = @_;
+warn "ForceIssue( $borrowernumber, $itemnumber, $date_due, $branchcode, $date );";
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare( "INSERT INTO `issues` ( `borrowernumber`, `itemnumber`, `date_due`, `branchcode`, `issuingbranch`, `returndate`, `lastreneweddate`, `return`, `renewals`, `timestamp`, `issuedate` )
+ VALUES ( ?, ?, ?, ?, ?, NULL, NULL, NULL, NULL, NOW(), ? )" );
+ $sth->execute( $borrowernumber, $itemnumber, $date_due, $branchcode, $branchcode, $date );
+ $sth->finish();
+
+ my $item = GetBiblioFromItemNumber( $itemnumber );
+
+ UpdateStats( $branchcode, 'issue', undef, undef, $itemnumber, $item->{ 'itemtype' }, $borrowernumber );
+}
+
+
=head2 GetLoanLength
Get loan length for an itemtype, a borrower type and a branch
return $loanlength->{issuelength}
if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
- $sth->execute( $borrowertype, $itemtype, "*" );
+ $sth->execute( $borrowertype, "*", $branchcode );
$loanlength = $sth->fetchrow_hashref;
return $loanlength->{issuelength}
if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
- $sth->execute( $borrowertype, "*", $branchcode );
+ $sth->execute( "*", $itemtype, $branchcode );
$loanlength = $sth->fetchrow_hashref;
return $loanlength->{issuelength}
if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
- $sth->execute( "*", $itemtype, $branchcode );
+ $sth->execute( "*", "*", $branchcode );
$loanlength = $sth->fetchrow_hashref;
return $loanlength->{issuelength}
if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
- $sth->execute( $borrowertype, "*", "*" );
+ $sth->execute( $borrowertype, $itemtype, "*" );
$loanlength = $sth->fetchrow_hashref;
return $loanlength->{issuelength}
if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
- $sth->execute( "*", "*", $branchcode );
+ $sth->execute( $borrowertype, "*", "*" );
$loanlength = $sth->fetchrow_hashref;
return $loanlength->{issuelength}
if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
$irule = $sth->fetchrow_hashref;
return $irule if defined($irule) ;
- $sth->execute( $borrowertype, $itemtype, "*" );
+ $sth->execute( $borrowertype, "*", $branchcode );
$irule = $sth->fetchrow_hashref;
return $irule if defined($irule) ;
- $sth->execute( $borrowertype, "*", $branchcode );
+ $sth->execute( "*", $itemtype, $branchcode );
$irule = $sth->fetchrow_hashref;
return $irule if defined($irule) ;
- $sth->execute( "*", $itemtype, $branchcode );
+ $sth->execute( "*", "*", $branchcode );
$irule = $sth->fetchrow_hashref;
return $irule if defined($irule) ;
- $sth->execute( $borrowertype, "*", "*" );
+ $sth->execute( $borrowertype, $itemtype, "*" );
$irule = $sth->fetchrow_hashref;
return $irule if defined($irule) ;
- $sth->execute( "*", "*", $branchcode );
+ $sth->execute( $borrowertype, "*", "*" );
$irule = $sth->fetchrow_hashref;
return $irule if defined($irule) ;
return undef;
}
+=head2 GetBranchBorrowerCircRule
+
+=over 4
+
+my $branch_cat_rule = GetBranchBorrowerCircRule($branchcode, $categorycode);
+
+=back
+
+Retrieves circulation rule attributes that apply to the given
+branch and patron category, regardless of item type.
+The return value is a hashref containing the following key:
+
+maxissueqty - maximum number of loans that a
+patron of the given category can have at the given
+branch. If the value is undef, no limit.
+
+This will first check for a specific branch and
+category match from branch_borrower_circ_rules.
+
+If no rule is found, it will then check default_branch_circ_rules
+(same branch, default category). If no rule is found,
+it will then check default_borrower_circ_rules (default
+branch, same category), then failing that, default_circ_rules
+(default branch, default category).
+
+If no rule has been found in the database, it will default to
+the buillt in rule:
+
+maxissueqty - undef
+
+C<$branchcode> and C<$categorycode> should contain the
+literal branch code and patron category code, respectively - no
+wildcards.
+
+=cut
+
+sub GetBranchBorrowerCircRule {
+ my $branchcode = shift;
+ my $categorycode = shift;
+
+ my $branch_cat_query = "SELECT maxissueqty
+ FROM branch_borrower_circ_rules
+ WHERE branchcode = ?
+ AND categorycode = ?";
+ my $dbh = C4::Context->dbh();
+ my $sth = $dbh->prepare($branch_cat_query);
+ $sth->execute($branchcode, $categorycode);
+ my $result;
+ if ($result = $sth->fetchrow_hashref()) {
+ return $result;
+ }
+
+ # try same branch, default borrower category
+ my $branch_query = "SELECT maxissueqty
+ FROM default_branch_circ_rules
+ WHERE branchcode = ?";
+ $sth = $dbh->prepare($branch_query);
+ $sth->execute($branchcode);
+ if ($result = $sth->fetchrow_hashref()) {
+ return $result;
+ }
+
+ # try default branch, same borrower category
+ my $category_query = "SELECT maxissueqty
+ FROM default_borrower_circ_rules
+ WHERE categorycode = ?";
+ $sth = $dbh->prepare($category_query);
+ $sth->execute($categorycode);
+ if ($result = $sth->fetchrow_hashref()) {
+ return $result;
+ }
+
+ # try default branch, default borrower category
+ my $default_query = "SELECT maxissueqty
+ FROM default_circ_rules";
+ $sth = $dbh->prepare($default_query);
+ $sth->execute();
+ if ($result = $sth->fetchrow_hashref()) {
+ return $result;
+ }
+
+ # built-in default circulation rule
+ return {
+ maxissueqty => undef,
+ };
+}
+
=head2 AddReturn
($doreturn, $messages, $iteminformation, $borrower) =
C<$barcode> is the bar code of the book being returned. C<$branch> is
the code of the branch where the book is being returned. C<$exemptfine>
indicates that overdue charges for the item will be removed. C<$dropbox>
-indicates that the check-in date is assumed to be yesterday. If overdue
+indicates that the check-in date is assumed to be yesterday, or the last
+non-holiday as defined in C4::Calendar . If overdue
charges are applied and C<$dropbox> is true, the last charge will be removed.
+This assumes that the fines accrual script has run for _today_.
C<&AddReturn> returns a list of four items:
# check if the book is in a permanent collection....
my $hbr = $iteminformation->{C4::Context->preference("HomeOrHoldingBranch")};
my $branches = GetBranches();
+ # FIXME -- This 'PE' attribute is largely undocumented. afaict, there's no user interface that reflects this functionality.
if ( $hbr && $branches->{$hbr}->{'PE'} ) {
$messages->{'IsPermanent'} = $hbr;
}
# case of a return of document (deal with issues and holdingbranch)
if ($doreturn) {
+ my $circControlBranch;
if($dropbox) {
- # don't allow dropbox mode to create an invalid entry in issues ( issuedate > returndate)
+ # don't allow dropbox mode to create an invalid entry in issues (issuedate > returndate) FIXME: actually checks eq, not gt
undef($dropbox) if ( $iteminformation->{'issuedate'} eq C4::Dates->today('iso') );
+ if (C4::Context->preference('CircControl') eq 'ItemHomeBranch' ) {
+ $circControlBranch = $iteminformation->{homebranch};
+ } elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary') {
+ $circControlBranch = $borrower->{branchcode};
+ } else { # CircControl must be PickupLibrary.
+ $circControlBranch = $iteminformation->{holdingbranch};
+ # FIXME - is this right ? are we sure that the holdingbranch is still the pickup branch?
+ }
}
- MarkIssueReturned($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'},$dropbox);
+ MarkIssueReturned($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'},$circControlBranch);
$messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
}
return ( $doreturn, $messages, $iteminformation, $borrower );
}
+=head2 ForceReturn
+
+ForceReturn( $barcode, $date, $branchcode );
+
+Returns an item is if it were returned on C<$date>.
+
+This function is non-interactive and does not check for reserves.
+
+C<$barcode> is the barcode of the item being returned.
+
+C<$date> is the date of the actual return, in the format YYYY-MM-DD.
+
+C<$branchcode> is the branchcode for the library the item was returned to.
+
+=cut
+
+sub ForceReturn {
+ my ( $barcode, $date, $branchcode ) = @_;
+ my $dbh = C4::Context->dbh;
+
+ my $item = GetBiblioFromItemNumber( undef, $barcode );
+
+ ## FIXME: Is there a way to get the borrower of an item through the Koha API?
+ my $sth=$dbh->prepare( "SELECT borrowernumber FROM issues WHERE itemnumber = ? AND returndate IS NULL");
+ $sth->execute( $item->{'itemnumber'} );
+ my ( $borrowernumber ) = $sth->fetchrow;
+ $sth->finish();
+
+ ## Move the issue from issues to old_issues
+ $sth = $dbh->prepare( "INSERT INTO old_issues ( SELECT * FROM issues WHERE itemnumber = ? AND returndate IS NULL )" );
+ $sth->execute( $item->{'itemnumber'} );
+ $sth->finish();
+ ## Delete the row in issues
+ $sth = $dbh->prepare( "DELETE FROM issues WHERE itemnumber = ? AND returndate IS NULL" );
+ $sth->execute( $item->{'itemnumber'} );
+ $sth->finish();
+ ## Now set the returndate
+ $sth = $dbh->prepare( 'UPDATE old_issues SET returndate = ? WHERE itemnumber = ? AND returndate IS NULL' );
+ $sth->execute( $date, $item->{'itemnumber'} );
+ $sth->finish();
+
+ UpdateStats( $branchcode, 'return', my $amount, my $other, $item->{ 'itemnumber' }, $item->{ 'itemtype' }, $borrowernumber );
+}
+
+
=head2 MarkIssueReturned
=over 4
-MarkIssueReturned($borrowernumber, $itemnumber);
+MarkIssueReturned($borrowernumber, $itemnumber, $dropbox_branch);
=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, or
-yesterday if C<dropbox> is true. Assumes you've
-already checked that yesterday > issuedate.
+the last non-holiday date of the branccode specified in
+C<dropbox> . Assumes you've already checked that
+it's safe to do this, i.e. last non-holiday > issuedate.
Ideally, this function would be internal to C<C4::Circulation>,
not exported, but it is currently needed by one
=cut
sub MarkIssueReturned {
- my ($borrowernumber, $itemnumber, $dropbox) = @_;
+ my ($borrowernumber, $itemnumber, $dropbox_branch ) = @_;
my $dbh = C4::Context->dbh;
my $query = "UPDATE issues SET returndate=";
my @bind = ($borrowernumber,$itemnumber);
- if($dropbox) {
- my @datearr = localtime( time() );
- my @yesterdayarr = Add_Delta_Days( $datearr[5] + 1900 , $datearr[4] + 1, $datearr[3] , -1 );
- unshift @bind, sprintf("%0.4d-%0.2d-%0.2d",@yesterdayarr) ;
+ if($dropbox_branch) {
+ my $calendar = C4::Calendar->new( branchcode => $dropbox_branch );
+ my $dropboxdate = $calendar->addDate(C4::Dates->new(), -1 );
+ unshift @bind, $dropboxdate->output('iso') ;
$query .= " ? "
} else {
$query .= " now() ";
=head2 FixOverduesOnReturn
- &FixOverduesOnReturn($brn,$itm, $exemptfine);
+ &FixOverduesOnReturn($brn,$itm, $exemptfine, $dropboxmode);
C<$brn> borrowernumber
C<$itm> itemnumber
+C<$exemptfine> BOOL -- remove overdue charge associated with this issue.
+C<$dropboxmode> BOOL -- remove lastincrement on overdue charge associated with this issue.
+
internal function, called only by AddReturn
=cut
sub FixOverduesOnReturn {
- my ( $borrowernumber, $item, $exemptfine ) = @_;
+ my ( $borrowernumber, $item, $exemptfine, $dropbox ) = @_;
my $dbh = C4::Context->dbh;
# check for overdue fine
# alter fine to show that the book has been returned
my $data;
if ($data = $sth->fetchrow_hashref) {
- my $uquery =($exemptfine)? "update accountlines set accounttype='FFOR', amountoutstanding=0":"update accountlines set accounttype='F' ";
+ my $uquery;
+ my @bind = ($borrowernumber,$item ,$data->{'accountno'});
+ if ($exemptfine) {
+ $uquery = "update accountlines set accounttype='FFOR', amountoutstanding=0";
+ if (C4::Context->preference("FinesLog")) {
+ &logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item");
+ }
+ } elsif ($dropbox && $data->{lastincrement}) {
+ my $outstanding = $data->{amountoutstanding} - $data->{lastincrement} ;
+ my $amt = $data->{amount} - $data->{lastincrement} ;
+ if (C4::Context->preference("FinesLog")) {
+ &logaction("FINES", 'MODIFY',$borrowernumber,"Dropbox adjustment $amt, item $item");
+ }
+ $uquery = "update accountlines set accounttype='F' ";
+ if($outstanding >= 0 && $amt >=0) {
+ $uquery .= ", amount = ? , amountoutstanding=? ";
+ unshift @bind, ($amt, $outstanding) ;
+ }
+ } else {
+ $uquery = "update accountlines set accounttype='F' ";
+ }
$uquery .= " where (borrowernumber = ?) and (itemnumber = ?) and (accountno = ?)";
my $usth = $dbh->prepare($uquery);
- $usth->execute($borrowernumber,$item ,$data->{'accountno'});
+ $usth->execute(@bind);
$usth->finish();
}
sub FixAccountForLostAndReturned {
my ($iteminfo, $borrower) = @_;
- my %env;
my $dbh = C4::Context->dbh;
my $itm = $iteminfo->{'itemnumber'};
# check for charge made for lost book
$usth->execute($data->{'borrowernumber'},$itm,$acctno);
$usth->finish;
#check if any credit is left if so writeoff other accounts
- my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
+ my $nextaccntno = getnextacctno($data->{'borrowernumber'});
if ($amountleft < 0){
$amountleft*=-1;
}
return ($data);
}
+=head2 GetOpenIssue
+
+$issue = GetOpenIssue( $itemnumber );
+
+Returns the row from the issues table if the item is currently issued, undef if the item is not currently issued
+
+C<$itemnumber> is the item's itemnumber
+
+Returns a hashref
+
+=cut
+
+sub GetOpenIssue {
+ my ( $itemnumber ) = @_;
+
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare( "SELECT * FROM issues WHERE itemnumber = ? AND returndate IS NULL" );
+ $sth->execute( $itemnumber );
+ my $issue = $sth->fetchrow_hashref();
+ return $issue;
+}
+
=head2 GetItemIssues
$issues = &GetItemIssues($itemnumber, $history);
return \@issues;
}
+=head2 GetUpcomingDueIssues
+
+=over 4
+
+my $upcoming_dues = GetUpcomingDueIssues( { days_in_advance => 4 } );
+
+=back
+
+=cut
+
+sub GetUpcomingDueIssues {
+ my $params = shift;
+
+ $params->{'days_in_advance'} = 7 unless exists $params->{'days_in_advance'};
+ my $dbh = C4::Context->dbh;
+
+ my $statement = <<END_SQL;
+SELECT issues.*, items.itype as itemtype, items.homebranch, TO_DAYS( date_due )-TO_DAYS( NOW() ) as days_until_due
+FROM issues
+LEFT JOIN items USING (itemnumber)
+WhERE returndate is NULL
+AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) < ?
+END_SQL
+
+ my @bind_parameters = ( $params->{'days_in_advance'} );
+
+ my $sth = $dbh->prepare( $statement );
+ $sth->execute( @bind_parameters );
+ my $upcoming_dues = $sth->fetchall_arrayref({});
+ $sth->finish;
+
+ return $upcoming_dues;
+}
+
=head2 CanBookBeRenewed
($ok,$error) = &CanBookBeRenewed($borrowernumber, $itemnumber);
# Update the issues record to have the new due date, and a new count
# of how many times it has been renewed.
my $renews = $issuedata->{'renewals'} + 1;
- $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?
+ $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = CURRENT_DATE
WHERE borrowernumber=?
AND itemnumber=?"
);
$sth->finish;
}
# Log the renewal
- UpdateStats( $branch, 'renew', $charge, '', $itemnumber );
+ UpdateStats( $branch, 'renew', $charge, '', $itemnumber, $item->{itype}, $borrowernumber);
}
+
+=head2 ForceRenewal
+
+ForRenewal( $itemnumber, $date, $date_due );
+
+Renews an item for the given date. This function should only be used to update renewals that have occurred in the past.
+
+C<$itemnumber> is the itemnumber of the item being renewed.
+
+C<$date> is the date the renewal took place, in the format YYYY-MM-DD
+
+C<$date_due> is the date the item is now due to be returned, in the format YYYY-MM-DD
+
+=cut
+
+sub ForceRenewal {
+ my ( $itemnumber, $date, $date_due ) = @_;
+ my $dbh = C4::Context->dbh;
+
+ my $sth = $dbh->prepare("SELECT * FROM issues WHERE itemnumber = ? AND returndate IS NULL");
+ $sth->execute( $itemnumber );
+ my $issue = $sth->fetchrow_hashref();
+ $sth->finish();
+
+
+ $sth = $dbh->prepare('UPDATE issues SET renewals = ?, lastreneweddate = ?, date_due = ? WHERE itemnumber = ? AND returndate IS NULL');
+ $sth->execute( $issue->{'renewals'} + 1, $date, $date_due, $itemnumber );
+ $sth->finish();
+
+ my $item = GetBiblioFromItemNumber( $itemnumber );
+ UpdateStats( $issue->{'branchcode'}, 'renew', undef, undef, $itemnumber, $item->{ 'itemtype' }, $issue->{'borrowernumber'} );
+}
+
+
sub GetRenewCount {
# check renewal status
my ($bornum,$itemno)=@_;