From f9df789f2fae81fca766bdcbab5041c9c00a1aa0 Mon Sep 17 00:00:00 2001
From: Chris Cormack
Date: Wed, 16 Mar 2011 13:34:34 +1300
Subject: [PATCH] Bug 5872 : Circulation management enhancements
See http://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=5872 for
details
---
C4/Circulation.pm | 932 +++++++++-----------
C4/Overdues.pm | 81 +--
C4/Reserves.pm | 631 ++++++++++----
admin/branch_transfer_limits.pl | 22 +-
admin/smart-rules.pl | 565 +++----------
circ/branchtransfers.pl | 4 +-
circ/circulation.pl | 282 +++++--
circ/returns.pl | 137 +++-
.../en/modules/admin/branch_transfer_limits.tmpl | 15 +-
.../prog/en/modules/admin/clone-rules.tmpl | 3 +-
.../prog/en/modules/admin/smart-rules.tmpl | 639 ++++++++------
.../prog/en/modules/circ/circulation.tmpl | 439 ++++++----
.../prog/en/modules/circ/returns.tmpl | 151 ++--
.../prog/en/modules/reserve/request.tmpl | 30 +-
.../opac-tmpl/prog/en/modules/opac-reserve.tmpl | 93 ++-
members/member.pl | 22 +-
opac/opac-modrequest.pl | 4 +-
opac/opac-reserve.pl | 63 +-
reserve/modrequest.pl | 19 +-
reserve/placerequest.pl | 92 +-
reserve/renewscript.pl | 57 ++-
reserve/request.pl | 48 +-
t/db_dependent/lib/KohaTest/Circulation.pm | 2 -
.../lib/KohaTest/Members/GetMemberDetails.pm | 6 +-
t/db_dependent/lib/KohaTest/Overdues.pm | 1 -
25 files changed, 2379 insertions(+), 1959 deletions(-)
diff --git a/C4/Circulation.pm b/C4/Circulation.pm
index 2b4e86c..0c8fb5b 100644
--- a/C4/Circulation.pm
+++ b/C4/Circulation.pm
@@ -27,13 +27,16 @@ use C4::Koha;
use C4::Biblio;
use C4::Items;
use C4::Members;
-use C4::Dates;
+use C4::IssuingRules;
+use C4::Dates qw/format_date/;
use C4::Calendar;
use C4::Accounts;
+use C4::Overdues ;
use C4::ItemCirculationAlertPreference;
use C4::Dates qw(format_date);
use C4::Message;
use C4::Debug;
+use YAML;
use Date::Calc qw(
Today
Today_and_Now
@@ -42,6 +45,9 @@ use Date::Calc qw(
Date_to_Days
Day_of_Week
Add_Delta_Days
+ Delta_Days
+ check_date
+ Add_Delta_Days
);
use POSIX qw(strftime);
use C4::Branch; # GetBranches
@@ -73,9 +79,6 @@ BEGIN {
&GetItemIssues
&GetBorrowerIssues
&GetIssuingCharges
- &GetIssuingRule
- &GetBranchBorrowerCircRule
- &GetBranchItemRule
&GetBiblioIssues
&GetOpenIssue
&AnonymiseIssueHistory
@@ -98,6 +101,15 @@ BEGIN {
&CreateBranchTransferLimit
&DeleteBranchTransferLimits
);
+
+ # subs to deal with offline circulation
+ push @EXPORT, qw(
+ &GetOfflineOperations
+ &GetOfflineOperation
+ &AddOfflineOperation
+ &DeleteOfflineOperation
+ &ProcessOfflineOperation
+ );
}
=head1 NAME
@@ -346,116 +358,97 @@ sub TooMany {
my $item = shift;
my $cat_borrower = $borrower->{'categorycode'};
my $dbh = C4::Context->dbh;
- my $branch;
+ my $exactbranch;
# Get which branchcode we need
- $branch = _GetCircControlBranch($item,$borrower);
- my $type = (C4::Context->preference('item-level_itypes'))
+ $exactbranch = _GetCircControlBranch($item,$borrower);
+ my $itype = (C4::Context->preference('item-level_itypes'))
? $item->{'itype'} # item-level
: $item->{'itemtype'}; # biblio-level
# 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;
- }
-
- $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;
- }
- }
-
- my $count_sth = $dbh->prepare($count_query);
- $count_sth->execute(@bind_params);
- my ($current_loan_count) = $count_sth->fetchrow_array;
+ my $branchfield = C4::Context->Preference('HomeOrHoldingBranch') || "homebranch";
+ #By default, Patron is supposed not to be able to borrow
+ my $toomany = 1;
+
+ foreach my $branch ( $exactbranch, '*' ) {
+ foreach my $type ( $itype, '*' ) {
+ 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 all the items issued for that branch no
+ # those existing loans that don't fall under a more
+ # specific rule Not QUITE
+ 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;
+ }
- my $max_loans_allowed = $issuing_rule->{'maxissueqty'};
- if ($current_loan_count >= $max_loans_allowed) {
- return ($current_loan_count, $max_loans_allowed);
- }
- }
+ $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.$branchfield = ? ";
+ push @bind_params, $branch;
+ }
+ }
- # 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;
- }
- 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 $count_sth = $dbh->prepare($count_query);
+ $count_sth->execute(@bind_params);
+ my ($current_loan_count) = $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);
+ my $max_loans_allowed = $issuing_rule->{'maxissueqty'};
+ if ( $current_loan_count >= $max_loans_allowed ) {
+ return 1,$current_loan_count,$max_loans_allowed;
+ }
+ else {
+ $toomany=0;
+ }
+ }
}
}
-
- # OK, the patron can issue !!!
- return;
+ return $toomany;
}
=head2 itemissues
@@ -684,7 +677,10 @@ sub CanBookBeIssued {
my $branch = _GetCircControlBranch($item,$borrower);
my $itype = ( C4::Context->preference('item-level_itypes') ) ? $item->{'itype'} : $biblioitem->{'itemtype'};
my $loanlength = GetLoanLength( $borrower->{'categorycode'}, $itype, $branch );
- $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower );
+ unless ($loanlength or C4::Context->preference("AllowNotForLoanOverride")) {
+ $issuingimpossible{LOAN_LENGTH_UNDEFINED} = "$borrower->{'categorycode'}, $itype, $branch";
+ }
+ $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower ) ;
# Offline circ calls AddIssue directly, doesn't run through here
# So issuingimpossible should be ok.
@@ -712,15 +708,22 @@ sub CanBookBeIssued {
$issuingimpossible{CARD_LOST} = 1;
}
if ( $borrower->{flags}->{'DBARRED'} ) {
- $issuingimpossible{DEBARRED} = 1;
+ if (my $dateenddebarred=$borrower->{flags}->{'DBARRED'}->{'dateend'}){
+ $issuingimpossible{DEBARRED} = format_date($dateenddebarred);
+ }
+ else {
+ $issuingimpossible{DEBARRED} = 1;
+ }
}
if ( $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 @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;
}
}
#
@@ -769,18 +772,15 @@ sub CanBookBeIssued {
#
# JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
#
- 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) {
+ my @toomany = TooMany( $borrower, $item->{biblionumber}, $item );
+
+ if ( $toomany[0] == 1 && scalar(@toomany)<3) {
$needsconfirmation{PATRON_CANT} = 1;
- } else {
- if($max_loans_allowed){
- $needsconfirmation{TOO_MANY} = 1;
- $needsconfirmation{current_loan_count} = $current_loan_count;
- $needsconfirmation{max_loans_allowed} = $max_loans_allowed;
- }
+ } elsif (scalar(@toomany)==3) {
+ $needsconfirmation{TOO_MANY} = "$toomany[1] / $toomany[2]";
}
+
#
# ITEM CHECKING
#
@@ -817,8 +817,18 @@ sub CanBookBeIssued {
}
}
}
- if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 )
- {
+ if ( $item->{'damaged'} && $item->{'damaged'} > 0 ){
+ $needsconfirmation{DAMAGED} = $item->{'damaged'};
+ my $fw = GetFrameworkCode($item->{biblionumber});
+ my $category = GetAuthValCode('items.damaged',$fw);
+ my $authorizedvalues = GetAuthorisedValues($category, $item->{damaged});
+
+ foreach my $authvalue (@$authorizedvalues){
+ $needsconfirmation{DAMAGED} = $authvalue->{lib} if $authvalue->{'authorised_value'} eq $item->{'damaged'};
+ }
+
+ }
+ if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 ) {
$issuingimpossible{WTHDRAWN} = 1;
}
if ( $item->{'restricted'}
@@ -826,15 +836,23 @@ sub CanBookBeIssued {
{
$issuingimpossible{RESTRICTED} = 1;
}
+ my $userenv = C4::Context->userenv;
+ my $branch=$userenv->{branch};
+ my $hbr= $item->{ C4::Context->preference("HomeOrHoldingBranch") };
if ( C4::Context->preference("IndependantBranches") ) {
- my $userenv = C4::Context->userenv;
if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
$issuingimpossible{ITEMNOTSAMEBRANCH} = 1
- if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} );
+ if ( $hbr ne $branch );
$needsconfirmation{BORRNOTSAMEBRANCH} = GetBranchName( $borrower->{'branchcode'} )
if ( $borrower->{'branchcode'} ne $userenv->{branch} );
}
}
+ my $branchtransferfield=C4::Context->preference("BranchTransferLimitsType") eq "ccode" ? "ccode" : "itype";
+ if ( C4::Context->preference("UseBranchTransferLimits")
+ and !IsBranchTransferAllowed( $branch, $hbr, $item->{ $branchtransferfield } ) ) {
+ $needsconfirmation{BRANCH_TRANSFER_NOT_ALLOWED} = $hbr;
+ }
+
#
# CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
@@ -875,30 +893,21 @@ sub CanBookBeIssued {
my ( $resborrower ) = C4::Members::GetMemberDetails( $resbor, 0 );
my $branches = GetBranches();
my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
- if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" )
- {
- # The item is on reserve and waiting, but has been
- # reserved by some other patron.
- $needsconfirmation{RESERVE_WAITING} = 1;
- $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'};
- $needsconfirmation{'ressurname'} = $resborrower->{'surname'};
- $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'};
- $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'};
- $needsconfirmation{'resbranchname'} = $branchname;
- $needsconfirmation{'reswaitingdate'} = format_date($res->{'waitingdate'});
- }
- elsif ( $restype eq "Reserved" ) {
- # The item is on reserve for someone else.
- $needsconfirmation{RESERVED} = 1;
- $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'};
- $needsconfirmation{'ressurname'} = $resborrower->{'surname'};
- $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'};
- $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'};
- $needsconfirmation{'resbranchname'} = $branchname;
- $needsconfirmation{'resreservedate'} = format_date($res->{'reservedate'});
+ if( $resbor ne $borrower->{'borrowernumber'}){
+ if ( $restype eq "Waiting" ) {
+ # The item is on reserve and waiting, but has been
+ # reserved by some other patron.
+ $needsconfirmation{RESERVE_WAITING} =
+ "$resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}, $branchname)";
+ }
+ elsif ( $restype eq "Reserved" ) {
+ # The item is on reserve for someone else.
+ $needsconfirmation{RESERVED} =
+ "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
+ }
}
}
- return ( \%issuingimpossible, \%needsconfirmation );
+ return ( \%issuingimpossible, \%needsconfirmation );
}
=head2 AddIssue
@@ -979,7 +988,10 @@ sub AddIssue {
# who wants to borrow it now. mark it returned before issuing to the new borrower
AddReturn(
$item->{'barcode'},
- C4::Context->userenv->{'branch'}
+ C4::Context->userenv->{'branch'},
+ undef,
+ undef,
+ 1
);
}
@@ -1012,7 +1024,9 @@ sub AddIssue {
ModReserve(1,
$res->{'biblionumber'},
$res->{'borrowernumber'},
- $res->{'branchcode'}
+ $res->{'branchcode'},
+ undef,
+ $res->{'reservenumber'}
);
}
}
@@ -1102,10 +1116,11 @@ sub AddIssue {
branch => $branch,
});
}
+
+ logaction( "CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $item->{'itemnumber'} )
+ if C4::Context->preference("IssueLog");
}
- logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'biblionumber'})
- if C4::Context->preference("IssueLog");
}
return ($datedue); # not necessarily the same as when it came in!
}
@@ -1120,254 +1135,8 @@ Get loan length for an itemtype, a borrower type and a branch
sub GetLoanLength {
my ( $borrowertype, $itemtype, $branchcode ) = @_;
- my $dbh = C4::Context->dbh;
- my $sth =
- $dbh->prepare(
-"select issuelength 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';
-
- $sth->execute( $borrowertype, "*", $branchcode );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- $sth->execute( "*", $itemtype, $branchcode );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- $sth->execute( "*", "*", $branchcode );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- $sth->execute( $borrowertype, $itemtype, "*" );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- $sth->execute( $borrowertype, "*", "*" );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- $sth->execute( "*", $itemtype, "*" );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- $sth->execute( "*", "*", "*" );
- $loanlength = $sth->fetchrow_hashref;
- return $loanlength->{issuelength}
- if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
-
- # if no rule is set => 21 days (hardcoded)
- return 21;
-}
-
-=head2 GetIssuingRule
-
- my $irule = &GetIssuingRule($borrowertype,$itemtype,branchcode)
-
-FIXME - This is a copy-paste of GetLoanLength
-as a stop-gap. Do not wish to change API for GetLoanLength
-this close to release, however, Overdues::GetIssuingRules is broken.
-
-Get the issuing rule for an itemtype, a borrower type and a branch
-Returns a hashref from the issuingrules table.
-
-=cut
-
-sub GetIssuingRule {
- my ( $borrowertype, $itemtype, $branchcode ) = @_;
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare( "select * from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null" );
- my $irule;
-
- $sth->execute( $borrowertype, $itemtype, $branchcode );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( $borrowertype, "*", $branchcode );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( "*", $itemtype, $branchcode );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( "*", "*", $branchcode );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( $borrowertype, $itemtype, "*" );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( $borrowertype, "*", "*" );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( "*", $itemtype, "*" );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- $sth->execute( "*", "*", "*" );
- $irule = $sth->fetchrow_hashref;
- return $irule if defined($irule) ;
-
- # if no rule matches,
- return undef;
-}
-
-=head2 GetBranchBorrowerCircRule
-
- my $branch_cat_rule = GetBranchBorrowerCircRule($branchcode, $categorycode);
-
-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 GetBranchItemRule
-
- my $branch_item_rule = GetBranchItemRule($branchcode, $itemtype);
-
-Retrieves circulation rule attributes that apply to the given
-branch and item type, regardless of patron category.
-
-The return value is a hashref containing the following key:
-
-holdallowed => Hold policy for this branch and itemtype. Possible values:
- 0: No holds allowed.
- 1: Holds allowed only by patrons that have the same homebranch as the item.
- 2: Holds allowed from any patron.
-
-This searches branchitemrules in the following order:
-
- * Same branchcode and itemtype
- * Same branchcode, itemtype '*'
- * branchcode '*', same itemtype
- * branchcode and itemtype '*'
-
-Neither C<$branchcode> nor C<$categorycode> should be '*'.
-
-=cut
-
-sub GetBranchItemRule {
- my ( $branchcode, $itemtype ) = @_;
- my $dbh = C4::Context->dbh();
- my $result = {};
-
- my @attempts = (
- ['SELECT holdallowed
- FROM branch_item_rules
- WHERE branchcode = ?
- AND itemtype = ?', $branchcode, $itemtype],
- ['SELECT holdallowed
- FROM default_branch_circ_rules
- WHERE branchcode = ?', $branchcode],
- ['SELECT holdallowed
- FROM default_branch_item_rules
- WHERE itemtype = ?', $itemtype],
- ['SELECT holdallowed
- FROM default_circ_rules'],
- );
-
- foreach my $attempt (@attempts) {
- my ($query, @bind_params) = @{$attempt};
-
- # Since branch/category and branch/itemtype use the same per-branch
- # defaults tables, we have to check that the key we want is set, not
- # just that a row was returned
- return $result if ( defined( $result->{'holdallowed'} = $dbh->selectrow_array( $query, {}, @bind_params ) ) );
- }
-
- # built-in default circulation rule
- return {
- holdallowed => 2,
- };
+ my $loanlength=GetIssuingRule($borrowertype,$itemtype,$branchcode);
+ return $loanlength->{issuelength};
}
=head2 AddReturn
@@ -1445,8 +1214,8 @@ patron who last borrowed the book.
=cut
sub AddReturn {
- my ( $barcode, $branch, $exemptfine, $dropbox ) = @_;
- if ($branch and not GetBranchDetail($branch)) {
+ my ( $barcode, $branch, $exemptfine, $dropbox, $force) = @_;
+ if ( $branch and not GetBranchDetail($branch) ) {
warn "AddReturn error: branch '$branch' not found. Reverting to " . C4::Context->userenv->{'branch'};
undef $branch;
}
@@ -1490,9 +1259,17 @@ sub AddReturn {
my $branches = GetBranches(); # a potentially expensive call for a non-feature.
$branches->{$hbr}->{PE} and $messages->{'IsPermanent'} = $hbr;
}
-
+ my $branchtransferfield=C4::Context->preference("BranchTransferLimitsType") eq "ccode" ? "ccode" : "itype";
+ $debug && warn "$branch, $hbr, ",C4::Context->preference("BranchTransferLimitsType")," ,",$item->{ $branchtransferfield } ;
+ $debug && warn Dump($item);
+ $debug && warn IsBranchTransferAllowed( $branch, $hbr, $item->{ C4::Context->preference("BranchTransferLimitsType") } );
# if indy branches and returning to different branch, refuse the return
- if ($hbr ne $branch && C4::Context->preference("IndependantBranches")){
+ if ( !$force && ($hbr ne $branch)
+ && (C4::Context->preference("IndependantBranches")
+ or ( C4::Context->preference("UseBranchTransferLimits")
+ and !IsBranchTransferAllowed( $branch, $hbr, $item->{$branchtransferfield } ) )
+ )
+ ){
$messages->{'Wrongbranch'} = {
Wrongbranch => $branch,
Rightbranch => $hbr,
@@ -1503,7 +1280,6 @@ sub AddReturn {
# FIXME - even in an indy branches situation, there should
# still be an option for the library to accept the item
# and transfer it to its owning library.
- return ( $doreturn, $messages, $issue, $borrower );
}
if ( $item->{'wthdrawn'} ) { # book has been cancelled
@@ -1514,12 +1290,11 @@ sub AddReturn {
# case of a return of document (deal with issues and holdingbranch)
if ($doreturn) {
$borrower or warn "AddReturn without current borrower";
- my $circControlBranch;
+ my $circControlBranch;
if ($dropbox) {
- # 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 );
+ # don't allow dropbox mode to create an invalid entry in issues (issuedate > returndate) FIXME: actually checks eq, not gt
+ undef($dropbox) if ( $item->{'issuedate'} eq C4::Dates->today('iso') );
}
if ($borrowernumber) {
@@ -1527,15 +1302,15 @@ sub AddReturn {
$messages->{'WasReturned'} = 1; # FIXME is the "= 1" right? This could be the borrower hash.
}
- ModItem({ onloan => undef }, $issue->{'biblionumber'}, $item->{'itemnumber'});
+ ModItem( { renewals=>0, onloan => undef }, $issue->{'biblionumber'}, $item->{'itemnumber'} );
+ # the holdingbranch is updated if the document is returned to another location.
+ # this is always done regardless of whether the item was on loan or not
+ if ( $item->{'holdingbranch'} ne $branch ) {
+ UpdateHoldingbranch( $branch, $item->{'itemnumber'} );
+ $item->{'holdingbranch'} = $branch; # update item data holdingbranch too
+ }
}
- # the holdingbranch is updated if the document is returned to another location.
- # this is always done regardless of whether the item was on loan or not
- if ($item->{'holdingbranch'} ne $branch) {
- UpdateHoldingbranch($branch, $item->{'itemnumber'});
- $item->{'holdingbranch'} = $branch; # update item data holdingbranch too
- }
ModDateLastSeen( $item->{'itemnumber'} );
# check if we have a transfer for this document
@@ -1562,11 +1337,26 @@ sub AddReturn {
_FixAccountForLostAndReturned($item->{'itemnumber'}, $borrowernumber, $barcode); # can tolerate undef $borrowernumber
$messages->{'WasLost'} = 1;
}
+ if ($item->{'notforloan'}){
+ $messages->{'NotForLoan'} = $item->{'notforloan'};
+ }
+ if ($item->{'damaged'}){
+ $messages->{'Damaged'} = $item->{'damaged'};
+ }
- # fix up the overdues in accounts...
- if ($borrowernumber) {
+ if ($borrowernumber && $doreturn) {
+ # fix up the overdues in accounts...
my $fix = _FixOverduesOnReturn($borrowernumber, $item->{itemnumber}, $exemptfine, $dropbox);
defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!"; # zero is OK, check defined
+
+ # fix fine days
+ my $debardate = _FixFineDaysOnReturn($borrower, $item, $issue->{date_due});
+ $messages->{'Debarred'} = $debardate if($debardate);
+
+ # get fines for the borrower
+ my $fineamount = C4::Overdues::GetFine($borrowernumber);
+ $messages->{'HaveFines'} = $fineamount if($fineamount);
+
}
# find reserves.....
@@ -1602,20 +1392,20 @@ sub AddReturn {
branch => $branch,
});
}
-
- logaction("CIRCULATION", "RETURN", $borrowernumber, $item->{'biblionumber'})
- if C4::Context->preference("ReturnLog");
-
+
+ logaction( "CIRCULATION", "RETURN", $borrowernumber, $item->{'itemnumber'} )
+ if C4::Context->preference("ReturnLog");
+
# FIXME: make this comment intelligible.
#adding message if holdingbranch is non equal a userenv branch to return the document to homebranch
#we check, if we don't have reserv or transfert for this document, if not, return it to homebranch .
- if ($doreturn and ($branch ne $hbr) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) ){
- if ( C4::Context->preference("AutomaticItemReturn" ) or
- (C4::Context->preference("UseBranchTransferLimits") and
- ! IsBranchTransferAllowed($branch, $hbr, $item->{C4::Context->preference("BranchTransferLimitsType")} )
- )) {
- $debug and warn sprintf "about to call ModItemTransfer(%s, %s, %s)", $item->{'itemnumber'},$branch, $hbr;
+ if ( $doreturn and ( $branch ne $hbr ) and not $messages->{'WrongTransfer'} and ( $validTransfert ne 1 ) ) {
+ if (C4::Context->preference("AutomaticItemReturn")
+ or ( C4::Context->preference("UseBranchTransferLimits")
+ and !IsBranchTransferAllowed( $branch, $hbr, $item->{ $branchtransferfield } ) )
+ ) {
+ $debug and warn sprintf "about to call ModItemTransfer(%s, %s, %s)", $item->{'itemnumber'}, $branch, $hbr;
$debug and warn "item: " . Dumper($item);
ModItemTransfer($item->{'itemnumber'}, $branch, $hbr);
$messages->{'WasTransfered'} = 1;
@@ -1690,6 +1480,61 @@ sub MarkIssueReturned {
$sth_del->execute($borrowernumber, $itemnumber);
}
+=head2 _FixFineDaysOnReturn
+
+ &_FixFineDaysOnReturn($borrower, $item, $datedue);
+
+C<$borrower> borrower hashref
+
+C<$item> item hashref
+
+C<$datedue> date due
+
+Internal function, called only by AddReturn that calculate and update the user fine days, and debars him
+
+=cut
+
+sub _FixFineDaysOnReturn {
+ my ($borrower, $item, $datedue) = @_;
+
+ if($datedue){
+ $datedue = C4::Dates->new($datedue,"iso");
+ }else{
+ return;
+ }
+
+ my $branchcode =_GetCircControlBranch($item, $borrower);
+ my $calendar = C4::Calendar->new( branchcode => $branchcode );
+ my $today = C4::Dates->new();
+
+ my $deltadays = $calendar->daysBetween($datedue, C4::Dates->new());
+
+ my $circcontrol = C4::Context::preference('CircControl');
+ my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode);
+ my $finedays = $issuingrule->{finedays};
+ # exit if no finedays defined
+ return unless $finedays;
+ my $grace = $issuingrule->{firstremind};
+
+ if( $deltadays - $grace > 0){
+ my @newdate = Add_Delta_Days(Today(), $deltadays * $finedays );
+ my $isonewdate = join('-',@newdate);
+ my ($deby, $debm, $debd) = split(/-/,$borrower->{debarred});
+ if(check_date($deby, $debm, $debd)){
+ my @olddate = split(/-/, $borrower->{debarred});
+
+ if(Delta_Days(@olddate,@newdate) > 0){
+ C4::Members::DebarMember($borrower->{borrowernumber}, $isonewdate);
+ return $isonewdate;
+ }
+ }else{
+ C4::Members::DebarMember($borrower->{borrowernumber}, $isonewdate);
+ return $isonewdate;
+ }
+ }
+}
+
+
=head2 _FixOverduesOnReturn
&_FixOverduesOnReturn($brn,$itm, $exemptfine, $dropboxmode);
@@ -1864,7 +1709,7 @@ sub _GetCircControlBranch {
my $circcontrol = C4::Context->preference('CircControl');
my $branch;
- if ($circcontrol eq 'PickupLibrary') {
+ if ($circcontrol eq 'PickupLibrary' && C4::Context->userenv->{'branch'}) {
$branch= C4::Context->userenv->{'branch'};
} elsif ($circcontrol eq 'PatronLibrary') {
$branch=$borrower->{branchcode};
@@ -1901,7 +1746,7 @@ sub GetItemIssue {
my ($itemnumber) = @_;
return unless $itemnumber;
my $sth = C4::Context->dbh->prepare(
- "SELECT *
+ "SELECT *, issues.renewals as 'issues.renewals'
FROM issues
LEFT JOIN items ON issues.itemnumber=items.itemnumber
WHERE issues.itemnumber=?");
@@ -1972,7 +1817,7 @@ sub GetItemIssues {
}
my $results = $sth->fetchall_arrayref({});
foreach (@$results) {
- $_->{'overdue'} = ($_->{'date_due'} lt $today) ? 1 : 0;
+ $_->{'overdue'} = ( $_->{'date_due'} lt $today && !defined($_->{return_date}) ) ? 1 : 0;
}
return $results;
}
@@ -2078,69 +1923,42 @@ already renewed the loan. $error will contain the reason the renewal can not pro
sub CanBookBeRenewed {
# check renewal status
- my ( $borrowernumber, $itemnumber, $override_limit ) = @_;
+ my ( $borrowernumber, $itemnumber ) = @_;
my $dbh = C4::Context->dbh;
my $renews = 1;
- my $renewokay = 0;
- my $error;
+ my $renewokay = 1;
+ my $error;
# Look in the issues table for this item, lent to this borrower,
# and not yet returned.
# Look in the issues table for this item, lent to this borrower,
# and not yet returned.
- my %branch = (
- 'ItemHomeLibrary' => 'items.homebranch',
- 'PickupLibrary' => 'items.holdingbranch',
- 'PatronLibrary' => 'borrowers.branchcode'
- );
- my $controlbranch = $branch{C4::Context->preference('CircControl')};
- my $itype = C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype';
+ my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef;
+ my $item = GetItem($itemnumber) or return undef;
+ my $itemissue = GetItemIssue($itemnumber) or return undef;
+ my $branchcode = _GetCircControlBranch($item, $borrower);
+ if ($itemissue->{'overdue'}){
+ $renewokay=0;
+ $error->{message}='overdue';
+ }
- my $sthcount = $dbh->prepare("
- SELECT
- borrowers.categorycode, biblioitems.itemtype, issues.renewals, renewalsallowed, $controlbranch
- FROM issuingrules,
- issues
- LEFT JOIN items USING (itemnumber)
- LEFT JOIN borrowers USING (borrowernumber)
- LEFT JOIN biblioitems USING (biblioitemnumber)
-
- WHERE
- (issuingrules.categorycode = borrowers.categorycode OR issuingrules.categorycode = '*')
- AND
- (issuingrules.itemtype = $itype OR issuingrules.itemtype = '*')
- AND
- (issuingrules.branchcode = $controlbranch OR issuingrules.branchcode = '*')
- AND
- borrowernumber = ?
- AND
- itemnumber = ?
- ORDER BY
- issuingrules.categorycode desc,
- issuingrules.itemtype desc,
- issuingrules.branchcode desc
- LIMIT 1;
- ");
-
- $sthcount->execute( $borrowernumber, $itemnumber );
- if ( my $data1 = $sthcount->fetchrow_hashref ) {
-
- if ( ( $data1->{renewalsallowed} && $data1->{renewalsallowed} > $data1->{renewals} ) || $override_limit ) {
- $renewokay = 1;
- }
- else {
- $error="too_many";
- }
-
- my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber);
- if ($resfound) {
- $renewokay = 0;
- $error="on_reserve"
- }
+ my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode);
+
+ if ( $issuingrule->{renewalsallowed} <= $itemissue->{'issues.renewals'} ) {
+ $renewokay=0;
+ $error->{message} = "too_many";
+ }
+ my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber);
+ if ($resfound) {
+ $renewokay = 0;
+ $error->{message} = "on_reserve";
}
- return ($renewokay,$error);
+ $error->{renewals} = $itemissue->{'issues.renewals'};
+ $error->{renewalsallowed}= $issuingrule->{renewalsallowed};
+
+ return ( $renewokay, $error );
}
=head2 AddRenewal
@@ -2195,22 +2013,28 @@ sub AddRenewal {
# based on the value of the RenewalPeriodBase syspref.
unless ($datedue) {
- my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef;
- my $loanlength = GetLoanLength(
- $borrower->{'categorycode'},
- (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ,
- $issuedata->{'branchcode'} ); # that's the circ control branch.
+ my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef;
+ my $branchcode = _GetCircControlBranch($item, $borrower);
+ my $loanlength = GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode );
+
+ $datedue =
+ ( C4::Context->preference('RenewalPeriodBase') eq 'date_due' )
+ ? C4::Dates->new( $issuedata->{date_due}, 'iso' )
+ : C4::Dates->new();
+
+ my $itype = ( C4::Context->preference('item-level_itypes') ) ? $biblio->{'itype'} : $biblio->{'itemtype'};
+ my $controlbranch = _GetCircControlBranch( $item, $borrower );
+ my $renewalperiod = $loanlength->{renewalperiod} || GetLoanLength( $borrower->{'categorycode'}, $itype, $controlbranch );
+
+ $datedue = CalcDateDue( $datedue, $renewalperiod, $issuedata->{'branchcode'}, $borrower );
- $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
- C4::Dates->new($issuedata->{date_due}, 'iso') :
- C4::Dates->new();
- $datedue = CalcDateDue($datedue,$loanlength,$issuedata->{'branchcode'},$borrower);
}
# 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 = ?, lastreneweddate = ?
+ $sth = $dbh->prepare(
+ "UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ?
WHERE borrowernumber=?
AND itemnumber=?"
);
@@ -2218,8 +2042,8 @@ sub AddRenewal {
$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);
+ $renews = $item->{'renewals'} + 1;
+ ModItem( { renewals => $renews, onloan => $datedue->output('iso') }, undef, $itemnumber );
# Charge a new rental fee, if applicable?
my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber );
@@ -2600,15 +2424,8 @@ sub CalcDateDue {
my ($startdate,$loanlength,$branch,$borrower) = @_;
my $datedue;
- 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);
- }
+ my $calendar = C4::Calendar->new( branchcode => $branch );
+ $datedue = $calendar->addDate($startdate, $loanlength);
# if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
if ( C4::Context->preference('ReturnBeforeExpiry') && $datedue->output('iso') gt $borrower->{dateexpiry} ) {
@@ -2818,18 +2635,121 @@ sub CreateBranchTransferLimit {
=head2 DeleteBranchTransferLimits
- DeleteBranchTransferLimits();
+ DeleteBranchTransferLimits($tobranch);
=cut
sub DeleteBranchTransferLimits {
- my $dbh = C4::Context->dbh;
- my $sth = $dbh->prepare("TRUNCATE TABLE branch_transfer_limits");
- $sth->execute();
+ my $branch = shift;
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("DELETE FROM branch_transfer_limits WHERE toBranch = ?");
+ $sth->execute($branch);
+}
+
+sub GetOfflineOperations {
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE branchcode=? ORDER BY timestamp");
+ $sth->execute(C4::Context->userenv->{'branch'});
+ my $results = $sth->fetchall_arrayref({});
+ $sth->finish;
+ return $results;
+}
+
+sub GetOfflineOperation {
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE operationid=?");
+ $sth->execute( shift );
+ my $result = $sth->fetchrow_hashref;
+ $sth->finish;
+ return $result;
+}
+
+sub AddOfflineOperation {
+ my $dbh = C4::Context->dbh;
+ warn Data::Dumper::Dumper(@_);
+ my $sth = $dbh->prepare("INSERT INTO pending_offline_operations VALUES('',?,?,?,?,?,?)");
+ $sth->execute( @_ );
+ return "Added.";
+}
+
+sub DeleteOfflineOperation {
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("DELETE FROM pending_offline_operations WHERE operationid=?");
+ $sth->execute( shift );
+ return "Deleted.";
+}
+
+sub ProcessOfflineOperation {
+ my $operation = shift;
+
+ my $report;
+ if ( $operation->{action} eq 'return' ) {
+ $report = ProcessOfflineReturn( $operation );
+ } elsif ( $operation->{action} eq 'issue' ) {
+ $report = ProcessOfflineIssue( $operation );
+ }
+
+ DeleteOfflineOperation( $operation->{operationid} ) if $operation->{operationid};
+
+ return $report;
+}
+
+sub ProcessOfflineReturn {
+ my $operation = shift;
+
+ my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} );
+
+ if ( $itemnumber ) {
+ my $issue = GetOpenIssue( $itemnumber );
+ if ( $issue ) {
+ MarkIssueReturned(
+ $issue->{borrowernumber},
+ $itemnumber,
+ undef,
+ $operation->{timestamp},
+ );
+ return "Success.";
+ } else {
+ return "Item not issued.";
+ }
+ } else {
+ return "Item not found.";
+ }
+}
+
+sub ProcessOfflineIssue {
+ my $operation = shift;
+
+ my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber
+
+ if ( $borrower->{borrowernumber} ) {
+ my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} );
+ my $issue = GetOpenIssue( $itemnumber );
+
+ if ( $issue and ( $issue->{borrowernumber} ne $borrower->{borrowernumber} ) ) { # Item already issued to another borrower, mark it returned
+ MarkIssueReturned(
+ $issue->{borrowernumber},
+ $itemnumber,
+ undef,
+ $operation->{timestamp},
+ );
+ }
+ AddIssue(
+ $borrower,
+ $operation->{'barcode'},
+ undef,
+ 1,
+ $operation->{timestamp},
+ undef,
+ );
+ return "Success.";
+ } else {
+ return "Borrower not found.";
+ }
}
- 1;
+1;
__END__
diff --git a/C4/Overdues.pm b/C4/Overdues.pm
index b5e3cdf..f781b82 100644
--- a/C4/Overdues.pm
+++ b/C4/Overdues.pm
@@ -23,6 +23,7 @@ use strict;
use Date::Calc qw/Today Date_to_Days/;
use Date::Manip qw/UnixDate/;
use C4::Circulation;
+use C4::IssuingRules;
use C4::Context;
use C4::Accounts;
use C4::Log; # logaction
@@ -71,9 +72,6 @@ BEGIN {
push @EXPORT, qw(
&GetIssuesIteminfo
);
- #
- # &GetIssuingRules - delete.
- # use C4::Circulation::GetIssuingRule instead.
# subs to move to Members.pm
push @EXPORT, qw(
@@ -124,7 +122,7 @@ sub Getoverdues {
SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode
FROM issues
LEFT JOIN items USING (itemnumber)
- WHERE date_due < CURDATE()
+ WHERE DATE(date_due) < CURDATE()
";
} else {
$statement = "
@@ -132,7 +130,7 @@ LEFT JOIN items USING (itemnumber)
FROM issues
LEFT JOIN items USING (itemnumber)
LEFT JOIN biblioitems USING (biblioitemnumber)
- WHERE date_due < CURDATE()
+ WHERE DATE(date_due) < CURDATE()
";
}
@@ -170,7 +168,7 @@ sub checkoverdues {
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 DATE(issues.date_due) < CURDATE()"
);
# FIXME: SELECT * across 4 tables? do we really need the marc AND marcxml blobs??
$sth->execute($borrowernumber);
@@ -245,7 +243,7 @@ sub CalcFine {
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);
+ my $data = 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 ).
@@ -269,7 +267,7 @@ sub CalcFine {
} else {
# a zero (or null) chargeperiod means no charge.
}
- $amount = C4::Context->preference('maxFine') if(C4::Context->preference('maxFine') && ( $amount > C4::Context->preference('maxFine')));
+ $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);
# FIXME: chargename is NEVER populated anywhere.
@@ -585,7 +583,7 @@ Returns the replacement cost of the item with the given item number.
=cut
-#'
+
sub ReplacementCost {
my ($itemnum) = @_;
my $dbh = C4::Context->dbh;
@@ -604,60 +602,22 @@ sub ReplacementCost {
return the total of fine
-C<$itemnum> is item number
-
C<$borrowernumber> is the borrowernumber
-=cut
-
+=cut
sub GetFine {
- my ( $itemnum, $borrowernumber ) = @_;
+ my ( $borrowernumber ) = @_;
my $dbh = C4::Context->dbh();
my $query = "SELECT sum(amountoutstanding) FROM accountlines
where accounttype like 'F%'
- AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?";
+ AND amountoutstanding > 0 AND borrowernumber=?";
my $sth = $dbh->prepare($query);
- $sth->execute( $itemnum, $borrowernumber );
+ $sth->execute( $borrowernumber );
my $data = $sth->fetchrow_hashref();
return ( $data->{'sum(amountoutstanding)'} );
}
-
-=head2 GetIssuingRules
-
-FIXME - This sub should be deprecated and removed.
-It ignores branch and defaults.
-
- $data = &GetIssuingRules($itemtype,$categorycode);
-
-Looks up for all issuingrules an item info
-
-C<$itemnumber> is a reference-to-hash whose keys are all of the fields
-from the borrowers and categories tables of the Koha database. Thus,
-
-C<$categorycode> contains information about borrowers category
-
-C<$data> contains all information about both the borrower and
-category he or she belongs to.
-=cut
-
-sub GetIssuingRules {
- warn "GetIssuingRules is deprecated: use GetIssuingRule from C4::Circulation instead.";
- my ($itemtype,$categorycode)=@_;
- my $dbh = C4::Context->dbh();
- my $query=qq|SELECT *
- FROM issuingrules
- WHERE issuingrules.itemtype=?
- AND issuingrules.categorycode=?
- |;
- my $sth = $dbh->prepare($query);
- # print $query;
- $sth->execute($itemtype,$categorycode);
- return $sth->fetchrow_hashref;
-}
-
-
sub ReplacementCost2 {
my ( $itemnum, $borrowernumber ) = @_;
my $dbh = C4::Context->dbh();
@@ -956,7 +916,7 @@ returns a list of branch codes for branches with overdue rules defined.
sub GetBranchcodesWithOverdueRules {
my $dbh = C4::Context->dbh;
- my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL AND branchcode <> ''");
+ my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL ");
$rqoverduebranches->execute;
my @branches = map { shift @$_ } @{ $rqoverduebranches->fetchall_arrayref };
return @branches;
@@ -1029,7 +989,7 @@ sub GetOverduerules {
Check if the borrowers is already debarred
-C<$debarredstatus> return 0 for not debarred and return 1 for debarred
+C<$debarredstatus> return undef for not debarred and return end of debar date for debarred
C<$borrowernumber> contains the borrower number
@@ -1043,32 +1003,35 @@ sub CheckBorrowerDebarred {
SELECT debarred
FROM borrowers
WHERE borrowernumber=?
+ AND debarred > NOW()
|;
my $sth = $dbh->prepare($query);
$sth->execute($borrowernumber);
- my ($debarredstatus) = $sth->fetchrow;
- return ( $debarredstatus eq '1' ? 1 : 0 );
+ my $debarredstatus= $sth->fetchrow;
+ return $debarredstatus;
+
}
=head2 UpdateBorrowerDebarred
- ($borrowerstatut) = &UpdateBorrowerDebarred($borrowernumber);
+ ($borrowerstatut) = &UpdateBorrowerDebarred($borrowernumber, $todate);
update status of borrowers in borrowers table (field debarred)
C<$borrowernumber> borrower number
+C<$todate> end of bare
=cut
sub UpdateBorrowerDebarred{
- my($borrowernumber) = @_;
+ my($borrowernumber, $todate) = @_;
my $dbh = C4::Context->dbh;
my $query=qq|UPDATE borrowers
- SET debarred='1'
+ SET debarred=?
WHERE borrowernumber=?
|;
my $sth=$dbh->prepare($query);
- $sth->execute($borrowernumber);
+ $sth->execute($todate, $borrowernumber);
$sth->finish;
return 1;
}
diff --git a/C4/Reserves.pm b/C4/Reserves.pm
index 2dd2688..9f15421 100644
--- a/C4/Reserves.pm
+++ b/C4/Reserves.pm
@@ -22,6 +22,7 @@ package C4::Reserves;
use strict;
#use warnings; FIXME - Bug 2505
+use Date::Calc qw( Add_Delta_Days Time_to_Date Today);
use C4::Context;
use C4::Biblio;
use C4::Members;
@@ -32,10 +33,11 @@ use C4::Accounts;
# for _koha_notify_reserve
use C4::Members::Messaging;
-use C4::Members qw();
+use C4::Members ;
use C4::Letters;
use C4::Branch qw( GetBranchDetail );
use C4::Dates qw( format_date_in_iso );
+use C4::IssuingRules;
use List::MoreUtils qw( firstidx );
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@@ -91,11 +93,14 @@ BEGIN {
@EXPORT = qw(
&AddReserve
+ &GetMaxPickupDelay
+ &GetMaxPickupDate
&GetReservesFromItemnumber
&GetReservesFromBiblionumber
&GetReservesFromBorrowernumber
&GetReservesForBranch
&GetReservesToBranch
+ &GetReservesControlBranch
&GetReserveCount
&GetReserveFee
&GetReserveInfo
@@ -113,13 +118,16 @@ BEGIN {
&CheckReserves
&CanBookBeReserved
&CanItemBeReserved
- &CancelReserve
- &CancelExpiredReserves
+ &CanHoldOnShelf
+ &CancelReserve
+ &CancelExpiredReserves
- &IsAvailableForItemLevelRequest
-
- &AlterPriority
- &ToggleLowestPriority
+ &IsAvailableForItemLevelRequest
+
+ &AlterPriority
+ &ToggleLowestPriority
+ &CanHoldMultipleItems
+ &BorrowerHasReserve
);
}
@@ -142,11 +150,17 @@ sub AddReserve {
my $const = lc substr( $constraint, 0, 1 );
$resdate = format_date_in_iso( $resdate ) if ( $resdate );
$resdate = C4::Dates->today( 'iso' ) unless ( $resdate );
+ my $item = C4::Items::GetItem($checkitem);
+ my @maxPickupDate = GetMaxPickupDate($resdate, $borrowernumber, $item);
+ my $maxpickupdate = sprintf( "%d-%02d-%02d", @maxPickupDate );
if ($expdate) {
$expdate = format_date_in_iso( $expdate );
+ my @expdate = split("-", $expdate);
+ $expdate = $maxpickupdate if Delta_Days(@expdate[ 0 .. 2 ], @maxPickupDate[ 0 .. 2 ]) < 0;
} else {
- undef $expdate; # make reserves.expirationdate default to null rather than '0000-00-00'
+ $expdate = $maxpickupdate;
}
+ undef $expdate if $resdate eq $maxpickupdate; # make reserves.expirationdate default to null rather than '0000-00-00'
if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
# Make room in reserves for this before those of a later reserve date
$priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
@@ -250,24 +264,27 @@ sub GetReservesFromBiblionumber {
# Find the desired items in the reserves
my $query = "
- SELECT branchcode,
- timestamp AS rtimestamp,
- priority,
- biblionumber,
- borrowernumber,
- reservedate,
- constrainttype,
- found,
- itemnumber,
- reservenotes,
- expirationdate,
- lowestPriority
+ SELECT reserves.reservenumber,
+ reserves.branchcode,
+ reserves.timestamp AS rtimestamp,
+ reserves.priority,
+ reserves.biblionumber,
+ reserves.borrowernumber,
+ reserves.reservedate,
+ reserves.constrainttype,
+ reserves.found,
+ reserves.itemnumber,
+ reserves.reservenotes,
+ reserves.expirationdate,
+ reserves.lowestPriority,
+ biblioitems.itemtype
FROM reserves
- WHERE biblionumber = ? ";
+ LEFT JOIN biblioitems ON biblioitems.biblionumber = reserves.biblionumber
+ WHERE reserves.biblionumber = ? ";
unless ( $all_dates ) {
$query .= "AND reservedate <= CURRENT_DATE()";
}
- $query .= "ORDER BY priority";
+ $query .= "ORDER BY reserves.priority";
my $sth = $dbh->prepare($query);
$sth->execute($biblionumber);
my @results;
@@ -373,9 +390,40 @@ sub GetReservesFromBorrowernumber {
return @$data;
}
#-------------------------------------------------------------------------------------
+sub BorrowerHasReserve {
+ my ( $borrowernumber, $biblionumber, $itemnumber ) = @_;
+ my $dbh = C4::Context->dbh;
+ my $sth;
+
+ if ( $biblionumber ) {
+ $sth = $dbh->prepare("
+ SELECT COUNT(*) AS hasReserve
+ FROM reserves
+ WHERE borrowernumber = ?
+ AND biblionumber = ?
+ ORDER BY reservedate
+ ");
+ $sth->execute( $borrowernumber, $biblionumber );
+ } elsif ( $itemnumber ) {
+ $sth = $dbh->prepare("
+ SELECT COUNT(*) AS hasReserve
+ FROM reserves
+ WHERE borrowernumber = ?
+ AND itemnumber = ?
+ ORDER BY reservedate
+ ");
+ $sth->execute( $borrowernumber, $itemnumber );
+ } else {
+ return -1;
+ }
+
+ my $data = $sth->fetchrow_hashref();
+ return $data->{'hasReserve'};
+}
+
=head2 CanBookBeReserved
- $error = &CanBookBeReserved($borrowernumber, $biblionumber)
+$error = &CanBookBeReserved($borrowernumber, $biblionumber)
=cut
@@ -399,7 +447,7 @@ This function return 1 if an item can be issued by this borrower.
sub CanItemBeReserved{
my ($borrowernumber, $itemnumber) = @_;
-
+
my $dbh = C4::Context->dbh;
my $allowedreserves = 0;
@@ -407,7 +455,7 @@ sub CanItemBeReserved{
my $itype = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
# we retrieve borrowers and items informations #
- my $item = GetItem($itemnumber);
+ my $item = C4::Items::GetItem($itemnumber);
my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber);
# we retrieve user rights on this itemtype and branchcode
@@ -438,22 +486,31 @@ sub CanItemBeReserved{
my $branchfield = "reserves.branchcode";
if( $controlbranch eq "ItemHomeLibrary" ){
- $branchfield = "items.homebranch";
- $branchcode = $item->{homebranch};
+ my $field=C4::Context->preference("HomeOrHoldingBranch")||"homebranch";
+ $branchfield = "items.$field";
+ $branchcode = $item->{$field};
}elsif( $controlbranch eq "PatronLibrary" ){
$branchfield = "borrowers.branchcode";
$branchcode = $borrower->{branchcode};
}
- # we retrieve rights
- $sth->execute($categorycode, $itemtype, $branchcode);
- if(my $rights = $sth->fetchrow_hashref()){
- $itemtype = $rights->{itemtype};
- $allowedreserves = $rights->{reservesallowed};
+ # we retrieve user rights on this itemtype and branchcode
+ my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{$itype}, $branchcode);
+ return 0 if( defined $issuingrule->{reservesallowed} && not $issuingrule->{reservesallowed} );
+
+ # We retrieve the count of reserves allowed for this category code
+ $issuingrule = GetIssuingRule ($borrower->{categorycode}, "*", "*");
+
+ if($issuingrule){
+ $itemtype = $issuingrule->{itemtype};
+ $allowedreserves = $issuingrule->{reservesallowed};
}else{
$itemtype = '*';
}
+ return 0 if ($issuingrule->{reservesallowed}==0 ||
+ ($issuingrule->{holdrestricted}== 1 && !($branchcode eq $borrower->{branchcode}))
+ );
# we retrieve count
$querycount .= "AND $branchfield = ?";
@@ -479,6 +536,106 @@ sub CanItemBeReserved{
return 0;
}
}
+
+=item GetMaxPickupDelay
+
+$resallowed = &GetMaxPickupDelay($borrowernumber, $itemnumber)
+
+this function return the number of allowed reserves.
+
+=cut
+
+sub GetMaxPickupDelay {
+ my ($borrowernumber, $itemnumber) = @_;
+
+ my $dbh = C4::Context->dbh;
+ my $allowedreserves = 0;
+
+ my $itype = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
+
+ # we retrieve borrowers and items informations #
+ my $item = C4::Items::GetItem($itemnumber);
+ my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber);
+ my $controlbranch = GetReservesControlBranch($borrower,$item);
+
+ # we retrieve user rights on this itemtype and branchcode
+ my $sth = $dbh->prepare("SELECT holdspickupdelay
+ FROM issuingrules
+ WHERE categorycode=?
+ AND itemtype=?
+ AND branchcode=?
+ AND holdspickupdelay IS NOT NULL"
+ );
+
+ my $itemtype = $item->{$itype};
+ my $borrowertype = $borrower->{categorycode};
+ my $branchcode = $controlbranch;
+
+ $sth->execute( $borrowertype, $itemtype, $branchcode );
+ my $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( $borrowertype, "*", $branchcode );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( "*", $itemtype, $branchcode );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( "*", "*", $branchcode );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( $borrowertype, $itemtype, "*" );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( $borrowertype, "*", "*" );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( "*", $itemtype, "*" );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ $sth->execute( "*", "*", "*" );
+ $pickupdelay = $sth->fetchrow_hashref;
+ return $pickupdelay->{holdspickupdelay}
+ if defined($pickupdelay) && $pickupdelay->{holdspickupdelay} ne 'NULL';
+
+ # if nothing found, return no rights
+ return 0;
+}
+
+=item GetMaxPickupDate
+
+$maxpickupdate = &GetMaxPickupDate($date);
+
+this function return the max pickup date.
+(e.g. : the date after which the hold will be considered cancelled)
+
+=cut
+
+sub GetMaxPickupDate{
+ my $inputdate=shift;
+ my $borrowernumber=shift;
+ my $item=shift;
+ return unless $inputdate;
+ my @date = split(/-/,$inputdate);
+ my $delay = GetMaxPickupDelay($borrowernumber, $item->{'itemnumber'});
+ ( @date ) =
+ Add_Delta_Days( @date[0..2], $delay);
+ return @date;
+}
+
#--------------------------------------------------------------------------------
=head2 GetReserveCount
@@ -518,21 +675,22 @@ sub GetOtherReserves {
my $nextreservinfo;
my ( $restype, $checkreserves ) = CheckReserves($itemnumber);
if ($checkreserves) {
- my $iteminfo = GetItem($itemnumber);
+ my $iteminfo = C4::Items::GetItem($itemnumber);
if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) {
$messages->{'transfert'} = $checkreserves->{'branchcode'};
#minus priorities of others reservs
ModReserveMinusPriority(
$itemnumber,
$checkreserves->{'borrowernumber'},
- $iteminfo->{'biblionumber'}
+ $iteminfo->{'biblionumber'},
+ $checkreserves->{'reservenumber'}
);
#launch the subroutine dotransfer
C4::Items::ModItemTransfer(
$itemnumber,
$iteminfo->{'holdingbranch'},
- $checkreserves->{'branchcode'}
+ $checkreserves->{'branchcode'},
),
;
}
@@ -543,7 +701,8 @@ sub GetOtherReserves {
ModReserveMinusPriority(
$itemnumber,
$checkreserves->{'borrowernumber'},
- $iteminfo->{'biblionumber'}
+ $iteminfo->{'biblionumber'},
+ $checkreserves->{'reservenumber'}
);
ModReserveStatus($itemnumber,'W');
}
@@ -657,6 +816,26 @@ sub GetReserveFee {
return $fee;
}
+=head2 GetReservesControlBranch
+
+$branchcode = &GetReservesControlBranch($borrower, $item)
+
+Returns the branchcode to consider to check hold rules against
+
+=cut
+sub GetReservesControlBranch{
+ my ($borrower, $item) = @_;
+ my $controlbranch = C4::Context->preference('ReservesControlBranch');
+ my $hbr = C4::Context->preference('HomeOrHoldingBranch')||"homebranch";
+ my $branchcode="*";
+ if($controlbranch eq "ItemHomeLibrary"){
+ $branchcode = $item->{$hbr};
+ } elsif($controlbranch eq "PatronLibrary"){
+ $branchcode = $borrower->{'branchcode'};
+ }
+ return $branchcode;
+}
+
=head2 GetReservesToBranch
@transreserv = GetReservesToBranch( $frombranch );
@@ -840,23 +1019,18 @@ sub CancelExpiredReserves {
$sth->execute();
while ( my $res = $sth->fetchrow_hashref() ) {
- CancelReserve( $res->{'biblionumber'}, '', $res->{'borrowernumber'} );
+ CancelReserve( $res->{'reservenumber'}, $res->{'biblionumber'} );
}
}
=head2 CancelReserve
- &CancelReserve($biblionumber, $itemnumber, $borrowernumber);
+ &CancelReserve( $reservenumber, $biblionumber );
Cancels a reserve.
-Use either C<$biblionumber> or C<$itemnumber> to specify the item to
-cancel, but not both: if both are given, C<&CancelReserve> does
-nothing.
-
-C<$borrowernumber> is the borrower number of the patron on whose
-behalf the book was reserved.
+C<$reservenumber> is the unique key for the reserve to be canceled.
If C<$biblionumber> was given, C<&CancelReserve> also adjusts the
priorities of the other people who are waiting on the book.
@@ -864,9 +1038,14 @@ priorities of the other people who are waiting on the book.
=cut
sub CancelReserve {
- my ( $biblio, $item, $borr ) = @_;
+ my ( $reservenumber, $biblio ) = @_;
my $dbh = C4::Context->dbh;
- if ( $item and $borr ) {
+
+ my $sth = $dbh->prepare('SELECT * FROM reserves WHERE reservenumber = ?');
+ $sth->execute( $reservenumber );
+ my $reserve = $sth->fetchrow_hashref();
+
+ if ( $reserve->{'found'} eq 'W' ) {
# removing a waiting reserve record....
# update the database...
my $query = "
@@ -874,27 +1053,46 @@ sub CancelReserve {
SET cancellationdate = now(),
found = Null,
priority = 0
- WHERE itemnumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
";
my $sth = $dbh->prepare($query);
- $sth->execute( $item, $borr );
- $sth->finish;
+ $sth->execute( $reservenumber );
+
+ # get reserve information to place into old_reserves
$query = "
INSERT INTO old_reserves
SELECT * FROM reserves
- WHERE itemnumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
";
$sth = $dbh->prepare($query);
- $sth->execute( $item, $borr );
+ $sth->execute( $reservenumber );
+ my $holditem = $sth->fetchrow_hashref;
+ my $insert_fields = '';
+ my $value_fields = '';
+ foreach my $column ('borrowernumber','reservedate','biblionumber','constrainttype','branchcode','notificationdate','reminderdate','cancellationdate','reservenotes','priority','found','itemnumber','waitingdate','expirationdate') {
+ if (defined($holditem->{$column})) {
+ if (length($insert_fields)) {
+ $insert_fields .= ",$column";
+ $value_fields .= ",\'$holditem->{$column}\'";
+ }
+ else {
+ $insert_fields .= "$column";
+ $value_fields .= "\'$holditem->{$column}\'";
+ }
+ }
+ }
+ $query = qq/
+ INSERT INTO old_reserves ($insert_fields)
+ VALUES ($value_fields)
+ /;
+ $sth = $dbh->prepare($query);
+ $sth->execute();
$query = "
DELETE FROM reserves
- WHERE itemnumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
";
$sth = $dbh->prepare($query);
- $sth->execute( $item, $borr );
+ $sth->execute( $reservenumber );
}
else {
# removing a reserve record....
@@ -902,52 +1100,72 @@ sub CancelReserve {
my $priority;
my $query = qq/
SELECT priority FROM reserves
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
AND cancellationdate IS NULL
AND itemnumber IS NULL
/;
my $sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borr );
+ $sth->execute( $reservenumber );
($priority) = $sth->fetchrow_array;
$sth->finish;
$query = qq/
UPDATE reserves
SET cancellationdate = now(),
found = Null,
- priority = 0
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ priority = 0,
+ expirationdate = NULL
+ WHERE reservenumber = ?
/;
# update the database, removing the record...
$sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borr );
- $sth->finish;
+ $sth->execute( $reservenumber );
$query = qq/
INSERT INTO old_reserves
SELECT * FROM reserves
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
+ /;
+ $sth = $dbh->prepare($query);
+ $sth->execute( $reservenumber );
+ my $holditem = $sth->fetchrow_hashref;
+
+ my $insert_fields = '';
+ my $value_fields = '';
+ foreach my $column ('borrowernumber','reservedate','biblionumber','constrainttype','branchcode','notificationdate','reminderdate','cancellationdate','reservenotes','priority','found','itemnumber','waitingdate','expirationdate') {
+ if (defined($holditem->{$column})) {
+ if (length($insert_fields)) {
+ $insert_fields .= ",$column";
+ $value_fields .= ",\'$holditem->{$column}\'";
+ }
+ else {
+ $insert_fields .= "$column";
+ $value_fields .= "\'$holditem->{$column}\'";
+ }
+ }
+ }
+ $query = qq/
+ INSERT INTO old_reserves ($insert_fields)
+ VALUES ($value_fields)
/;
$sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borr );
+ $sth->execute();
$query = qq/
DELETE FROM reserves
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
/;
$sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borr );
+ $sth->execute( $reservenumber );
# now fix the priority on the others....
- _FixPriority( $biblio, $borr );
+ _FixPriority( '', '', $priority,'', $reservenumber );
}
}
-=head2 ModReserve
+=item ModReserve
+
+=over 4
ModReserve($rank, $biblio, $borrower, $branch[, $itemnumber])
@@ -980,52 +1198,48 @@ itemnumber and supplying itemnumber.
sub ModReserve {
#subroutine to update a reserve
- my ( $rank, $biblio, $borrower, $branch , $itemnumber) = @_;
+ my ( $rank, $biblio, $borrower, $branch , $itemnumber, $reservenumber) = @_;
return if $rank eq "W";
return if $rank eq "n";
my $dbh = C4::Context->dbh;
if ( $rank eq "del" ) {
my $query = qq/
UPDATE reserves
- SET cancellationdate=now()
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ SET cancellationdate=now(),
+ expirationdate = NULL
+ WHERE reservenumber = ?
/;
my $sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borrower );
+ $sth->execute( $reservenumber );
$sth->finish;
$query = qq/
INSERT INTO old_reserves
- SELECT *
- FROM reserves
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ SELECT * FROM reserves
+ WHERE reservenumber = ?
/;
$sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borrower );
+ $sth->execute( $reservenumber );
$query = qq/
DELETE FROM reserves
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
/;
$sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borrower );
+ $sth->execute( $reservenumber );
}
elsif ($rank =~ /^\d+/ and $rank > 0) {
my $query = qq/
UPDATE reserves SET priority = ? ,branchcode = ?, itemnumber = ?, found = NULL, waitingdate = NULL
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
/;
my $sth = $dbh->prepare($query);
- $sth->execute( $rank, $branch,$itemnumber, $biblio, $borrower);
+ $sth->execute( $rank, $branch, $itemnumber, $reservenumber );
$sth->finish;
- _FixPriority( $biblio, $borrower, $rank);
+ _FixPriority( $biblio, $borrower, $rank, '', $reservenumber);
}
}
-=head2 ModReserveFill
+=item ModReserveFill
&ModReserveFill($reserve);
@@ -1044,16 +1258,17 @@ sub ModReserveFill {
my $biblionumber = $res->{'biblionumber'};
my $borrowernumber = $res->{'borrowernumber'};
my $resdate = $res->{'reservedate'};
+ my $resnumber = $res->{'reservenumber'};
# get the priority on this record....
my $priority;
my $query = "SELECT priority
FROM reserves
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE borrowernumber = ?
+ AND reservenumber = ?
AND reservedate = ?";
my $sth = $dbh->prepare($query);
- $sth->execute( $biblionumber, $borrowernumber, $resdate );
+ $sth->execute( $resnumber, $borrowernumber, $resdate );
($priority) = $sth->fetchrow_array;
$sth->finish;
@@ -1061,35 +1276,35 @@ sub ModReserveFill {
$query = "UPDATE reserves
SET found = 'F',
priority = 0
- WHERE biblionumber = ?
+ WHERE reservenumber = ?
AND reservedate = ?
AND borrowernumber = ?
";
$sth = $dbh->prepare($query);
- $sth->execute( $biblionumber, $resdate, $borrowernumber );
+ $sth->execute( $resnumber, $resdate, $borrowernumber );
$sth->finish;
# move to old_reserves
$query = "INSERT INTO old_reserves
SELECT * FROM reserves
- WHERE biblionumber = ?
+ WHERE reservenumber = ?
AND reservedate = ?
AND borrowernumber = ?
";
$sth = $dbh->prepare($query);
- $sth->execute( $biblionumber, $resdate, $borrowernumber );
+ $sth->execute( $resnumber, $resdate, $borrowernumber );
$query = "DELETE FROM reserves
- WHERE biblionumber = ?
+ WHERE reservenumber = ?
AND reservedate = ?
AND borrowernumber = ?
";
$sth = $dbh->prepare($query);
- $sth->execute( $biblionumber, $resdate, $borrowernumber );
+ $sth->execute( $resnumber, $resdate, $borrowernumber );
# now fix the priority on the others (if the priority wasn't
# already sorted!)....
unless ( $priority == 0 ) {
- _FixPriority( $biblionumber, $borrowernumber );
+ _FixPriority( $priority, $biblionumber,undef,undef, $res->{reservenumber} );
}
}
@@ -1106,7 +1321,6 @@ $newstatus is the new status.
=cut
sub ModReserveStatus {
-
#first : check if we have a reservation for this item .
my ($itemnumber, $newstatus) = @_;
my $dbh = C4::Context->dbh;
@@ -1140,9 +1354,8 @@ take care of the waiting status
=cut
sub ModReserveAffect {
- my ( $itemnumber, $borrowernumber,$transferToDo ) = @_;
+ my ( $itemnumber, $borrowernumber,$transferToDo, $reservenumber ) = @_;
my $dbh = C4::Context->dbh;
-
# we want to attach $itemnumber to $borrowernumber, find the biblionumber
# attached to $itemnumber
my $sth = $dbh->prepare("SELECT biblionumber FROM items WHERE itemnumber=?");
@@ -1160,27 +1373,52 @@ sub ModReserveAffect {
$query = "
UPDATE reserves
SET priority = 0,
- itemnumber = ?,
- found = 'T'
- WHERE borrowernumber = ?
- AND biblionumber = ?
+ itemnumber = ?
+ WHERE reservenumber = ?
";
}
else {
# affect the reserve to Waiting as well.
- $query = "
- UPDATE reserves
- SET priority = 0,
- found = 'W',
- waitingdate=now(),
- itemnumber = ?
- WHERE borrowernumber = ?
- AND biblionumber = ?
- ";
+ my $holdperiod = C4::Context->preference('ReservesMaxPickUpDelay');
+ if ((!defined($holdperiod)) || ($holdperiod eq '') || ($holdperiod == 0)) {
+ $query = "
+ UPDATE reserves
+ SET priority = 0,
+ found = 'W',
+ waitingdate=now(),
+ itemnumber = ?
+ WHERE reservenumber = ?
+ ";
+ }
+ else {
+ my ($holdexpyear,$holdexpmonth,$holdexpday) = Today();
+ my $holdstartdate = C4::Dates->new(sprintf "%02d/%02d/%04d",$holdexpmonth,$holdexpday,$holdexpyear, 'us');
+
+ # Grab branch for calendar purposes
+ $sth = $dbh->prepare("SELECT branchcode FROM reserves WHERE borrowernumber=? AND biblionumber=?");
+ $sth->execute($borrowernumber,$biblionumber);
+ my ($branch) = $sth->fetchrow;
+
+ # Check to see if hold expiration date falls on a closed library day.
+ # Note the useDaysMode syspref will need to be set to Calendar for
+ # the code to advance to the next non-closed day.
+ my $calendar = C4::Calendar->new( branchcode => $branch);
+ my $holdexpdate = $calendar->addDate($holdstartdate, $holdperiod);
+ my $sqlexpdate = $holdexpdate->output('iso');
+ $query = "
+ UPDATE reserves
+ SET priority = 0,
+ found = 'W',
+ waitingdate=now(),
+ itemnumber = ?,
+ expirationdate='$sqlexpdate'
+ WHERE reservenumber = ?
+ ";
+ }
}
$sth = $dbh->prepare($query);
- $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
- _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf );
+ $sth->execute( $itemnumber, $reservenumber );
+ _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber, $reservenumber ) if ( !$transferToDo && !$already_on_shelf );
if ( C4::Context->preference("ReturnToShelvingCart") ) {
CartToShelf( $itemnumber );
@@ -1220,23 +1458,21 @@ Reduce the values of queuded list
=cut
sub ModReserveMinusPriority {
- my ( $itemnumber, $borrowernumber, $biblionumber ) = @_;
-
+ my ( $itemnumber, $borrowernumber, $biblionumber, $reservenumber ) = @_;
#first step update the value of the first person on reserv
my $dbh = C4::Context->dbh;
my $query = "
UPDATE reserves
SET priority = 0 , itemnumber = ?
- WHERE borrowernumber=?
- AND biblionumber=?
+ WHERE reservenumber=?
";
my $sth_upd = $dbh->prepare($query);
- $sth_upd->execute( $itemnumber, $borrowernumber, $biblionumber );
+ $sth_upd->execute( $itemnumber, $reservenumber );
# second step update all others reservs
- _FixPriority($biblionumber, $borrowernumber, '0');
+ _FixPriority($biblionumber, $borrowernumber, '0', '', $reservenumber);
}
-=head2 GetReserveInfo
+=item GetReserveInfo
&GetReserveInfo($borrowernumber,$biblionumber);
@@ -1321,7 +1557,7 @@ and canreservefromotherbranches.
sub IsAvailableForItemLevelRequest {
my $itemnumber = shift;
- my $item = GetItem($itemnumber);
+ my $item = C4::Items::GetItem($itemnumber);
# must check the notforloan setting of the itemtype
# FIXME - a lot of places in the code do this
@@ -1356,10 +1592,10 @@ sub IsAvailableForItemLevelRequest {
$notforloan_per_itemtype;
- if (C4::Context->preference('AllowOnShelfHolds')) {
+ if (CanHoldOnShelf($itemnumber)) {
return $available_per_item;
} else {
- return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "W"));
+ return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "W"));
}
}
@@ -1387,15 +1623,15 @@ sub AlterPriority {
my $priority = $reserve->{'priority'};
$priority = $where eq 'up' ? $priority - 1 : $priority + 1;
- _FixPriority( $biblionumber, $borrowernumber, $priority )
+ _FixPriority( $biblionumber, $borrowernumber, $priority ,undef,$reserve->{reservenumber})
} elsif ( $where eq 'top' ) {
- _FixPriority( $biblionumber, $borrowernumber, '1' )
+ _FixPriority( $biblionumber, $borrowernumber, '1',undef,$reserve->{reservenumber} )
} elsif ( $where eq 'bottom' ) {
- _FixPriority( $biblionumber, $borrowernumber, '999999' )
+ _FixPriority( $biblionumber, $borrowernumber, '999999',undef,$reserve->{reservenumber} )
}
}
@@ -1418,18 +1654,49 @@ sub ToggleLowestPriority {
WHERE biblionumber = ?
AND borrowernumber = ?"
);
- $sth->execute(
- $biblionumber,
- $borrowernumber,
- );
+ $sth->execute( $biblionumber, $borrowernumber, );
$sth->finish;
_FixPriority( $biblionumber, $borrowernumber, '999999' );
}
+=head2 CanHoldOnShelf
+
+my $canhold = &CanHoldOnShelf($itemnumber);
+
+ Check if a book can be hold on shelf.
+
+=cut
+
+sub CanHoldOnShelf {
+ my ($itemnumber) = @_;
+
+ my $item = C4::Items::GetItem($itemnumber);
+ my $itemtype = C4::Context->preference('item-level_itypes');
+ $itemtype = $itemtype ? $item->{itype} : $item->{itemtype} ;
+ my $branch = $item->{C4::Context->preference('HomeOrHoldingBranch')};
+
+ my $issuingrule = GetIssuingRule('*', $itemtype, $branch);
+ return $issuingrule->{allowonshelfholds};
+
+}
+
+sub CanHoldMultipleItems {
+ my ( $itemtype ) = @_;
+
+ my @multi_itemtypes = split( / /, C4::Context->preference('AllowMultipleHoldsPerBib') );
+ for my $mtype ( @multi_itemtypes ) {
+ if ( $itemtype eq $mtype ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
=head2 _FixPriority
- &_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank);
+&_FixPriority($biblio,$borrowernumber,$rank,$ignoreSetLowestRank,$reservenumber);
Only used internally (so don't export it)
Changed how this functions works #
@@ -1441,7 +1708,7 @@ in new priority rank
=cut
sub _FixPriority {
- my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank ) = @_;
+ my ( $biblio, $borrowernumber, $rank, $ignoreSetLowestRank, $reservenumber ) = @_;
my $dbh = C4::Context->dbh;
if ( $rank eq "del" ) {
CancelReserve( $biblio, undef, $borrowernumber );
@@ -1452,12 +1719,11 @@ sub _FixPriority {
my $query = qq/
UPDATE reserves
SET priority = 0
- WHERE biblionumber = ?
- AND borrowernumber = ?
- AND found IN ('W', 'T')
+ WHERE reservenumber = ?
+ AND found IN ('W',"T")
/;
my $sth = $dbh->prepare($query);
- $sth->execute( $biblio, $borrowernumber );
+ $sth->execute( $reservenumber );
}
my @priority;
my @reservedates;
@@ -1468,7 +1734,7 @@ sub _FixPriority {
# This is wrong a waiting reserve has W set
# The assumption that having an itemnumber set means waiting is wrong and should be corrected any place it occurs
my $query = qq/
- SELECT borrowernumber, reservedate, constrainttype
+ SELECT borrowernumber, reservedate, constrainttype, reservenumber
FROM reserves
WHERE biblionumber = ?
AND ((found <> 'W' AND found <> 'T') or found is NULL)
@@ -1503,16 +1769,15 @@ sub _FixPriority {
$query = "
UPDATE reserves
SET priority = ?
- WHERE biblionumber = ?
- AND borrowernumber = ?
+ WHERE reservenumber = ?
AND reservedate = ?
AND found IS NULL
";
$sth = $dbh->prepare($query);
for ( my $j = 0 ; $j < @priority ; $j++ ) {
$sth->execute(
- $j + 1, $biblio,
- $priority[$j]->{'borrowernumber'},
+ $j + 1,
+ $priority[$j]->{'reservenumber'},
$priority[$j]->{'reservedate'}
);
$sth->finish;
@@ -1552,15 +1817,16 @@ sub _Findgroupreserve {
# TODO: consolidate at least the SELECT portion of the first 2 queries to a common $select var.
# check for exact targetted match
my $item_level_target_query = qq/
- SELECT reserves.biblionumber AS biblionumber,
- reserves.borrowernumber AS borrowernumber,
- reserves.reservedate AS reservedate,
- reserves.branchcode AS branchcode,
- reserves.cancellationdate AS cancellationdate,
- reserves.found AS found,
- reserves.reservenotes AS reservenotes,
- reserves.priority AS priority,
- reserves.timestamp AS timestamp,
+ SELECT reserves.reservenumber AS reservenumber,
+ reserves.biblionumber AS biblionumber,
+ reserves.borrowernumber AS borrowernumber,
+ reserves.reservedate AS reservedate,
+ reserves.branchcode AS branchcode,
+ reserves.cancellationdate AS cancellationdate,
+ reserves.found AS found,
+ reserves.reservenotes AS reservenotes,
+ reserves.priority AS priority,
+ reserves.timestamp AS timestamp,
biblioitems.biblioitemnumber AS biblioitemnumber,
reserves.itemnumber AS itemnumber
FROM reserves
@@ -1582,15 +1848,16 @@ sub _Findgroupreserve {
# check for title-level targetted match
my $title_level_target_query = qq/
- SELECT reserves.biblionumber AS biblionumber,
- reserves.borrowernumber AS borrowernumber,
- reserves.reservedate AS reservedate,
- reserves.branchcode AS branchcode,
- reserves.cancellationdate AS cancellationdate,
- reserves.found AS found,
- reserves.reservenotes AS reservenotes,
- reserves.priority AS priority,
- reserves.timestamp AS timestamp,
+ SELECT reserves.reservenumber AS reservenumber,
+ reserves.biblionumber AS biblionumber,
+ reserves.borrowernumber AS borrowernumber,
+ reserves.reservedate AS reservedate,
+ reserves.branchcode AS branchcode,
+ reserves.cancellationdate AS cancellationdate,
+ reserves.found AS found,
+ reserves.reservenotes AS reservenotes,
+ reserves.priority AS priority,
+ reserves.timestamp AS timestamp,
biblioitems.biblioitemnumber AS biblioitemnumber,
reserves.itemnumber AS itemnumber
FROM reserves
@@ -1611,16 +1878,16 @@ sub _Findgroupreserve {
return @results if @results;
my $query = qq/
- SELECT reserves.biblionumber AS biblionumber,
- reserves.borrowernumber AS borrowernumber,
- reserves.reservedate AS reservedate,
- reserves.waitingdate AS waitingdate,
- reserves.branchcode AS branchcode,
- reserves.cancellationdate AS cancellationdate,
- reserves.found AS found,
- reserves.reservenotes AS reservenotes,
- reserves.priority AS priority,
- reserves.timestamp AS timestamp,
+ SELECT reserves.reservenumber AS reservenumber,
+ reserves.biblionumber AS biblionumber,
+ reserves.borrowernumber AS borrowernumber,
+ reserves.reservedate AS reservedate,
+ reserves.branchcode AS branchcode,
+ reserves.cancellationdate AS cancellationdate,
+ reserves.found AS found,
+ reserves.reservenotes AS reservenotes,
+ reserves.priority AS priority,
+ reserves.timestamp AS timestamp,
reserveconstraints.biblioitemnumber AS biblioitemnumber,
reserves.itemnumber AS itemnumber
FROM reserves
@@ -1652,30 +1919,29 @@ ModReserveAffect, _not_ ModReserveFill)
=cut
sub _koha_notify_reserve {
- my ($itemnumber, $borrowernumber, $biblionumber) = @_;
+ my ($itemnumber, $borrowernumber, $biblionumber, $reservenumber) = @_;
- my $dbh = C4::Context->dbh;
- my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
+ my $dbh = C4::Context->dbh;
+ my $borrower = C4::Members::GetMember( 'borrowernumber'=> $borrowernumber );
my $letter_code;
my $print_mode = 0;
my $messagingprefs;
- if ( $borrower->{'email'} || $borrower->{'smsalertnumber'} ) {
+ if ( C4::Members::GetFirstValidEmailAddress($borrowernumber) || $borrower->{'smsalertnumber'} ) {
$messagingprefs = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber, message_name => 'Hold Filled' } );
return if ( !defined( $messagingprefs->{'letter_code'} ) );
$letter_code = $messagingprefs->{'letter_code'};
} else {
- $letter_code = 'HOLD_PRINT';
+ $letter_code = 'HOLD';
$print_mode = 1;
}
my $sth = $dbh->prepare("
SELECT *
FROM reserves
- WHERE borrowernumber = ?
- AND biblionumber = ?
+ WHERE reservenumber = ?
");
- $sth->execute( $borrowernumber, $biblionumber );
+ $sth->execute( $reservenumber );
my $reserve = $sth->fetchrow_hashref;
my $branch_details = GetBranchDetail( $reserve->{'branchcode'} );
@@ -1688,6 +1954,7 @@ sub _koha_notify_reserve {
C4::Letters::parseletter( $letter, 'borrowers', $borrowernumber );
C4::Letters::parseletter( $letter, 'biblio', $biblionumber );
C4::Letters::parseletter( $letter, 'reserves', $borrowernumber, $biblionumber );
+ C4::Letters::parseletter( $letter, 'items', $itemnumber );
if ( $reserve->{'itemnumber'} ) {
C4::Letters::parseletter( $letter, 'items', $reserve->{'itemnumber'} );
diff --git a/admin/branch_transfer_limits.pl b/admin/branch_transfer_limits.pl
index 64d2a70..4f8c080 100755
--- a/admin/branch_transfer_limits.pl
+++ b/admin/branch_transfer_limits.pl
@@ -91,13 +91,13 @@ while ( my $row = $sth->fetchrow_hashref ) {
## If Form Data Passed, Update the Database
if ( $input->param('updateLimits') ) {
- DeleteBranchTransferLimits();
+ DeleteBranchTransferLimits($branchcode);
foreach my $code ( @codes ) {
- foreach my $toBranch ( @branchcodes ) {
- my $isSet = not $input->param( $code . "_" . $toBranch);
+ foreach my $fromBranch ( @branchcodes ) {
+ my $isSet = not $input->param( $code . "_" . $fromBranch);
if ( $isSet ) {
- CreateBranchTransferLimit( $toBranch, $branchcode, $code );
+ CreateBranchTransferLimit( $branchcode, $fromBranch, $code );
}
}
}
@@ -115,18 +115,18 @@ my $branchcount = scalar(@branchcode_loop);
## Build the default data
my @codes_loop;
foreach my $code ( @codes ) {
- my @to_branch_loop;
+ my @from_branch_loop;
my %row_data;
$row_data{ code } = $code;
- $row_data{ to_branch_loop } = \@to_branch_loop;
- foreach my $toBranch ( @branchcodes ) {
+ $row_data{ from_branch_loop } = \@from_branch_loop;
+ foreach my $fromBranch ( @branchcodes ) {
my %row_data;
- my $isChecked = IsBranchTransferAllowed( $toBranch, $branchcode, $code );
+ my $isChecked = IsBranchTransferAllowed( $branchcode, $fromBranch, $code );
$row_data{ code } = $code;
- $row_data{ toBranch } = $toBranch;
+ $row_data{ fromBranch } = $fromBranch;
$row_data{ isChecked } = $isChecked;
- $row_data{ toBranchname } = GetBranchName($toBranch);
- push( @to_branch_loop, \%row_data );
+ $row_data{ fromBranchname } = GetBranchName($fromBranch);
+ push( @from_branch_loop, \%row_data );
}
push( @codes_loop, \%row_data );
diff --git a/admin/smart-rules.pl b/admin/smart-rules.pl
index d015ec2..1388104 100755
--- a/admin/smart-rules.pl
+++ b/admin/smart-rules.pl
@@ -25,317 +25,32 @@ use C4::Output;
use C4::Auth;
use C4::Koha;
use C4::Debug;
-use C4::Branch; # GetBranches
+use C4::Branch;
+use C4::IssuingRules;
+use C4::Circulation;
my $input = new CGI;
my $dbh = C4::Context->dbh;
-# my $flagsrequired;
-# $flagsrequired->{circulation}=1;
-my ($template, $loggedinuser, $cookie)
- = get_template_and_user({template_name => "admin/smart-rules.tmpl",
- query => $input,
- type => "intranet",
- authnotrequired => 0,
- flagsrequired => {parameters => 1},
- debug => 1,
- });
-
-my $type=$input->param('type');
-my $branch = $input->param('branch') || ( C4::Branch::onlymine() ? ( C4::Branch::mybranch() || '*' ) : '*' );
-my $op = $input->param('op');
-
-if ($op eq 'delete') {
- my $itemtype = $input->param('itemtype');
- my $categorycode = $input->param('categorycode');
- $debug and warn "deleting $1 $2 $branch";
-
- my $sth_Idelete = $dbh->prepare("delete from issuingrules where branchcode=? and categorycode=? and itemtype=?");
- $sth_Idelete->execute($branch, $categorycode, $itemtype);
-}
-elsif ($op eq 'delete-branch-cat') {
- my $categorycode = $input->param('categorycode');
- if ($branch eq "*") {
- if ($categorycode eq "*") {
- my $sth_delete = $dbh->prepare("DELETE FROM default_circ_rules");
- $sth_delete->execute();
- } else {
- my $sth_delete = $dbh->prepare("DELETE FROM default_borrower_circ_rules
- WHERE categorycode = ?");
- $sth_delete->execute($categorycode);
- }
- } elsif ($categorycode eq "*") {
- my $sth_delete = $dbh->prepare("DELETE FROM default_branch_circ_rules
- WHERE branchcode = ?");
- $sth_delete->execute($branch);
- } else {
- my $sth_delete = $dbh->prepare("DELETE FROM branch_borrower_circ_rules
- WHERE branchcode = ?
- AND categorycode = ?");
- $sth_delete->execute($branch, $categorycode);
- }
-}
-elsif ($op eq 'delete-branch-item') {
- my $itemtype = $input->param('itemtype');
- if ($branch eq "*") {
- if ($itemtype eq "*") {
- my $sth_delete = $dbh->prepare("DELETE FROM default_circ_rules");
- $sth_delete->execute();
- } else {
- my $sth_delete = $dbh->prepare("DELETE FROM default_branch_item_rules
- WHERE itemtype = ?");
- $sth_delete->execute($itemtype);
- }
- } elsif ($itemtype eq "*") {
- my $sth_delete = $dbh->prepare("DELETE FROM default_branch_circ_rules
- WHERE branchcode = ?");
- $sth_delete->execute($branch);
- } else {
- my $sth_delete = $dbh->prepare("DELETE FROM branch_item_rules
- WHERE branchcode = ?
- AND itemtype = ?");
- $sth_delete->execute($branch, $itemtype);
- }
-}
-# 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, fine, finedays, firstremind, chargeperiod,rentaldiscount) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)");
- my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, reservesallowed=?, issuelength=?, rentaldiscount=? WHERE branchcode=? AND categorycode=? AND itemtype=?");
-
- my $br = $branch; # branch
- my $bor = $input->param('categorycode'); # borrower category
- my $cat = $input->param('itemtype'); # item type
- my $fine = $input->param('fine');
- my $finedays = $input->param('finedays');
- my $firstremind = $input->param('firstremind');
- my $chargeperiod = $input->param('chargeperiod');
- my $maxissueqty = $input->param('maxissueqty');
- my $renewalsallowed = $input->param('renewalsallowed');
- my $reservesallowed = $input->param('reservesallowed');
- $maxissueqty =~ s/\s//g;
- $maxissueqty = undef if $maxissueqty !~ /^\d+/;
- my $issuelength = $input->param('issuelength');
- my $rentaldiscount = $input->param('rentaldiscount');
- $debug and warn "Adding $br, $bor, $cat, $fine, $maxissueqty";
-
- $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,$rentaldiscount, $br,$bor,$cat);
- } else {
- $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed,$reservesallowed,$issuelength,$fine,$finedays,$firstremind,$chargeperiod,$rentaldiscount);
- }
-}
-elsif ($op eq "set-branch-defaults") {
- my $categorycode = $input->param('categorycode');
- my $maxissueqty = $input->param('maxissueqty');
- my $holdallowed = $input->param('holdallowed');
- $maxissueqty =~ s/\s//g;
- $maxissueqty = undef if $maxissueqty !~ /^\d+/;
- $holdallowed =~ s/\s//g;
- $holdallowed = undef if $holdallowed !~ /^\d+/;
-
- if ($branch eq "*") {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_circ_rules");
- my $sth_insert = $dbh->prepare("INSERT INTO default_circ_rules
- (maxissueqty, holdallowed)
- VALUES (?, ?)");
- my $sth_update = $dbh->prepare("UPDATE default_circ_rules
- SET maxissueqty = ?, holdallowed = ?");
-
- $sth_search->execute();
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($maxissueqty, $holdallowed);
- } else {
- $sth_insert->execute($maxissueqty, $holdallowed);
- }
- } else {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_branch_circ_rules
- WHERE branchcode = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules
- (branchcode, maxissueqty, holdallowed)
- VALUES (?, ?, ?)");
- my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules
- SET maxissueqty = ?, holdallowed = ?
- WHERE branchcode = ?");
- $sth_search->execute($branch);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($maxissueqty, $holdallowed, $branch);
- } else {
- $sth_insert->execute($branch, $maxissueqty, $holdallowed);
- }
- }
-}
-elsif ($op eq "add-branch-cat") {
- my $categorycode = $input->param('categorycode');
- my $maxissueqty = $input->param('maxissueqty');
- $maxissueqty =~ s/\s//g;
- $maxissueqty = undef if $maxissueqty !~ /^\d+/;
-
- if ($branch eq "*") {
- if ($categorycode eq "*") {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_circ_rules");
- my $sth_insert = $dbh->prepare("INSERT INTO default_circ_rules
- (maxissueqty)
- VALUES (?)");
- my $sth_update = $dbh->prepare("UPDATE default_circ_rules
- SET maxissueqty = ?");
-
- $sth_search->execute();
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($maxissueqty);
- } else {
- $sth_insert->execute($maxissueqty);
- }
- } else {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_borrower_circ_rules
- WHERE categorycode = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO default_borrower_circ_rules
- (categorycode, maxissueqty)
- VALUES (?, ?)");
- my $sth_update = $dbh->prepare("UPDATE default_borrower_circ_rules
- SET maxissueqty = ?
- WHERE categorycode = ?");
- $sth_search->execute($branch);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($maxissueqty, $categorycode);
- } else {
- $sth_insert->execute($categorycode, $maxissueqty);
- }
- }
- } elsif ($categorycode eq "*") {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_branch_circ_rules
- WHERE branchcode = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules
- (branchcode, maxissueqty)
- VALUES (?, ?)");
- my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules
- SET maxissueqty = ?
- WHERE branchcode = ?");
- $sth_search->execute($branch);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($maxissueqty, $branch);
- } else {
- $sth_insert->execute($branch, $maxissueqty);
- }
- } else {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM branch_borrower_circ_rules
- WHERE branchcode = ?
- AND categorycode = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO branch_borrower_circ_rules
- (branchcode, categorycode, maxissueqty)
- VALUES (?, ?, ?)");
- my $sth_update = $dbh->prepare("UPDATE branch_borrower_circ_rules
- SET maxissueqty = ?
- WHERE branchcode = ?
- AND categorycode = ?");
-
- $sth_search->execute($branch, $categorycode);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($maxissueqty, $branch, $categorycode);
- } else {
- $sth_insert->execute($branch, $categorycode, $maxissueqty);
- }
- }
-}
-elsif ($op eq "add-branch-item") {
- my $itemtype = $input->param('itemtype');
- my $holdallowed = $input->param('holdallowed');
- $holdallowed =~ s/\s//g;
- $holdallowed = undef if $holdallowed !~ /^\d+/;
-
- if ($branch eq "*") {
- if ($itemtype eq "*") {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_circ_rules");
- my $sth_insert = $dbh->prepare("INSERT INTO default_circ_rules
- (holdallowed)
- VALUES (?)");
- my $sth_update = $dbh->prepare("UPDATE default_circ_rules
- SET holdallowed = ?");
-
- $sth_search->execute();
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($holdallowed);
- } else {
- $sth_insert->execute($holdallowed);
- }
- } else {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_branch_item_rules
- WHERE itemtype = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO default_branch_item_rules
- (itemtype, holdallowed)
- VALUES (?, ?)");
- my $sth_update = $dbh->prepare("UPDATE default_branch_item_rules
- SET holdallowed = ?
- WHERE itemtype = ?");
- $sth_search->execute($itemtype);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($holdallowed, $itemtype);
- } else {
- $sth_insert->execute($itemtype, $holdallowed);
- }
- }
- } elsif ($itemtype eq "*") {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM default_branch_circ_rules
- WHERE branchcode = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules
- (branchcode, holdallowed)
- VALUES (?, ?)");
- my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules
- SET holdallowed = ?
- WHERE branchcode = ?");
- $sth_search->execute($branch);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($holdallowed, $branch);
- } else {
- $sth_insert->execute($branch, $holdallowed);
- }
- } else {
- my $sth_search = $dbh->prepare("SELECT count(*) AS total
- FROM branch_item_rules
- WHERE branchcode = ?
- AND itemtype = ?");
- my $sth_insert = $dbh->prepare("INSERT INTO branch_item_rules
- (branchcode, itemtype, holdallowed)
- VALUES (?, ?, ?)");
- my $sth_update = $dbh->prepare("UPDATE branch_item_rules
- SET holdallowed = ?
- WHERE branchcode = ?
- AND itemtype = ?");
-
- $sth_search->execute($branch, $itemtype);
- my $res = $sth_search->fetchrow_hashref();
- if ($res->{total}) {
- $sth_update->execute($holdallowed, $branch, $itemtype);
- } else {
- $sth_insert->execute($branch, $itemtype, $holdallowed);
- }
- }
-}
-
+my ($template, $loggedinuser, $cookie) = get_template_and_user({
+ template_name => "admin/smart-rules.tmpl",
+ query => $input,
+ type => "intranet",
+ authnotrequired => 0,
+ flagsrequired => {parameters => 1},
+ debug => 1,
+});
+
+my $type = $input->param('type');
+my $branchcode = $input->param('branchcode') || ( C4::Branch::onlymine() ? ( C4::Branch::mybranch() || '*' ) : '*' );
+my $op = $input->param('op');
+my $confirm = $input->param('confirm');
+
+# This block builds the branch list
my $branches = GetBranches();
my @branchloop;
-for my $thisbranch (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %$branches) {
- my $selected = 1 if $thisbranch eq $branch;
+for my $thisbranch (sort { $branches->{$a}->{'branchname'} cmp $branches->{$b}->{'branchname'} } keys %$branches) {
+ my $selected = 1 if $thisbranch eq $branchcode;
my %row =(value => $thisbranch,
selected => $selected,
branchname => $branches->{$thisbranch}->{'branchname'},
@@ -343,177 +58,115 @@ for my $thisbranch (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{b
push @branchloop, \%row;
}
-my $sth=$dbh->prepare("SELECT description,categorycode FROM categories ORDER BY description");
-$sth->execute;
-my @category_loop;
-while (my $data=$sth->fetchrow_hashref){
- push @category_loop,$data;
-}
-
-$sth->finish;
-$sth=$dbh->prepare("SELECT description,itemtype FROM itemtypes ORDER BY description");
-$sth->execute;
-# $i=0;
-my @row_loop;
-my @itemtypes;
-while (my $row=$sth->fetchrow_hashref){
- push @itemtypes,$row;
-}
+# Get the patron category list
+my @category_loop = C4::Category->all;
-my $sth2 = $dbh->prepare("
- SELECT issuingrules.*, itemtypes.description AS humanitemtype, categories.description AS humancategorycode
- FROM issuingrules
- LEFT JOIN itemtypes
- ON (itemtypes.itemtype = issuingrules.itemtype)
- LEFT JOIN categories
- ON (categories.categorycode = issuingrules.categorycode)
- WHERE issuingrules.branchcode = ?
-");
-$sth2->execute($branch);
+# Get the item types list
+my @itemtypes = C4::ItemType->all;
-while (my $row = $sth2->fetchrow_hashref) {
- $row->{'humanitemtype'} ||= $row->{'itemtype'};
- $row->{'default_humanitemtype'} = 1 if $row->{'humanitemtype'} eq '*';
- $row->{'humancategorycode'} ||= $row->{'categorycode'};
- $row->{'default_humancategorycode'} = 1 if $row->{'humancategorycode'} eq '*';
- $row->{'fine'} = sprintf('%.2f', $row->{'fine'});
- push @row_loop, $row;
+if ( $op eq 'delete' ) {
+ DelIssuingRule({
+ branchcode => $branchcode,
+ categorycode => $input->param('categorycode'),
+ itemtype => $input->param('itemtype'),
+ });
}
-$sth->finish;
-
-my @sorted_row_loop = sort by_category_and_itemtype @row_loop;
+# save the values entered
+elsif ( $op eq 'add' ) {
-my $sth_branch_cat;
-if ($branch eq "*") {
- $sth_branch_cat = $dbh->prepare("
- SELECT default_borrower_circ_rules.*, categories.description AS humancategorycode
- FROM default_borrower_circ_rules
- JOIN categories USING (categorycode)
-
- ");
- $sth_branch_cat->execute();
-} else {
- $sth_branch_cat = $dbh->prepare("
- SELECT branch_borrower_circ_rules.*, categories.description AS humancategorycode
- FROM branch_borrower_circ_rules
- JOIN categories USING (categorycode)
- WHERE branch_borrower_circ_rules.branchcode = ?
- ");
- $sth_branch_cat->execute($branch);
-}
+ # Converts '' to undef, so we can have NULL in database fields
+ my $issuingrule;
+ for ( $input->param ) {
+ my $v = $input->param($_);
+ $issuingrule->{$_} = length $v ? $v : undef;
+ }
-my @branch_cat_rules = ();
-while (my $row = $sth_branch_cat->fetchrow_hashref) {
- push @branch_cat_rules, $row;
+ # We don't want op to be passed to the API
+ delete $issuingrule->{'op'};
+
+ # If the (branchcode,categorycode,itemtype) combination already exists...
+ my @issuingrules = GetIssuingRules({
+ branchcode => $issuingrule->{'branchcode'},
+ categorycode => $issuingrule->{'categorycode'},
+ itemtype => $issuingrule->{'itemtype'},
+ });
+
+ # ...we modify the existing rule...
+ if ( @issuingrules ) {
+ ModIssuingRule( $issuingrule );
+# } elsif (@issuingrules){
+# $template->param(confirm=>1);
+# $template->param(%$issuingrule);
+# foreach (@category_loop) {
+# $_->{selected}="selected" if ($_->{categorycode} eq $issuingrule->{categorycode});
+# }
+# foreach (@itemtypes) {
+# $_->{selected}="selected" if ($_->{itemtype} eq $issuingrule->{itemtype});
+# }
+ # ...else we add a new rule.
+ } else {
+ AddIssuingRule( $issuingrule );
+ }
}
-my @sorted_branch_cat_rules = sort { $a->{'humancategorycode'} cmp $b->{'humancategorycode'} } @branch_cat_rules;
-# note undef maxissueqty so that template can deal with them
-foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
- $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
-}
+# Get the issuing rules list...
+my @issuingrules = GetIssuingRulesByBranchCode($branchcode);
-my @sorted_row_loop = sort by_category_and_itemtype @row_loop;
+# ...and refine its data, row by row.
+for my $rule ( @issuingrules ) {
+ $rule->{'humanitemtype'} ||= $rule->{'itemtype'};
+ $rule->{'default_humanitemtype'} = $rule->{'humanitemtype'} eq '*';
+ $rule->{'humancategorycode'} ||= $rule->{'categorycode'};
+ $rule->{'default_humancategorycode'} = $rule->{'humancategorycode'} eq '*';
-my $sth_branch_item;
-if ($branch eq "*") {
- $sth_branch_item = $dbh->prepare("
- SELECT default_branch_item_rules.*, itemtypes.description AS humanitemtype
- FROM default_branch_item_rules
- JOIN itemtypes USING (itemtype)
- ");
- $sth_branch_item->execute();
-} else {
- $sth_branch_item = $dbh->prepare("
- SELECT branch_item_rules.*, itemtypes.description AS humanitemtype
- FROM branch_item_rules
- JOIN itemtypes USING (itemtype)
- WHERE branch_item_rules.branchcode = ?
- ");
- $sth_branch_item->execute($branch);
+ # This block is to show herited values in grey.
+ # We juste compare keys from our raw rule, with keys from the computed rule.
+ my $computedrule = GetIssuingRule($rule->{'categorycode'}, $rule->{'itemtype'}, $rule->{'branchcode'});
+ for ( keys %$rule ) {
+ if ( not defined $rule->{$_} ) {
+ $rule->{$_} = $computedrule->{$_};
+ $rule->{"herited_$_"} = 1;
+ }
+ }
}
-my @branch_item_rules = ();
-while (my $row = $sth_branch_item->fetchrow_hashref) {
- push @branch_item_rules, $row;
-}
-my @sorted_branch_item_rules = sort { $a->{'humanitemtype'} cmp $b->{'humanitemtype'} } @branch_item_rules;
+# Get the issuing rules list...
+my @issuingrules = GetIssuingRulesByBranchCode($branchcode);
-# note undef holdallowed so that template can deal with them
-foreach my $entry (@sorted_branch_item_rules) {
- $entry->{holdallowed_any} = 1 if($entry->{holdallowed} == 2);
- $entry->{holdallowed_same} = 1 if($entry->{holdallowed} == 1);
-}
+# ...and refine its data, row by row.
+for my $rule ( @issuingrules ) {
+ $rule->{'humanitemtype'} ||= $rule->{'itemtype'};
+ $rule->{'default_humanitemtype'} = $rule->{'humanitemtype'} eq '*';
+ $rule->{'humancategorycode'} ||= $rule->{'categorycode'};
+ $rule->{'default_humancategorycode'} = $rule->{'humancategorycode'} eq '*';
-$template->param(show_branch_cat_rule_form => 1);
-$template->param(branch_item_rule_loop => \@sorted_branch_item_rules);
-$template->param(branch_cat_rule_loop => \@sorted_branch_cat_rules);
+ # This block is to show herited values in grey.
+ # We juste compare keys from our raw rule, with keys from the computed rule.
+ my $computedrule = GetIssuingRule($rule->{'categorycode'}, $rule->{'itemtype'}, $rule->{'branchcode'});
+ for ( keys %$rule ) {
+ if ( not defined $rule->{$_} ) {
+ $rule->{$_} = $computedrule->{$_};
+ $rule->{"herited_$_"} = 1;
+ }
+ }
-my $sth_defaults;
-if ($branch eq "*") {
- $sth_defaults = $dbh->prepare("
- SELECT *
- FROM default_circ_rules
- ");
- $sth_defaults->execute();
-} else {
- $sth_defaults = $dbh->prepare("
- SELECT *
- FROM default_branch_circ_rules
- WHERE branchcode = ?
- ");
- $sth_defaults->execute($branch);
+ $rule->{'fine'} = sprintf('%.2f', $rule->{'fine'});
}
-my $defaults = $sth_defaults->fetchrow_hashref;
-
-if ($defaults) {
- $template->param(default_holdallowed_none => 1) if($defaults->{holdallowed} == 0);
- $template->param(default_holdallowed_same => 1) if($defaults->{holdallowed} == 1);
- $template->param(default_holdallowed_any => 1) if($defaults->{holdallowed} == 2);
- $template->param(default_maxissueqty => $defaults->{maxissueqty});
-}
$template->param(default_rules => ($defaults ? 1 : 0));
-$template->param(categoryloop => \@category_loop,
- itemtypeloop => \@itemtypes,
- rules => \@sorted_row_loop,
- branchloop => \@branchloop,
- humanbranch => ($branch ne '*' ? $branches->{$branch}->{branchname} : ''),
- current_branch => $branch,
- definedbranch => scalar(@sorted_row_loop)>0
- );
+$template->param(
+ categoryloop => \@category_loop,
+ itemtypeloop => \@itemtypes,
+ rules => \@issuingrules,
+ branchloop => \@branchloop,
+ humanbranch => ($branchcode ne '*' ? $branches->{$branchcode}->{branchname} : ''),
+ branchcode => $branchcode,
+ definedbranch => scalar(@issuingrules) > 0,
+);
+
output_html_with_http_headers $input, $cookie, $template->output;
exit 0;
-# sort by patron category, then item type, putting
-# default entries at the bottom
-sub by_category_and_itemtype {
- unless (by_category($a, $b)) {
- return by_itemtype($a, $b);
- }
-}
-
-sub by_category {
- my ($a, $b) = @_;
- if ($a->{'default_humancategorycode'}) {
- return ($b->{'default_humancategorycode'} ? 0 : 1);
- } elsif ($b->{'default_humancategorycode'}) {
- return -1;
- } else {
- return $a->{'humancategorycode'} cmp $b->{'humancategorycode'};
- }
-}
-
-sub by_itemtype {
- my ($a, $b) = @_;
- if ($a->{'default_humanitemtype'}) {
- return ($b->{'default_humanitemtype'} ? 0 : 1);
- } elsif ($b->{'default_humanitemtype'}) {
- return -1;
- } else {
- return $a->{'humanitemtype'} cmp $b->{'humanitemtype'};
- }
-}
diff --git a/circ/branchtransfers.pl b/circ/branchtransfers.pl
index ab38749..6374f71 100755
--- a/circ/branchtransfers.pl
+++ b/circ/branchtransfers.pl
@@ -87,7 +87,9 @@ if ( $request eq "KillWaiting" ) {
}
elsif ( $request eq "SetWaiting" ) {
my $item = $query->param('itemnumber');
- ModReserveAffect( $item, $borrowernumber );
+ my $itemhash=C4::Items::GetItem($item);
+ my ( $reservetype, $reserve ) = C4::Reserves::CheckReserves( undef, $itemhash->{barcode} );
+ ModReserveAffect( $item, $borrowernumber,$tobranchcd ne C4::Context->userenv->{'branch'}, $reserve->{reservenumber});
$ignoreRs = 1;
$setwaiting = 1;
$reqmessage = 1;
diff --git a/circ/circulation.pl b/circ/circulation.pl
index f145f5c..40f3a70 100755
--- a/circ/circulation.pl
+++ b/circ/circulation.pl
@@ -30,27 +30,33 @@ use C4::Dates qw/format_date/;
use C4::Branch; # GetBranches
use C4::Koha; # GetPrinter
use C4::Circulation;
+use C4::Overdues qw/CheckBorrowerDebarred/;
use C4::Members;
use C4::Biblio;
use C4::Reserves;
use C4::Context;
+use C4::Debug;
use CGI::Session;
-
+use C4::Items;
+use JSON;
+use YAML;
use Date::Calc qw(
Today
Add_Delta_YM
Add_Delta_Days
Date_to_Days
);
+use List::MoreUtils qw/uniq/;
#
# PARAMETERS READING
#
my $query = new CGI;
-
-my $sessionID = $query->cookie("CGISESSID") ;
-my $session = get_session($sessionID);
+my $dbh = C4::Context->dbh;
+my $remotehost = $query->remote_host();
+my $sessionID = $query->cookie("CGISESSID");
+my $session = get_session($sessionID);
# branch and printer are now defined by the userenv
# but first we have to check if someone has tried to change them
@@ -88,9 +94,14 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user (
my $branches = GetBranches();
-my @failedrenews = $query->param('failedrenew'); # expected to be itemnumbers
+my @failedrenews = $query->param('failedrenew'); # expected to be itemnumbers
+my @failedreturns = $query->param('failedreturn'); # expected to be barcodes
+my @renewerrors = $query->param('renewerror'); # expected to be json
+my @returnerrors = $query->param('returnerror'); # expected to be json
my %renew_failed;
-for (@failedrenews) { $renew_failed{$_} = 1; }
+my %return_failed;
+for (@failedrenews) { $renew_failed{$_} = decode_json(shift @renewerrors); }
+for (@failedreturns) { $return_failed{GetItemnumberFromBarcode($_)} = decode_json(shift @returnerrors); }
my $findborrower = $query->param('findborrower');
$findborrower =~ s|,| |g;
@@ -125,14 +136,13 @@ my $organisation = $query->param('organisations');
my $print = $query->param('print');
my $newexpiry = $query->param('dateexpiry');
my $debt_confirmed = $query->param('debt_confirmed') || 0; # Don't show the debt error dialog twice
-
+$debug && warn $newexpiry;
# Check if stickyduedate is turned off
if ( $barcode ) {
# was stickyduedate loaded from session?
if ( $stickyduedate && ! $query->param("stickyduedate") ) {
$session->clear( 'stickyduedate' );
$stickyduedate = $query->param('stickyduedate');
- $duedatespec = $query->param('duedatespec');
}
}
@@ -169,7 +179,7 @@ if($duedatespec_allow){
$datedue = $globalduedate if ($globalduedate);
}
-my $todaysdate = C4::Dates->new->output('iso');
+my $todaysdate = C4::Dates->new->output( 'iso' );
# check and see if we should print
if ( $barcode eq '' && $print eq 'maybe' ) {
@@ -231,8 +241,8 @@ if ($borrowernumber) {
# Warningdate is the date that the warning starts appearing
my ( $today_year, $today_month, $today_day) = Today();
- my ($warning_year, $warning_month, $warning_day) = split /-/, $borrower->{'dateexpiry'};
- my ( $enrol_year, $enrol_month, $enrol_day) = split /-/, $borrower->{'dateenrolled'};
+ my ($warning_year, $warning_month, $warning_day) = split (/-/, $borrower->{'dateexpiry'});
+ my ( $enrol_year, $enrol_month, $enrol_day) = split (/-/, $borrower->{'dateenrolled'});
# Renew day is calculated by adding the enrolment period to today
my ( $renew_year, $renew_month, $renew_day);
if ($enrol_year*$enrol_month*$enrol_day>0) {
@@ -250,6 +260,7 @@ if ($borrowernumber) {
flagged => "1",
noissues => "1",
expired => format_date($borrower->{dateexpiry}),
+ warning => 1,
renewaldate => format_date("$renew_year-$renew_month-$renew_day")
);
}
@@ -260,9 +271,9 @@ if ($borrowernumber) {
{
# borrower card soon to expire warn librarian
$template->param("warndeparture" => format_date($borrower->{dateexpiry}),
- flagged => "1",);
+ flagged => "1", "warning" => 1);
if (C4::Context->preference('ReturnBeforeExpiry')){
- $template->param("returnbeforeexpiry" => 1);
+ $template->param("returnbeforeexpiry" => 1, "warning => 1");
}
}
$template->param(
@@ -270,12 +281,22 @@ if ($borrowernumber) {
issuecount => $issue,
finetotal => $fines
);
+
+ my $debar = CheckBorrowerDebarred($borrowernumber);
+ if($debar){
+ $template->param(userdebarred => 1);
+ $template->param(debarredcomment => $borrower->{debarredcomment});
+ if( $debar ne "9999-12-31"){
+ $template->param(userdebarreddate => C4::Dates::format_date($debar));
+ }
+ }
}
#
# STEP 3 : ISSUING
#
#
+my $confirm_required = 0;
if ($barcode) {
# always check for blockers on issuing
my ( $error, $question ) =
@@ -321,8 +342,33 @@ if ($barcode) {
# FIXME If the issue is confirmed, we launch another time GetMemberIssuesAndFines, now display the issue count after issue
my ( $od, $issue, $fines ) = GetMemberIssuesAndFines( $borrowernumber );
$template->param( issuecount => $issue );
+
+ # Is there a circulation note?
+ my $itemnumber = GetItemnumberFromBarcode($barcode);
+ my $biblionumber = GetBiblionumberFromItemnumber($itemnumber);
+ my $record = GetMarcBiblio($biblionumber);
+ my $frameworkcode = GetFrameworkCode($biblionumber);
+ my $circnotefield = GetRecordValue('circnote', $record, $frameworkcode);
+ if (defined @$circnotefield[0]) {
+ $template->param(circnote => @$circnotefield[0]->{'subfield'});
+ }
}
+
+# Setting the right status if an hold has been confirmed
+#This should never be used in fact
+my $resbarcode = $query->param("resbarcode");
+if ($resbarcode) {
+ if ( my ( $reservetype, $reserve ) = C4::Reserves::CheckReserves( undef, $resbarcode ) ) {
+ if ( $reservetype eq "Waiting" || $reservetype eq "Reserved" ) {
+ my $transfer = C4::Context->userenv->{branch} ne $reserve->{branchcode};
+ ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber}, $transfer, $reserve->{"reservenumber"} );
+ }
+ }
+}
+
+
+
# reload the borrower info for the sake of reseting the flags.....
if ($borrowernumber) {
$borrower = GetMemberDetails( $borrowernumber, 0 );
@@ -360,11 +406,18 @@ if ($borrowernumber) {
$getreserv{itemcallnumber} = $getiteminfo->{'itemcallnumber'};
$getreserv{biblionumber} = $getiteminfo->{'biblionumber'};
$getreserv{waitingat} = GetBranchName( $num_res->{'branchcode'} );
+ my $materials = $getiteminfo->{'materials'};
+ $template->param(materials => $materials);
# check if we have a waiting status for reservations
if ( $num_res->{'found'} eq 'W' ) {
$getreserv{color} = 'reserved';
$getreserv{waiting} = 1;
# genarate information displaying only waiting reserves
+ my @maxpickupdate = $num_res->{'waitingdate'} ? GetMaxPickupDate( $num_res->{'waitingdate'}, $borrowernumber, $num_res ) : '';
+ $getreserv{'maxpickupdate'} = sprintf( "%d-%02d-%02d", @maxpickupdate );
+ $getWaitingReserveInfo{'formattedwaitingdate'} = format_date( $getreserv{'maxpickupdate'} );
+ $getreserv{'formattedwaitingdate'} = format_date( $getreserv{'maxpickupdate'} );
+
$getWaitingReserveInfo{title} = $getiteminfo->{'title'};
$getWaitingReserveInfo{biblionumber} = $getiteminfo->{'biblionumber'};
$getWaitingReserveInfo{itemtype} = $itemtypeinfo->{'description'};
@@ -420,51 +473,97 @@ my $previssues = '';
my @todaysissues;
my @previousissues;
+my @borrowers_with_issues;
+my @relissues;
+my @relprevissues;
+my $displayrelissues;
+## ADDED BY JF: new itemtype issuingrules counter stuff
+my $issued_itemtypes_count;
+my @issued_itemtypes_count_loop;
+
my $totalprice = 0;
-if ($borrower) {
-# get each issue of the borrower & separate them in todayissues & previous issues
- my ($issueslist) = GetPendingIssues($borrower->{'borrowernumber'});
- # split in 2 arrays for today & previous
- foreach my $it ( @$issueslist ) {
- my $itemtypeinfo = getitemtypeinfo( (C4::Context->preference('item-level_itypes')) ? $it->{'itype'} : $it->{'itemtype'} );
- # set itemtype per item-level_itype syspref - FIXME this is an ugly hack
- $it->{'itemtype'} = ( C4::Context->preference( 'item-level_itypes' ) ) ? $it->{'itype'} : $it->{'itemtype'};
+sub build_issue_data {
+ my $issueslist = shift;
+ my $relatives = shift;
- ($it->{'charge'}, $it->{'itemtype_charge'}) = GetIssuingCharges(
- $it->{'itemnumber'}, $borrower->{'borrowernumber'}
- );
- $it->{'charge'} = sprintf("%.2f", $it->{'charge'});
- my ($can_renew, $can_renew_error) = CanBookBeRenewed(
- $borrower->{'borrowernumber'},$it->{'itemnumber'}
- );
- $it->{"renew_error_${can_renew_error}"} = 1 if defined $can_renew_error;
+ # split in 2 arrays for today & previous
+ foreach my $it (@$issueslist) {
+ my $itemtypeinfo = getitemtypeinfo( ( C4::Context->preference('item-level_itypes') ) ? $it->{'itype'} : $it->{'itemtype'} );
+
+ # Getting borrower details
+ my $memberdetails = GetMemberDetails($it->{'borrowernumber'});
+ $it->{'borrowername'} = $memberdetails->{'firstname'} . " " . $memberdetails->{'surname'};
+
+ # set itemtype per item-level_itype syspref - FIXME this is an ugly hack
+ $it->{'itemtype'} = ( C4::Context->preference('item-level_itypes') ) ? $it->{'itype'} : $it->{'itemtype'};
+
+ ( $it->{'charge'}, $it->{'itemtype_charge'} ) = GetIssuingCharges( $it->{'itemnumber'}, $borrower->{'borrowernumber'} );
+ $it->{'charge'} = sprintf( "%.2f", $it->{'charge'} );
+ my ( $can_renew, $can_renew_error ) = CanBookBeRenewed( $borrower->{'borrowernumber'}, $it->{'itemnumber'} );
+ if ( defined $can_renew_error->{message} ) {
+ $it->{ "renew_error_" . $can_renew_error->{message} } = 1;
+ $it->{'renew_error'} = 1;
+ }
+ $it->{renewals} ||= 0;
+ $it->{$_} = $can_renew_error->{$_} for (qw(renewalsallowed reserves));
my ( $restype, $reserves ) = CheckReserves( $it->{'itemnumber'} );
- $it->{'can_renew'} = $can_renew;
- $it->{'can_confirm'} = !$can_renew && !$restype;
- $it->{'renew_error'} = $restype;
- $it->{'checkoutdate'} = C4::Dates->new($it->{'issuedate'},'iso')->output('syspref');
+ if ($restype) {
+ $it->{'reserved'} = 1;
+ $it->{$restype} = 1;
+ }
+ $it->{'can_renew'} = $can_renew;
+ $it->{'can_confirm'} = !$can_renew && !$restype;
+ $it->{'renew_error'} = $restype;
+ $it->{'checkoutdate'} = $it->{'issuedate'};
$totalprice += $it->{'replacementprice'};
- $it->{'itemtype'} = $itemtypeinfo->{'description'};
+ $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->{'author'} eq '') and $it->{'author'} = ' ';
- $it->{'renew_failed'} = $renew_failed{$it->{'itemnumber'}};
-
- if ( $todaysdate eq $it->{'issuedate'} or $todaysdate eq $it->{'lastreneweddate'} ) {
- push @todaysissues, $it;
- } else {
- push @previousissues, $it;
+ $it->{'dd'} = format_date( $it->{'date_due'} );
+ $it->{'displaydate'} = format_date( $it->{'issuedate'} );
+ ( $it->{'author'} eq '' ) and $it->{'author'} = ' ';
+ if ( defined( $return_failed{ $it->{'itemnumber'} } ) ) {
+ $it->{ 'return_error_' . $return_failed{ $it->{'itemnumber'} }->{message} } = 1;
+ delete $return_failed{ $it->{'itemnumber'} };
}
+ if ( defined( $renew_failed{ $it->{'itemnumber'} } ) ) {
+ $it->{ 'renew_error_' . $renew_failed{ $it->{'itemnumber'} }->{message} } = 1;
+ }
+ $it->{'return_failed'} = defined( $return_failed{ $it->{'itemnumber'} } );
+ $it->{'branchdisplay'} = GetBranchName( ( C4::Context->preference('HomeOrHoldingBranch') eq 'holdingbranch' ) ? $it->{'holdingbranch'} : $it->{'homebranch'} );
+
+ # ADDED BY JF: NEW ITEMTYPE COUNT DISPLAY
+ $issued_itemtypes_count->{ $it->{'itemtype'} }++;
+ if ( $todaysdate eq $it->{'checkoutdate'} or $todaysdate eq $it->{'lastreneweddate'} ) {
+ (!$relatives) ? push @todaysissues, $it : push @relissues, $it;
+ } else {
+ (!$relatives) ? push @previousissues, $it : push @relprevissues, $it;
+ }
+
}
- if ( C4::Context->preference( "todaysIssuesDefaultSortOrder" ) eq 'asc' ) {
- @todaysissues = sort { $a->{'timestamp'} cmp $b->{'timestamp'} } @todaysissues;
- }
- else {
- @todaysissues = sort { $b->{'timestamp'} cmp $a->{'timestamp'} } @todaysissues;
+
+}
+
+if ($borrower) {
+
+ # Getting borrower relatives
+ my @relborrowernumbers = GetMemberRelatives($borrower->{'borrowernumber'});
+ #push @borrowernumbers, $borrower->{'borrowernumber'};
+
+ # get each issue of the borrower & separate them in todayissues & previous issues
+ my ($issueslist) = GetPendingIssues($borrower->{'borrowernumber'});
+ my ($relissueslist) = GetPendingIssues(@relborrowernumbers);
+
+ build_issue_data($issueslist, 0);
+ build_issue_data($relissueslist, 1);
+
+ $displayrelissues = scalar($relissueslist);
+
+ if ( C4::Context->preference("todaysIssuesDefaultSortOrder") eq 'asc' ) {
+ @todaysissues = sort { $a->{'timestamp'} cmp $b->{'timestamp'} } @todaysissues;
+ } else {
+ @todaysissues = sort { $b->{'timestamp'} cmp $a->{'timestamp'} } @todaysissues;
}
if ( C4::Context->preference( "previousIssuesDefaultSortOrder" ) eq 'asc' ){
@previousissues = sort { $a->{'date_due'} cmp $b->{'date_due'} } @previousissues;
@@ -473,7 +572,48 @@ if ($borrower) {
@previousissues = sort { $b->{'date_due'} cmp $a->{'date_due'} } @previousissues;
}
}
+my @reserveswaiting;
+foreach my $itemnumber (keys %return_failed){
+ next unless $return_failed{$itemnumber}->{'reservesdata'};
+ my $hashdata=$return_failed{$itemnumber}->{'reservesdata'};
+ $hashdata->{circborrowernumber}=$borrowernumber;
+ $hashdata->{script_name}=$query->script_name();
+ push @reserveswaiting, $hashdata if (%$hashdata);
+}
+
+$template->param(reserves_waiting=>\@reserveswaiting);
+
+#### ADDED BY JF FOR COUNTS BY ITEMTYPE RULES
+# FIXME: This should utilize all the issuingrules options rather than just the defaults
+# and it should be moved to a module
+
+# how many of each is allowed?
+my $issueqty_sth = $dbh->prepare(
+ 'SELECT itemtypes.description AS description,issuingrules.itemtype,maxissueqty ' .
+ 'FROM issuingrules LEFT JOIN itemtypes ON (itemtypes.itemtype=issuingrules.itemtype) ' .
+ 'WHERE categorycode=?'
+);
+$issueqty_sth->execute(q{*}); # This is a literal asterisk, not a wildcard.
+
+while ( my $data = $issueqty_sth->fetchrow_hashref() ) {
+
+ # subtract how many of each this borrower has
+ $data->{'count'} = $issued_itemtypes_count->{ $data->{'description'} };
+ $data->{'left'} =
+ ( $data->{'maxissueqty'} -
+ $issued_itemtypes_count->{ $data->{'description'} } );
+
+ # can't have a negative number of remaining
+ if ( $data->{'left'} < 0 ) { $data->{'left'} = '0' }
+ if ( $data->{maxissueqty} <= $data->{count} ) {
+ $data->{flag} = 1;
+ }
+ if ( $data->{maxissueqty} > 0 && $data->{itemtype} !~m/^(\*|CIRC)$/ ) {
+ push @issued_itemtypes_count_loop, $data;
+ }
+}
+#### / JF
my @values;
my %labels;
@@ -503,6 +643,7 @@ if ($borrowerslist) {
#title
my $flags = $borrower->{'flags'};
+ $debug && warn Dump($flags);
foreach my $flag ( sort keys %$flags ) {
$template->param( flagged=> 1);
$flags->{$flag}->{'message'} =~ s#\n# #g;
@@ -512,20 +653,19 @@ foreach my $flag ( sort keys %$flags ) {
noissues => 'true',
);
if ( $flag eq 'GNA' ) {
- $template->param( gna => 'true' );
+ $template->param( gna => 'true', gonenoaddresscomment => $borrower->{'gonenoaddresscomment'}, warning => 1 );
}
elsif ( $flag eq 'LOST' ) {
- $template->param( lost => 'true' );
- }
- elsif ( $flag eq 'DBARRED' ) {
- $template->param( dbarred => 'true' );
- }
- elsif ( $flag eq 'CHARGES' ) {
+ $template->param( lost => 'true', lostcomment => $borrower->{'lostcomment'}, warning => 1 );
+ } elsif ( $flag eq 'DEBARRED' ) {
+ $template->param( userdebarred => 'true', warning => 1, userdebarreddate=> format_date($flags->{DEBARRED}->{dateend}),debardebarredcomment=>$borrower->{'debarredcomment'});
+ } elsif ( $flag eq 'CHARGES' ) {
$template->param(
charges => 'true',
chargesmsg => $flags->{'CHARGES'}->{'message'},
chargesamount => $flags->{'CHARGES'}->{'amount'},
- charges_is_blocker => 1
+ charges_is_blocker => 1,
+ warning => 1
);
}
elsif ( $flag eq 'CREDITS' ) {
@@ -533,6 +673,7 @@ foreach my $flag ( sort keys %$flags ) {
credits => 'true',
creditsmsg => $flags->{'CREDITS'}->{'message'},
creditsamount => sprintf("%.02f", -($flags->{'CREDITS'}->{'amount'})), # from patron's pov
+ warning => 1
);
}
}
@@ -543,6 +684,7 @@ foreach my $flag ( sort keys %$flags ) {
flagged => 1,
chargesmsg => $flags->{'CHARGES'}->{'message'},
chargesamount => $flags->{'CHARGES'}->{'amount'},
+ warning => 1
);
}
elsif ( $flag eq 'CREDITS' ) {
@@ -550,13 +692,15 @@ foreach my $flag ( sort keys %$flags ) {
credits => 'true',
creditsmsg => $flags->{'CREDITS'}->{'message'},
creditsamount => sprintf("%.02f", -($flags->{'CREDITS'}->{'amount'})), # from patron's pov
+ warning => 1
);
}
elsif ( $flag eq 'ODUES' ) {
$template->param(
odues => 'true',
flagged => 1,
- oduesmsg => $flags->{'ODUES'}->{'message'}
+ oduesmsg => $flags->{'ODUES'}->{'message'},
+ warning => 1
);
my $items = $flags->{$flag}->{'itemlist'};
@@ -611,9 +755,16 @@ if($lib_messages_loop){ $template->param(flagged => 1 ); }
my $bor_messages_loop = GetMessages( $borrowernumber, 'B', $branch );
if($bor_messages_loop){ $template->param(flagged => 1 ); }
+
# Computes full borrower address
my (undef, $roadttype_hashref) = &GetRoadTypes();
-my $address = $borrower->{'streetnumber'}.' '.$roadttype_hashref->{$borrower->{'streettype'}}.' '.$borrower->{'address'};
+my $address = $borrower->{'streetnumber'}.' ' if ($borrower->{'streetnumber'});
+$address .= $roadttype_hashref->{$borrower->{'streettype'}}.' ' if ( $roadttype_hashref->{$borrower->{'streettype'}});
+$address .= $borrower->{'address'};
+
+
+$duedatespec = "" if not ($stickyduedate or scalar $confirm_required);
+my @categories = C4::Category->all;
my $fast_cataloging = 0;
if (defined getframeworkinfo('FA')) {
@@ -633,9 +784,9 @@ $template->param(
printername => $printer,
firstname => $borrower->{'firstname'},
surname => $borrower->{'surname'},
- dateexpiry => format_date($newexpiry),
+ printer => $printer,
+ printername => $printer,
expiry => format_date($borrower->{'dateexpiry'}),
- categorycode => $borrower->{'categorycode'},
categoryname => $borrower->{description},
address => $address,
address2 => $borrower->{'address2'},
@@ -646,7 +797,6 @@ $template->param(
zipcode => $borrower->{'zipcode'},
country => $borrower->{'country'},
phone => $borrower->{'phone'} || $borrower->{'mobile'},
- cardnumber => $borrower->{'cardnumber'},
amountold => $amountold,
barcode => $barcode,
stickyduedate => $stickyduedate,
@@ -657,6 +807,9 @@ $template->param(
totaldue => sprintf('%.2f', $total),
todayissues => \@todaysissues,
previssues => \@previousissues,
+ relissues => \@relissues,
+ relprevissues => \@relprevissues,
+ displayrelissues => $displayrelissues,
inprocess => $inprocess,
memberofinstution => $member_of_institution,
CGIorganisations => $CGIorganisations,
@@ -664,8 +817,13 @@ $template->param(
circview => 1,
soundon => C4::Context->preference("SoundOn"),
fast_cataloging => $fast_cataloging,
+ categoryloop => \@categories,
);
+$template->param(newexpiry => format_date($newexpiry)) if (defined $newexpiry);
+
+SetMemberInfosInTemplate($borrowernumber, $template);
+
# save stickyduedate to session
if ($stickyduedate) {
$session->param( 'stickyduedate', $duedatespec );
diff --git a/circ/returns.pl b/circ/returns.pl
index 40706a2..b014ca3 100755
--- a/circ/returns.pl
+++ b/circ/returns.pl
@@ -36,16 +36,20 @@ use C4::Circulation;
use C4::Dates qw/format_date/;
use Date::Calc qw/Add_Delta_Days/;
use C4::Calendar;
+use C4::Budgets qw/GetCurrency/;
use C4::Print;
use C4::Reserves;
use C4::Biblio;
use C4::Items;
use C4::Members;
+use C4::Overdues qw/CheckBorrowerDebarred/;
use C4::Branch; # GetBranches GetBranchName
use C4::Koha; # FIXME : is it still useful ?
use C4::RotatingCollections;
+use C4::Debug;
my $query = new CGI;
+my $remotehost = $query->remote_host();
if (!C4::Context->userenv){
my $sessionID = $query->cookie("CGISESSID");
@@ -133,6 +137,7 @@ if ( $query->param('resbarcode') ) {
my $item = $query->param('itemnumber');
my $borrowernumber = $query->param('borrowernumber');
my $resbarcode = $query->param('resbarcode');
+ my $reservenumber = $query->param('reservenumber');
my $diffBranchReturned = $query->param('diffBranch');
my $iteminfo = GetBiblioFromItemNumber($item);
# fix up item type for display
@@ -140,18 +145,19 @@ if ( $query->param('resbarcode') ) {
my $diffBranchSend = ($userenv_branch ne $diffBranchReturned) ? $diffBranchReturned : undef;
# diffBranchSend tells ModReserveAffect whether document is expected in this library or not,
# i.e., whether to apply waiting status
- ModReserveAffect( $item, $borrowernumber, $diffBranchSend);
+ ModReserveAffect( $item, $borrowernumber, $diffBranchSend, $reservenumber );
+
# check if we have other reserves for this document, if we have a return send the message of transfer
- my ( $messages, $nextreservinfo ) = GetOtherReserves($item);
+ my ( $reservemessages, $nextreservinfo ) = GetOtherReserves($item);
my ($borr) = GetMemberDetails( $nextreservinfo, 0 );
my $name = $borr->{'surname'} . ", " . $borr->{'title'} . " " . $borr->{'firstname'};
- if ( $messages->{'transfert'} ) {
+ if ( $reservemessages->{'transfert'} ) {
$template->param(
itemtitle => $iteminfo->{'title'},
itembiblionumber => $iteminfo->{'biblionumber'},
iteminfo => $iteminfo->{'author'},
- tobranchname => GetBranchName($messages->{'transfert'}),
+ tobranchname => GetBranchName($reservemessages->{'transfert'}),
name => $name,
borrowernumber => $borrowernumber,
borcnum => $borr->{'cardnumber'},
@@ -167,10 +173,12 @@ my $returned = 0;
my $messages;
my $issueinformation;
my $itemnumber;
+my $biblio;
my $barcode = $query->param('barcode');
my $exemptfine = $query->param('exemptfine');
my $dropboxmode = $query->param('dropboxmode');
my $dotransfer = $query->param('dotransfer');
+my $override = $query->param('override');
my $calendar = C4::Calendar->new( branchcode => $userenv_branch );
#dropbox: get last open day (today - 1)
my $today = C4::Dates->new();
@@ -184,7 +192,7 @@ if ($dotransfer){
}
# actually return book and prepare item table.....
-if ($barcode) {
+if ($barcode and not $query->param('cancel')) {
$barcode =~ s/^\s*|\s*$//g; # remove leading/trailing whitespace
$barcode = barcodedecode($barcode) if C4::Context->preference('itemBarcodeInputFilter');
$itemnumber = GetItemnumberFromBarcode($barcode);
@@ -207,11 +215,19 @@ if ($barcode) {
# save the return
#
( $returned, $messages, $issueinformation, $borrower ) =
- AddReturn( $barcode, $userenv_branch, $exemptfine, $dropboxmode); # do the return
+ AddReturn( $barcode, $userenv_branch, $exemptfine, $dropboxmode,$override); # do the return
my $homeorholdingbranchreturn = C4::Context->preference('HomeOrHoldingBranchReturn') or 'homebranch';
+ my @ips=split /,|\|/, C4::Context->preference("CI-3M:AuthorizedIPs");
+ #my $pid=fork();
+ #unless($pid && $remotehost=~qr(^$ips$)){
+ #if (!$pid && any{ $remotehost eq $_ }@ips ){
+ if (any{ $remotehost eq $_ }@ips ){
+ system("../services/magnetise.pl $remotehost in");
+ #die 0;
+ }
# get biblio description
- my $biblio = GetBiblioFromItemNumber($itemnumber);
+ $biblio = GetBiblioFromItemNumber($itemnumber);
# fix up item type for display
$biblio->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $biblio->{'itype'} : $biblio->{'itemtype'};
@@ -249,7 +265,7 @@ if ($barcode) {
}
}
- elsif ( !$messages->{'BadBarcode'} ) {
+ elsif ( !$messages->{'BadBarcode'} and ! $messages->{'Wrongbranch'} ) {
$input{duedate} = 0;
$returneditems{0} = $barcode;
$riduedate{0} = 0;
@@ -267,6 +283,18 @@ if ($barcode) {
}
$template->param( inputloop => \@inputloop );
+
+# Is there a circulation note?
+my $itemnumber = GetItemnumberFromBarcode($barcode);
+my $biblionumber = GetBiblionumberFromItemnumber($itemnumber);
+my $record = GetMarcBiblio($biblionumber);
+my $frameworkcode = GetFrameworkCode($biblionumber);
+my $circnotefield = GetRecordValue('circnote', $record, $frameworkcode);
+if (defined @$circnotefield[0]) {
+ $template->param(circnote => @$circnotefield[0]->{'subfield'});
+}
+
+
my $found = 0;
my $waiting = 0;
my $reserved = 0;
@@ -290,9 +318,13 @@ if ( $messages->{'NeedsTransfer'} ){
}
if ( $messages->{'Wrongbranch'} ){
- $template->param(
- wrongbranch => 1,
- );
+ $template->param(
+ wrongbranch => $branches->{$messages->{'Wrongbranch'}->{'Wrongbranch'}}->{'branchname'},
+ rightbranch => $branches->{$messages->{'Wrongbranch'}->{'Rightbranch'}}->{'branchname'},
+ barcode => $barcode,
+ exemptfine => $exemptfine,
+ dropboxmode => $dropboxmode,
+ );
}
# case of wrong transfert, if the document wasn't transfered to the right library (according to branchtransfer (tobranch) BDD)
@@ -347,7 +379,7 @@ if ( $messages->{'ResFound'}) {
reserved => 1,
);
}
-
+ my $debarred = CheckBorrowerDebarred($reserve->{borrowernumber});
# same params for Waiting or Reserved
$template->param(
found => 1,
@@ -364,17 +396,18 @@ if ( $messages->{'ResFound'}) {
borcity => $borr->{'city'},
borzip => $borr->{'zipcode'},
borcnum => $borr->{'cardnumber'},
- debarred => $borr->{'debarred'},
+ debarred => $debarred,
gonenoaddress => $borr->{'gonenoaddress'},
barcode => $barcode,
- destbranch => $reserve->{'branchcode'},
+ reservenumber => $reserve->{'reservenumber'},
+ destbranch => $reserve->{'branchcode'},
borrowernumber => $reserve->{'borrowernumber'},
itemnumber => $reserve->{'itemnumber'},
reservenotes => $reserve->{'reservenotes'},
);
} # else { ; } # error?
}
-
+$borrower->{'flags'}= C4::Members::patronflags($borrower);
# Error Messages
my @errmsgloop;
foreach my $code ( keys %$messages ) {
@@ -400,6 +433,35 @@ foreach my $code ( keys %$messages ) {
elsif ( $code eq 'WasTransfered' ) {
; # FIXME... anything to do here?
}
+ elsif ( $code eq 'NotForLoan' ) {
+ my $fw = GetFrameworkCode($biblio->{'biblionumber'});
+ my $category = GetAuthValCode('items.notforloan',$fw);
+ my $authorizedvalues = GetAuthorisedValues($category, $messages->{$code});
+
+ foreach my $authvalue (@$authorizedvalues){
+ $err{notforloan} = $authvalue->{lib} if $authvalue->{'authorised_value'} eq $messages->{$code};
+ }
+ }
+ elsif ( $code eq 'Damaged' ) {
+ my $fw = GetFrameworkCode($biblio->{'biblionumber'});
+ my $category = GetAuthValCode('items.damaged',$fw);
+ my $authorizedvalues = GetAuthorisedValues($category, $messages->{$code});
+
+ foreach my $authvalue (@$authorizedvalues){
+ $err{damaged} = $authvalue->{lib} if $authvalue->{'authorised_value'} eq $messages->{$code};
+ }
+ }
+ elsif( $code eq 'Debarred' ){
+ $err{debarred} = format_date($messages->{'Debarred'});
+ $err{debarcardnumber} = $borrower->{cardnumber};
+ $err{debarborrowernumber} = $borrower->{borrowernumber};
+ $err{debarname} = "$borrower->{firstname} $borrower->{surname}";
+ }
+ elsif( $code eq 'HaveFines' ){
+ $err{havefines} = $borrower->{'flags'}->{'CHARGES'}->{'amount'} . %{GetCurrency()}->{symbol};
+ $err{finecardnumber} = $borrower->{cardnumber};
+ $err{finename} = "$borrower->{firstname} $borrower->{surname}";
+ }
elsif ( $code eq 'wthdrawn' ) {
$err{withdrawn} = 1;
$exit_required_p = 1;
@@ -421,7 +483,6 @@ foreach my $code ( keys %$messages ) {
}
elsif ( $code eq 'Wrongbranch' ) {
}
-
else {
die "Unknown error code $code"; # note we need all the (empty) elsif's above, or we die.
# This forces the issue of staying in sync w/ Circulation.pm
@@ -435,14 +496,10 @@ $template->param( errmsgloop => \@errmsgloop );
# patrontable ....
if ($borrower) {
- my $flags = $borrower->{'flags'};
my @flagloop;
- my $flagset;
+ my $flags=$borrower->{flags};
foreach my $flag ( sort keys %$flags ) {
my %flaginfo;
- unless ($flagset) { $flagset = 1; }
- $flaginfo{redfont} = ( $flags->{$flag}->{'noissues'} );
- $flaginfo{flag} = $flag;
if ( $flag eq 'CHARGES' ) {
$flaginfo{msg} = $flag;
$flaginfo{charges} = 1;
@@ -457,14 +514,29 @@ if ($borrower) {
foreach my $item (@$items) {
my $biblio = GetBiblioFromItemNumber( $item->{'itemnumber'});
push @waitingitemloop, {
- biblionum => $biblio->{'biblionumber'},
- barcode => $biblio->{'barcode'},
- title => $biblio->{'title'},
- brname => $branches->{ $biblio->{'holdingbranch'} }->{'branchname'},
+ biblionumber=> $biblio->{'biblionumber'},
+ author => $biblio->{'author'},
+ itemtype => $item->{'ccode'},
+ reservedate => format_date($item->{'reservedate'}),
+ waitingdate => format_date($item->{'waitingdate'}),
+ barcode => $item->{'barcode'},
+ maxpickupdate=> format_date( sprintf("%d-%02d-%02d",GetMaxPickupDate($item->{'waitingdate'}, $borrower->{borrowernumber},$item ))),
+ title => $biblio->{'title'},
+ brname => $branches->{ $item->{'branchcode'} }->{'branchname'},
+ waitinghere => ($item->{'branchcode'} eq $userenv_branch)
};
}
$flaginfo{itemloop} = \@waitingitemloop;
}
+ elsif ( $flag =~ /DEBARRED|LOST|GNA/ ) {
+ if ($flag ne "GNA"){
+ $flag=lc($flag);
+ }
+ else{
+ $flag="gonenoaddress";
+ }
+ %flaginfo=( $flag =>1, $flag."comment"=>$borrower->{$flag."comment"},dateend=>$flags->{uc($flag)}->{dateend});
+ }
elsif ( $flag eq 'ODUES' ) {
my $items = $flags->{$flag}->{'itemlist'};
my @itemloop;
@@ -490,7 +562,6 @@ if ($borrower) {
push( @flagloop, \%flaginfo );
}
$template->param(
- flagset => $flagset,
flagloop => \@flagloop,
riborrowernumber => $borrower->{'borrowernumber'},
riborcnum => $borrower->{'cardnumber'},
@@ -501,7 +572,7 @@ if ($borrower) {
}
#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 $returned_counter = ( C4::Context->preference('numReturnedItemsToShow') ) ? C4::Context->preference('numReturnedItemsToShow') : 10;
my $count = 0;
my @riloop;
foreach ( sort { $a <=> $b } keys %returneditems ) {
@@ -528,19 +599,25 @@ foreach ( sort { $a <=> $b } keys %returneditems ) {
else {
$ri{borrowernumber} = $riborrowernumber{$_};
}
-
# my %ri;
my $biblio = GetBiblioFromItemNumber(GetItemnumberFromBarcode($bar_code));
+ my $item = GetItem(GetItemnumberFromBarcode($bar_code));
# fix up item type for display
$biblio->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $biblio->{'itype'} : $biblio->{'itemtype'};
$ri{itembiblionumber} = $biblio->{'biblionumber'};
$ri{itemtitle} = $biblio->{'title'};
$ri{itemauthor} = $biblio->{'author'};
- $ri{itemtype} = $biblio->{'itemtype'};
$ri{itemnote} = $biblio->{'itemnotes'};
- $ri{ccode} = $biblio->{'ccode'};
$ri{itemnumber} = $biblio->{'itemnumber'};
+ $ri{itemtype} = $item->{'itype'};
+ $ri{ccode} = $item->{'ccode'};
+ $ri{itemcallnumber} = $item->{'itemcallnumber'};
+ $ri{homebranch} = $item->{'homebranch'};
+ $ri{holdingbranch} = $item->{'holdingbranch'};
$ri{barcode} = $bar_code;
+ my $shelflocations = GetKohaAuthorisedValues('items.location','');
+ $ri{itemlocation} = $shelflocations->{$biblio->{'location'}};
+
}
else {
last;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branch_transfer_limits.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branch_transfer_limits.tmpl
index ce4ccaf..be1b325 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branch_transfer_limits.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branch_transfer_limits.tmpl
@@ -50,6 +50,7 @@
return false;
});
+ $('#selectlibrary input[type=submit]').hide();
});
@@ -70,13 +71,18 @@
-
+
Check the boxes for the libraries you accept to checkin items from.
For all s: Check All | Uncheck All
@@ -126,11 +132,11 @@
-
+
- row"> -
- row" name="_" checked="checked" />
+ row"> -
+ row" name="_" checked="checked" />
@@ -148,6 +154,7 @@
+
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/clone-rules.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/clone-rules.tmpl
index ad675d1..dcb57a2 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/clone-rules.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/clone-rules.tmpl
@@ -24,7 +24,8 @@
Cloning of issuing rules failed!
- The rules have been cloned.
+
+
Return to Issuing rules
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl
index 92a44ce..f7f76fe 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl
@@ -6,9 +6,145 @@
//tbody>tr>td.editable').click(function() {
+ if ( ! editor ) {
+ var td = $(this);
+ var tdvalue = td.text().trim();
+ td.text('');
+
+ var type;
+ if ( $.inArray('boolean', td.attr('class').split(' ')) != -1 ) { // If the td is boolean, we display a checkbox
+ editor = $(' ');
+ editor.attr('checked', tdvalue == 'Yes' ? 'checked' : '');
+ type = 'boolean';
+ } else if ( $.inArray('triple', td.attr('class').split(' ')) != -1 ) { // else if it is a triple, we display a listbox
+ var selectedn = tdvalue == '' ? 'selected="selected"' : '';
+ var selected1 = tdvalue == 'Yes' ? 'selected="selected"' : '';
+ var selected0 = tdvalue == 'No' ? 'selected="selected"' : '';
+ var str = '';
+ str += ' ';
+ str += 'Yes ';
+ str += 'No ';
+ str += ' ';
+ editor = $(str);
+ type = 'triple';
+ } else { // else we display a textbox
+ editor = $(' ');
+ editor.attr('value', tdvalue);
+ }
+
+ editor.focus();
+
+ editor.keyup(function(e) { // on press
+ if ( e.keyCode == 13 ) { // enter
+
+ // let's build the atomic rule
+ var branchcode = $('#branchcode').val();
+ var categorycode = td.parent().children('td.categorycode').children('span').text();
+ var itemtype = td.parent().children('td.itemtype').children('span').text();
+ var varname = td.attr('class').split(' ')[0];
+ var inputvalue;
+ switch ( type ) {
+ case 'boolean':
+ inputvalue = editor.attr('checked') ? 1 : 0;
+ break;
+ default:
+ inputvalue = editor.val();
+ break;
+ }
+
+ // post it to the server
+ $.ajax({
+ url: '/cgi-bin/koha/admin/smart-rules-service.pl',
+ type: "POST",
+ async: false,
+ data: "branchcode="+branchcode+"&categorycode="+categorycode+"&itemtype="+itemtype+"&varname="+varname+"&inputvalue="+inputvalue,
+ success: function(result) {
+
+ // repopulate the td with data
+ switch ( type ) {
+ case 'boolean':
+ td.text(result == '1' ? 'Yes' : 'No');
+ break;
+ case 'triple':
+ switch ( result ) {
+ case '' : td.text(''); break;
+ case '1': td.text('Yes'); break;
+ case '0': td.text('No'); break;
+ }
+ break;
+ default:
+ td.text(result);
+ break;
+ }
+
+ editor.remove();
+ editor = false;
+ }
+ });
+ }
+ });
+
+ td.append(editor);
+ }
+ });
});
//]]>
@@ -32,25 +168,11 @@ $(document).ready(function() {
Defining circulation and fine rules for all libraries
-
-
The rules are applied from most specific to less specific, using the first found in this order:
-
- same library, same patron type, same item type
- same library, same patron type, all item types
- same library, all patron types, same item type
- same library, all patron types, all item types
- all libraries, same patron type, same item type
- all libraries, same patron type, all item types
- all libraries, all patron types, same item type
- all libraries, all patron types, all item types
-
-
To modify a rule, create a new one with the same patron type and item type.
-
-
+
-
-
Default checkout and hold policy for all libraries
-
You can set a default maximum number of checkouts and hold policy that will be used if none is defined below for a particular item type or category.
-
-
-
-
-
Checkout limit by patron category for all libraries
-
For this library, you can specify the maximum number of loans that
- a patron of a given category can make, regardless of the item type.
-
-
If the total amount loanable for a given patron category is left blank,
- no limit applies, except possibly for a limit you define for a specific item type.
-
-
+
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tmpl
index dc7ed5f..dbb43d0 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tmpl
@@ -10,6 +10,12 @@
-
- Remember for Session:
-
-
-
-
+
+ Remember for Session:
+
+
+
+
+
+
+
+
-
-
" />
" />
" />
@@ -491,9 +547,8 @@ No patron matched
-
- Cannot check out!
- Attention:
+
Cannot Check Out!
+ Attention:
@@ -814,7 +947,7 @@ No patron matched
"> , by
Item
- waiting at
+ waiting at until
in transit from
since
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tmpl
index 724a3e9..d39f4a5 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tmpl
@@ -1,6 +1,9 @@
Koha › Circulation › Check In
+