Bugzilla – Attachment 4854 Details for
Bug 5872
Enhancements to circulation (see comment 23 for details)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
proposed patch (rebased aug, 6th)
0001-Bug-5872-Circulation-management-enhancements.patch (text/plain), 261.83 KB, created by
Paul Poulain
on 2011-08-05 08:39:36 UTC
(
hide
)
Description:
proposed patch (rebased aug, 6th)
Filename:
MIME Type:
Creator:
Paul Poulain
Created:
2011-08-05 08:39:36 UTC
Size:
261.83 KB
patch
obsolete
>From e55bb4c7d1c306a069bc4ffdd85da20f1aed8fa5 Mon Sep 17 00:00:00 2001 >From: Paul Poulain <paul.poulain@biblibre.com> >Date: Mon, 1 Aug 2011 16:02:55 +0200 >Subject: [PATCH] Bug 5872 : Circulation management enhancements > >See http://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=5872 for >details >--- > .gitmodules | 3 + > C4/Circulation.pm | 894 +++++++------------- > C4/IssuingRules.pm | 278 ++++++ > C4/Members.pm | 2 + > C4/Overdues.pm | 79 +-- > C4/Reserves.pm | 632 ++++++++++---- > admin/smart-rules-service.pl | 82 ++ > admin/smart-rules.pl | 571 +++----------- > circ/branchtransfers.pl | 4 +- > circ/circulation.pl | 231 ++++-- > circ/returns.pl | 130 +++- > installer/data/mysql/de-DE/mandatory/sysprefs.sql | 2 +- > installer/data/mysql/en/mandatory/sysprefs.sql | 1 + > .../1-Obligatoire/unimarc_standard_systemprefs.sql | 2 +- > installer/data/mysql/it-IT/necessari/sysprefs.sql | 1 + > installer/data/mysql/kohastructure.sql | 14 +- > .../data/mysql/nb-NO/1-Obligatorisk/sysprefs.sql | 1 + > installer/data/mysql/pl-PL/mandatory/sysprefs.sql | 1 + > ...m_preferences_full_optimal_for_install_only.sql | 1 + > ...m_preferences_full_optimal_for_install_only.sql | 2 +- > installer/data/mysql/updatedatabase.pl | 53 ++ > .../intranet-tmpl/prog/en/css/staff-global.css | 3 + > .../prog/en/lib/jquery/plugins/jeditable | 1 + > .../prog/en/modules/admin/smart-rules.tt | 675 ++++++++------- > .../intranet-tmpl/prog/en/modules/circ/returns.tt | 3 + > .../prog/en/modules/reserve/request.tt | 41 +- > kohaversion.pl | 2 +- > opac/opac-modrequest.pl | 4 +- > opac/opac-reserve.pl | 67 +- > reserve/modrequest.pl | 19 +- > reserve/placerequest.pl | 96 +-- > reserve/renewscript.pl | 57 ++- > reserve/request.pl | 68 +- > t/db_dependent/lib/KohaTest/Circulation.pm | 2 - > .../lib/KohaTest/Members/GetMemberDetails.pm | 6 +- > t/db_dependent/lib/KohaTest/Overdues.pm | 1 - > 36 files changed, 2205 insertions(+), 1824 deletions(-) > create mode 100644 .gitmodules > create mode 100644 C4/IssuingRules.pm > create mode 100755 admin/smart-rules-service.pl > create mode 160000 koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable > >diff --git a/.gitmodules b/.gitmodules >new file mode 100644 >index 0000000..aa8c7a9 >--- /dev/null >+++ b/.gitmodules >@@ -0,0 +1,3 @@ >+[submodule "koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable"] >+ path = koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable >+ url = https://github.com/tuupola/jquery_jeditable.git >diff --git a/C4/Circulation.pm b/C4/Circulation.pm >index 179fe70..252c532 100644 >--- a/C4/Circulation.pm >+++ b/C4/Circulation.pm >@@ -28,13 +28,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 >@@ -43,6 +46,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 >@@ -74,9 +80,6 @@ BEGIN { > &GetItemIssues > &GetBorrowerIssues > &GetIssuingCharges >- &GetIssuingRule >- &GetBranchBorrowerCircRule >- &GetBranchItemRule > &GetBiblioIssues > &GetOpenIssue > &AnonymiseIssueHistory >@@ -99,6 +102,7 @@ BEGIN { > &CreateBranchTransferLimit > &DeleteBranchTransferLimits > ); >+ > } > > =head1 NAME >@@ -347,116 +351,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 +669,11 @@ sub CanBookBeIssued { > > my $branch = _GetCircControlBranch($item,$borrower); > my $itype = ( C4::Context->preference('item-level_itypes') ) ? $item->{'itype'} : $biblioitem->{'itemtype'}; >- $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $itype, $branch, $borrower ); >+ my $loanlength = GetLoanLength( $borrower->{'categorycode'}, $itype, $branch ); >+ unless ($loanlength or C4::Context->preference("AllowNotForLoanOverride")) { >+ $issuingimpossible{LOAN_LENGTH_UNDEFINED} = "$borrower->{'categorycode'}, $itype, $branch"; >+ } >+ $duedate = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $itype, $branch, $borrower ) ; > > # Offline circ calls AddIssue directly, doesn't run through here > # So issuingimpossible should be ok. >@@ -712,15 +701,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 +765,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 +810,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 +829,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 +886,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 +981,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 +1017,9 @@ sub AddIssue { > ModReserve(1, > $res->{'biblionumber'}, > $res->{'borrowernumber'}, >- $res->{'branchcode'} >+ $res->{'branchcode'}, >+ undef, >+ $res->{'reservenumber'} > ); > } > } >@@ -1101,10 +1108,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! > } >@@ -1119,314 +1127,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 GetHardDueDate >- >- my ($hardduedate,$hardduedatecompare) = &GetHardDueDate($borrowertype,$itemtype,branchcode) >- >-Get the Hard Due Date and it's comparison for an itemtype, a borrower type and a branch >- >-=cut >- >-sub GetHardDueDate { >- my ( $borrowertype, $itemtype, $branchcode ) = @_; >- my $dbh = C4::Context->dbh; >- my $sth = >- $dbh->prepare( >-"select hardduedate, hardduedatecompare from issuingrules where categorycode=? and itemtype=? and branchcode=?" >- ); >- $sth->execute( $borrowertype, $itemtype, $branchcode ); >- my $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( $borrowertype, "*", $branchcode ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( "*", $itemtype, $branchcode ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( "*", "*", $branchcode ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( $borrowertype, $itemtype, "*" ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( $borrowertype, "*", "*" ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( "*", $itemtype, "*" ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- $sth->execute( "*", "*", "*" ); >- $results = $sth->fetchrow_hashref; >- return (C4::Dates->new($results->{hardduedate}, 'iso'),$results->{hardduedatecompare}) >- if defined($results) && $results->{hardduedate} ne 'NULL'; >- >- # if no rule is set => return undefined >- return (undef, undef); >-} >- >-=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 >@@ -1504,8 +1206,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; > } >@@ -1549,9 +1251,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, >@@ -1562,7 +1272,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 >@@ -1573,12 +1282,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) { >@@ -1586,15 +1294,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 >@@ -1621,11 +1329,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..... >@@ -1661,20 +1384,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 or $messages->{'NotIssued'}) and !$resfound and ($branch ne $hbr) and not $messages->{'WrongTransfer'}){ >- 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 or $messages->{'NotIssued'}) 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; >@@ -1749,6 +1472,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); >@@ -1923,8 +1701,8 @@ sub _GetCircControlBranch { > my $circcontrol = C4::Context->preference('CircControl'); > my $branch; > >- if ($circcontrol eq 'PickupLibrary') { >- $branch= C4::Context->userenv->{'branch'} if C4::Context->userenv; >+ if ($circcontrol eq 'PickupLibrary' && C4::Context->userenv->{'branch'}) { >+ $branch= C4::Context->userenv->{'branch'}; > } elsif ($circcontrol eq 'PatronLibrary') { > $branch=$borrower->{branchcode}; > } else { >@@ -1960,7 +1738,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=?"); >@@ -2031,7 +1809,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; > } >@@ -2138,69 +1916,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 >@@ -2255,19 +2006,29 @@ sub AddRenewal { > # based on the value of the RenewalPeriodBase syspref. > unless ($datedue) { > >- my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef; >- my $itemtype = (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'}; >+ 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 ); >+ warn "RENEWALPERIOD : ".$loanlength->{renewalperiod}; >+ my $renewalperiod = $loanlength->{renewalperiod} || GetLoanLength( $borrower->{'categorycode'}, $itype, $controlbranch ); >+ >+ $datedue = CalcDateDue( $datedue, $biblio->{'itemtype'}, $issuedata->{'branchcode'}, $borrower, $renewalperiod ); > >- $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ? >- C4::Dates->new($issuedata->{date_due}, 'iso') : >- C4::Dates->new(); >- $datedue = CalcDateDue($datedue,$itemtype,$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=?" > ); >@@ -2275,8 +2036,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 ); >@@ -2692,20 +2453,22 @@ C<$startdate> = C4::Dates object representing start date of loan period (assum > C<$itemtype> = itemtype code of item in question > C<$branch> = location whose calendar to use > C<$borrower> = Borrower object >+C<$renewalperiod> = if it's a renewal, give the duration of the renew. If empty, it will be set to initial checkout duration > > =cut > > sub CalcDateDue { >- my ($startdate,$itemtype,$branch,$borrower) = @_; >+ my ($startdate,$itemtype,$branch,$borrower,$renewalperiod) = @_; > my $datedue; >- my $loanlength = GetLoanLength($borrower->{'categorycode'},$itemtype, $branch); > > # if globalDueDate ON the datedue is set to that date >+ my $rule = GetIssuingRule($borrower->{'categorycode'},$itemtype, $branch); > if ( C4::Context->preference('globalDueDate') > && ( C4::Context->preference('globalDueDate') =~ C4::Dates->regexp('syspref') ) ) { > $datedue = C4::Dates->new( C4::Context->preference('globalDueDate') ); > } else { > # otherwise, calculate the datedue as normal >+ my $loanlength = $renewalperiod || $rule->{issuelength}; > if(C4::Context->preference('useDaysMode') eq 'Days') { # ignoring calendar > my $timedue = time + ($loanlength) * 86400; > #FIXME - assumes now even though we take a startdate >@@ -2717,18 +2480,17 @@ sub CalcDateDue { > } > } > >- # if Hard Due Dates are used, retreive them and apply as necessary >- my ($hardduedate, $hardduedatecompare) = GetHardDueDate($borrower->{'categorycode'},$itemtype, $branch); >- if ( $hardduedate && $hardduedate->output('iso') && $hardduedate->output('iso') ne '0000-00-00') { >+ my ($hardduedate, $hardduedatecompare) = $rule->{qw(hardduedate hardduedatecompare)}; >+ if ( $hardduedate and $hardduedate ne '0000-00-00') { > # if the calculated due date is after the 'before' Hard Due Date (ceiling), override >- if ( $datedue->output( 'iso' ) gt $hardduedate->output( 'iso' ) && $hardduedatecompare == -1) { >- $datedue = $hardduedate; >+ if ( $datedue->output( 'iso' ) gt $hardduedate && $hardduedatecompare == -1) { >+ $datedue = C4::Dates->new( $hardduedate ); > # if the calculated date is before the 'after' Hard Due Date (floor), override >- } elsif ( $datedue->output( 'iso' ) lt $hardduedate->output( 'iso' ) && $hardduedatecompare == 1) { >- $datedue = $hardduedate; >+ } elsif ( $datedue->output( 'iso' ) lt $hardduedate && $hardduedatecompare == 1) { >+ $datedue = C4::Dates->new( $hardduedate ); > # if the hard due date is set to 'exactly', overrride > } elsif ( $hardduedatecompare == 0) { >- $datedue = $hardduedate; >+ $datedue = C4::Dates->new( $hardduedate ); > } > # in all other cases, keep the date due as it is > } >@@ -2932,18 +2694,18 @@ 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); > } > >- >- 1; >+1; > > __END__ > >diff --git a/C4/IssuingRules.pm b/C4/IssuingRules.pm >new file mode 100644 >index 0000000..807957c >--- /dev/null >+++ b/C4/IssuingRules.pm >@@ -0,0 +1,278 @@ >+package C4::IssuingRules; >+ >+# Copyright 2009 BibLibre SARL >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along with >+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, >+# Suite 330, Boston, MA 02111-1307 USA >+ >+use strict; >+use warnings; >+use C4::Context; >+use C4::Koha; >+use C4::SQLHelper qw( SearchInTable InsertInTable UpdateInTable DeleteInTable ); >+eval { >+ my $servers = C4::Context->config('memcached_servers'); >+ if ($servers) { >+ require Memoize::Memcached; >+ import Memoize::Memcached qw(memoize_memcached flush_cache); >+ my $memcached = { >+ servers => [$servers], >+ key_prefix => C4::Context->config('memcached_namespace') || 'koha', >+ }; >+ memoize_memcached( 'GetIssuingRule', memcached => $memcached, expire_time => 600000 ); #cache for 10 minutes >+ memoize_memcached( 'GetIssuingRules', memcached => $memcached, expire_time => 600000 ); #cache for 10 minutes >+ } >+ >+}; >+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); >+ >+BEGIN { >+ # set the version for version checking >+ $VERSION = 3.0.5; >+ @ISA = qw(Exporter); >+ @EXPORT = qw( >+ &GetIssuingRule >+ &GetIssuingRulesByBranchCode >+ &GetIssuingRules >+ &AddIssuingRule >+ &ModIssuingRule >+ &DelIssuingRule >+ ); >+} >+ >+=head1 NAME >+ >+C4::IssuingRules - Koha issuing rules module >+ >+=head1 SYNOPSIS >+ >+use C4::IssuingRules; >+ >+=head1 DESCRIPTION >+ >+The functions in this module deal with issuing rules. >+ >+=head1 FUNCTIONS >+ >+=head2 GetIssuingRule >+ >+Compute the issuing rule for an itemtype, a borrower category and a branch. >+Returns a hashref from the issuingrules table. >+ >+my $rule = &GetIssuingRule($categorycode, $itemtype, $branchcode); >+ >+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, default item type >+ * same library, default patron type, same item type >+ * same library, default patron type, default item type >+ * default library, same patron type, same item type >+ * default library, same patron type, default item type >+ * default library, default patron type, same item type >+ * default library, default patron type, default item type >+ >+The values in the returned hashref are inherited from a more generic rules if undef. >+ >+=cut >+#Caching GetIssuingRule >+ >+sub GetIssuingRule { >+ my ( $categorycode, $itemtype, $branchcode ) = @_; >+ $categorycode||="*"; >+ $itemtype||="*"; >+ $branchcode||="*"; >+ >+ # This configuration table defines the order of inheritance. We'll loop over it. >+ my @attempts = ( >+ [ "*" , "*" , "*" ], >+ [ "*" , $itemtype, "*" ], >+ [ $categorycode, "*" , "*" ], >+ [ $categorycode, $itemtype, "*" ], >+ [ "*" , "*" , $branchcode ], >+ [ "*" , $itemtype, $branchcode ], >+ [ $categorycode, "*" , $branchcode ], >+ [ $categorycode, $itemtype, $branchcode ], >+ ); >+ >+ # This complex query returns a nested hashref, so we can access a rule using : >+ # my $rule = $$rules{$categorycode}{$itemtype}{$branchcode}; >+ # this will be usefull in the inheritance computation code >+ my $dbh = C4::Context->dbh; >+ my $rules = $dbh->selectall_hashref( >+ "SELECT * FROM issuingrules where branchcode IN ('*',?) and itemtype IN ('*', ?) and categorycode IN ('*',?)", >+ ["branchcode", "itemtype", "categorycode"], >+ undef, >+ ( $branchcode, $itemtype, $categorycode ) >+ ); >+ >+ # This block is for inheritance. It loops over rules returned by the >+ # previous query. If a value is found in a more specific rule, it replaces >+ # the old value from the more generic rule. >+ my $oldrule; >+ for my $attempt ( @attempts ) { >+ if ( my $rule = $$rules{@$attempt[2]}{@$attempt[1]}{@$attempt[0]} ) { >+ if ( $oldrule ) { >+ for ( keys %$oldrule ) { >+ if ( defined $rule->{$_} ) { >+ $oldrule->{$_} = $rule->{$_}; >+ } >+ } >+ } else { >+ $oldrule = $rule; >+ } >+ } >+ } >+ if($oldrule){ >+ return $oldrule; >+ }else{ >+ return { >+ 'itemtype' => $itemtype, >+ 'categorycode' => $categorycode, >+ 'branchcode' => $branchcode, >+ 'holdspickupdelay' => 0, >+ # 'maxissueqty' => 0, >+ 'renewalsallowed' => 0, >+ 'firstremind' => 0, >+ 'accountsent' => 0, >+ 'reservecharge' => 0, >+ 'fine' => 0, >+ 'restrictedtype' => 0, >+ 'rentaldiscount' => 0, >+ 'chargename' => 0, >+ 'finedays' => 0, >+ 'holdrestricted' => 0, >+ 'allowonshelfholds' => 0, >+ 'reservesallowed' => 0, >+ 'chargeperiod' => 0, >+ # 'issuelength' => 0, >+ 'renewalperiod' => 0, >+ }; >+ } >+} >+ >+=head2 GetIssuingRulesByBranchCode >+ >+ my @issuingrules = &GetIssuingRulesByBranchCode($branchcode); >+ >+ Retruns a list of hashref from the issuingrules Koha table for a given >+ branchcode. >+ Each hashref will contain data from issuingrules plus human readable names of >+ patron and item categories. >+ >+=cut >+ >+sub GetIssuingRulesByBranchCode { >+ my $dbh = C4::Context->dbh; >+ my $sth = $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 = ? >+ ORDER BY humancategorycode, humanitemtype >+ "); >+ $sth->execute(shift); >+ >+ my $res = $sth->fetchall_arrayref({}); >+ >+ return @$res; >+} >+ >+=head2 GetIssuingRules >+ >+ my @issuingrules = &GetIssuingRules({ >+ branchcode => $branch, >+ categorycode => $input->param('categorycode'), >+ itemtype => $input->param('itemtype'), >+ }); >+ >+ Get an issuing rule from Koha database. >+ An alias for SearchInTable, see C4::SQLHelper for more help. >+ >+=cut >+ >+sub GetIssuingRules { >+ my $res = SearchInTable('issuingrules', shift); >+ return @$res; >+} >+ >+=head2 AddIssuingRule >+ >+ my $issuingrule = { >+ branchcode => $branch, >+ categorycode => $input->param('categorycode'), >+ itemtype => $input->param('itemtype'), >+ maxissueqty => $maxissueqty, >+ renewalsallowed => $input->param('renewalsallowed'), >+ reservesallowed => $input->param('reservesallowed'), >+ issuelength => $input->param('issuelength'), >+ fine => $input->param('fine'), >+ finedays => $input->param('finedays'), >+ firstremind => $input->param('firstremind'), >+ chargeperiod => $input->param('chargeperiod'), >+ }; >+ >+ &AddIssuingRule( $issuingrule ); >+ >+ Adds an issuing rule to Koha database. >+ An alias for InsertInTable, see C4::SQLHelper for more help. >+ >+=cut >+ >+sub AddIssuingRule { InsertInTable('issuingrules',shift); } >+ >+=head2 ModIssuingRule >+ >+ &ModIssuingRule( $issuingrule ); >+ >+ Update an issuing rule of the Koha database. >+ An alias for UpdateInTable, see C4::SQLHelper for more help. >+ >+=cut >+ >+sub ModIssuingRule { >+ if (C4::Context->config('memcached_servers')){ >+ flush_cache('GetIssuingRule'); >+ flush_cache('GetIssuingRules'); >+ } >+ UpdateInTable('issuingrules',shift); >+} >+ >+=head2 DelIssuingRule >+ >+ DelIssuingRule({ >+ branchcode => $branch, >+ categorycode => $input->param('categorycode'), >+ itemtype => $input->param('itemtype'), >+ }); >+ >+ Delete an issuing rule from Koha database. >+ An alias for DeleteInTable, see C4::SQLHelper for more help. >+ >+=cut >+ >+sub DelIssuingRule { DeleteInTable('issuingrules',shift); } >+ >+1; >+ >+=head1 AUTHOR >+ >+Koha Developement team <info@koha.org> >+ >+Jean-André Santoni <jeanandre.santoni@biblibre.com> >+ >+=cut >diff --git a/C4/Members.pm b/C4/Members.pm >index ce694e5..253298c 100644 >--- a/C4/Members.pm >+++ b/C4/Members.pm >@@ -1041,6 +1041,8 @@ sub GetPendingIssues { > $bquery .= " OR" if ($i < (scalar(@borrowernumbers) - 1)); > } > >+ $bquery="1" unless $bquery; # if nothing, set to 1 (true) to avoid a SQL error >+ > # must avoid biblioitems.* to prevent large marc and marcxml fields from killing performance > # FIXME: namespace collision: each table has "timestamp" fields. Which one is "timestamp" ? > # FIXME: circ/ciculation.pl tries to sort by timestamp! >diff --git a/C4/Overdues.pm b/C4/Overdues.pm >index 28b135c..0497d37 100644 >--- a/C4/Overdues.pm >+++ b/C4/Overdues.pm >@@ -24,6 +24,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 >@@ -72,9 +73,6 @@ BEGIN { > push @EXPORT, qw( > &GetIssuesIteminfo > ); >- # >- # &GetIssuingRules - delete. >- # use C4::Circulation::GetIssuingRule instead. > > # subs to move to Members.pm > push @EXPORT, qw( >@@ -125,7 +123,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 = " >@@ -133,7 +131,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() > "; > } > >@@ -171,7 +169,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); >@@ -246,7 +244,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 ). >@@ -270,7 +268,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. >@@ -586,7 +584,7 @@ Returns the replacement cost of the item with the given item number. > > =cut > >-#' >+ > sub ReplacementCost { > my ($itemnum) = @_; > my $dbh = C4::Context->dbh; >@@ -605,60 +603,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(); >@@ -1034,7 +994,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 > >@@ -1048,32 +1008,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 8373840..3e264dd 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,15 +390,45 @@ 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 > > sub CanBookBeReserved{ > my ($borrowernumber, $biblionumber) = @_; >- > my @items = GetItemsInfo($biblionumber); > foreach my $item (@items){ > return 1 if CanItemBeReserved($borrowernumber, $item->{itemnumber}); >@@ -399,7 +446,6 @@ 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 +453,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 +484,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 +534,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 +673,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 +699,8 @@ sub GetOtherReserves { > ModReserveMinusPriority( > $itemnumber, > $checkreserves->{'borrowernumber'}, >- $iteminfo->{'biblionumber'} >+ $iteminfo->{'biblionumber'}, >+ $checkreserves->{'reservenumber'} > ); > ModReserveStatus($itemnumber,'W'); > } >@@ -657,6 +814,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 ); >@@ -804,12 +981,6 @@ sub CheckReserves { > } else { > # See if this item is more important than what we've got so far > if ( $res->{'priority'} && $res->{'priority'} < $priority ) { >- my $borrowerinfo=C4::Members::GetMemberDetails($res->{'borrowernumber'}); >- my $iteminfo=C4::Items::GetItem($itemnumber); >- my $branch=C4::Circulation::_GetCircControlBranch($iteminfo,$borrowerinfo); >- my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$iteminfo->{'itype'}); >- next if ($branchitemrule->{'holdallowed'} == 0); >- next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $borrowerinfo->{'branchcode'})); > $priority = $res->{'priority'}; > $highest = $res; > } >@@ -846,23 +1017,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. >@@ -870,9 +1036,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 = " >@@ -880,27 +1051,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.... >@@ -908,52 +1098,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]) > >@@ -986,52 +1196,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); > >@@ -1050,16 +1256,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; > >@@ -1067,35 +1274,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} ); > } > } > >@@ -1112,7 +1319,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; >@@ -1146,9 +1352,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=?"); >@@ -1166,27 +1371,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 ); >@@ -1226,23 +1456,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); > >@@ -1327,7 +1555,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 >@@ -1362,10 +1590,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")); > } > } > >@@ -1393,15 +1621,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} ) > > } > } >@@ -1424,18 +1652,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 # >@@ -1447,7 +1706,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 ); >@@ -1458,12 +1717,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; >@@ -1474,7 +1732,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) >@@ -1509,16 +1767,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; >@@ -1558,15 +1815,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 >@@ -1588,15 +1846,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 >@@ -1617,16 +1876,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 >@@ -1658,7 +1917,7 @@ 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); >@@ -1678,21 +1937,19 @@ sub _koha_notify_reserve { > my $messagingprefs; > if ( $to_address || $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'} ); > >@@ -1705,6 +1962,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/smart-rules-service.pl b/admin/smart-rules-service.pl >new file mode 100755 >index 0000000..d498f26 >--- /dev/null >+++ b/admin/smart-rules-service.pl >@@ -0,0 +1,82 @@ >+#!/usr/bin/perl >+ >+# 2009 BibLibre <jeanandre.santoni@biblibre.com> >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 2 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along with >+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, >+# Suite 330, Boston, MA 02111-1307 USA >+# >+ >+use CGI; >+use C4::Auth; >+use C4::IssuingRules qw(ModIssuingRule GetIssuingRule); >+ >+my $cgi = CGI->new; >+ >+# get the status of the user, this will check his credentials and rights >+my ($status, $cookie, $sessionId) = C4::Auth::check_api_auth($cgi, undef); >+ >+my $result; >+ >+if ($status eq 'ok') { # if authentication is ok >+ >+ # Get the POST data >+ my $branchcode = $cgi->param('branchcode'); >+ my $categorycode = $cgi->param('categorycode'); >+ my $itemtype = $cgi->param('itemtype'); >+ my $varname = $cgi->param('varname'); >+ my $inputvalue = $cgi->param('inputvalue') eq '' ? undef : $cgi->param('inputvalue'); >+ >+ # Modify the existing rule >+ ModIssuingRule({ >+ branchcode => $branchcode, >+ categorycode => $categorycode, >+ itemtype => $itemtype, >+ $varname => $inputvalue, >+ }); >+ >+ # Compute inheritance, and return the new value; >+ my $rule = GetIssuingRule($categorycode, $itemtype, $branchcode); >+ >+ $result = $rule->{$varname}; >+ $result = C4::Dates->new($rule->{$varname},'iso')->output() if ($varname eq 'hardduedate'); >+ if ($varname eq 'fine') { >+ my $num_formatter; >+ my $cur_format = C4::Context->preference("CurrencyFormat"); >+ use Number::Format; >+ if ( $cur_format eq 'FR' ) { >+ $num_formatter = Number::Format->new( >+ 'decimal_fill' => '2', >+ 'decimal_point' => ',', >+ 'int_curr_symbol' => '', >+ 'mon_thousands_sep' => ' ', >+ 'thousands_sep' => ' ', >+ 'mon_decimal_point' => ',' >+ ); >+ } else { # US by default.. >+ $num_formatter = Number::Format->new( >+ 'int_curr_symbol' => '', >+ 'mon_thousands_sep' => ',', >+ 'mon_decimal_point' => '.' >+ ); >+ } >+ $result = $num_formatter->format_price($result); >+ } >+} else { >+ $result = 'Fail'; >+} >+ >+print CGI::header('-type'=>'text/plain', '-charset'=>'utf-8'); >+print $result; >+ >diff --git a/admin/smart-rules.pl b/admin/smart-rules.pl >index f290934..85692c5 100755 >--- a/admin/smart-rules.pl >+++ b/admin/smart-rules.pl >@@ -27,505 +27,132 @@ use C4::Koha; > use C4::Debug; > use C4::Branch; # GetBranches > use C4::Dates qw/format_date format_date_in_iso/; >+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, hardduedate, hardduedatecompare, fine, finedays, firstremind, chargeperiod,rentaldiscount) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); >- my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, reservesallowed=?, issuelength=?, hardduedate=?, hardduedatecompare=?, rentaldiscount=? WHERE branchcode=? AND categorycode=? AND itemtype=?"); >- >- my $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 $hardduedate = $input->param('hardduedate'); >- $hardduedate = format_date_in_iso($hardduedate); >- my $hardduedatecompare = $input->param('hardduedatecompare'); >- 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,$hardduedate,$hardduedatecompare,$rentaldiscount, $br,$bor,$cat); >- } else { >- $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed,$reservesallowed,$issuelength,$hardduedate,$hardduedatecompare,$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) { > push @branchloop, { > value => $thisbranch, >- selected => $thisbranch eq $branch, >+ selected => $thisbranch eq $branchcode, > branchname => $branches->{$thisbranch}->{'branchname'}, > }; > } > >-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; >-} >+# Get the patron category list >+my @category_loop = C4::Category->all; > >-$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 item types list >+my @itemtypes = C4::ItemType->all; >+ >+if ( $op eq 'delete' ) { >+ DelIssuingRule({ >+ branchcode => $branchcode, >+ categorycode => $input->param('categorycode'), >+ itemtype => $input->param('itemtype'), >+ }); > } >+# save the values entered >+elsif ( $op eq 'add' ) { > >-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); >+ # 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; >+ } > >-while (my $row = $sth2->fetchrow_hashref) { >- $row->{'current_branch'} ||= $row->{'branchcode'}; >- $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'}); >- if ($row->{'hardduedate'} ne '0000-00-00') { >- $row->{'hardduedate'} = format_date( $row->{'hardduedate'}); >- $row->{'hardduedatebefore'} = 1 if ($row->{'hardduedatecompare'} == -1); >- $row->{'hardduedateexact'} = 1 if ($row->{'hardduedatecompare'} == 0); >- $row->{'hardduedateafter'} = 1 if ($row->{'hardduedatecompare'} == 1); >+ # 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 { >- $row->{'hardduedate'} = 0; >+ AddIssuingRule( $issuingrule ); > } >- push @row_loop, $row; >-} >-$sth->finish; >- >-my @sorted_row_loop = sort by_category_and_itemtype @row_loop; >- >-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); >-} >- >-my @branch_cat_rules = (); >-while (my $row = $sth_branch_cat->fetchrow_hashref) { >- push @branch_cat_rules, $row; >-} >-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}); >-} >- >-@sorted_row_loop = sort by_category_and_itemtype @row_loop; >- >-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); >-} >- >-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; >- >-# 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); > } > >-$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); >- >-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); >+# Get the issuing rules list... >+my @issuingrules = GetIssuingRulesByBranchCode($branchcode); >+ >+# ...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 '*'; >+ if ($rule->{'hardduedate'} ne '0000-00-00') { >+ $rule->{'hardduedate'} = format_date( $rule->{'hardduedate'}); >+ $rule->{'hardduedatebefore'} = 1 if ($rule->{'hardduedatecompare'} == -1); >+ $rule->{'hardduedateexact'} = 1 if ($rule->{'hardduedatecompare'} == 0); >+ $rule->{'hardduedateafter'} = 1 if ($rule->{'hardduedatecompare'} == 1); >+ } else { >+ $rule->{'hardduedate'} = 0; >+ } >+ $rule->{'fine'} = sprintf('%.2f', $rule->{'fine'}); >+ >+ # 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 $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 => ($branchcode eq '*' ? 1 : 0)); > >-$template->param(default_rules => ($defaults ? 1 : 0)); >+$template->param( >+ categoryloop => \@category_loop, >+ itemtypeloop => \@itemtypes, >+ rules => \@issuingrules, >+ branchloop => \@branchloop, >+ humanbranch => ($branchcode ne '*' ? $branches->{$branchcode}->{branchname} : ''), >+ branchcode => $branchcode, >+ definedbranch => scalar(@issuingrules) > 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 >- ); > 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 7e0cb75..a16dd25 100755 >--- a/circ/branchtransfers.pl >+++ b/circ/branchtransfers.pl >@@ -86,7 +86,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 c34db54..4a8c2a5 100755 >--- a/circ/circulation.pl >+++ b/circ/circulation.pl >@@ -30,12 +30,16 @@ 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 >@@ -49,9 +53,10 @@ 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 >@@ -89,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; >@@ -126,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'); > } > } > >@@ -158,7 +167,7 @@ if($duedatespec_allow){ > } > } > >-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' ) { >@@ -238,7 +247,8 @@ if ($borrowernumber) { > $template->param( > flagged => "1", > noissues => "1", >- expired => "1", >+ expired => format_date($borrower->{dateexpiry}), >+ warning => 1, > renewaldate => format_date("$renew_year-$renew_month-$renew_day") > ); > } >@@ -249,9 +259,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( >@@ -259,12 +269,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 ) = >@@ -306,8 +326,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 ); >@@ -345,11 +390,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'}; >@@ -408,51 +460,75 @@ my @relissues; > my @relprevissues; > my $displayrelissues; > >+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; > > sub build_issue_data { > my $issueslist = shift; > my $relatives = shift; > >- # split in 2 arrays for today & previous >- foreach my $it ( @$issueslist ) { >- my $itemtypeinfo = getitemtypeinfo( (C4::Context->preference('item-level_itypes')) ? $it->{'itype'} : $it->{'itemtype'} ); >+ # 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'}; > $it->{'cardnumber'} = $memberdetails->{'cardnumber'}; > # 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'} >- ); >- $it->{"renew_error_${can_renew_error}"} = 1 if defined $can_renew_error; >+ $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'} ) { >- (!$relatives) ? push @todaysissues, $it : push @relissues, $it; >- } else { >- (!$relatives) ? push @previousissues, $it : push @relprevissues, $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; >+ } >+ > } > } > >@@ -484,7 +560,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; >@@ -514,6 +631,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#<br />#g; >@@ -523,20 +641,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' ) { >@@ -544,6 +661,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 > ); > } > } >@@ -554,6 +672,7 @@ foreach my $flag ( sort keys %$flags ) { > flagged => 1, > chargesmsg => $flags->{'CHARGES'}->{'message'}, > chargesamount => $flags->{'CHARGES'}->{'amount'}, >+ warning => 1 > ); > } > elsif ( $flag eq 'CREDITS' ) { >@@ -561,13 +680,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'}; >@@ -624,7 +745,13 @@ 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')) { >@@ -644,7 +771,8 @@ $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}, >@@ -679,8 +807,11 @@ $template->param( > circview => 1, > soundon => C4::Context->preference("SoundOn"), > fast_cataloging => $fast_cataloging, >+ categoryloop => \@categories, > ); > >+$template->param(newexpiry => format_date($newexpiry)) if (defined $newexpiry); >+ > # save stickyduedate to session > if ($stickyduedate) { > $session->param( 'stickyduedate', $duedatespec ); >diff --git a/circ/returns.pl b/circ/returns.pl >index 6826d7c..785e1dd 100755 >--- a/circ/returns.pl >+++ b/circ/returns.pl >@@ -37,14 +37,17 @@ 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; > >@@ -134,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 >@@ -141,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'}, >@@ -168,12 +173,14 @@ 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 $canceltransfer = $query->param('canceltransfer'); > my $dest = $query->param('dest'); >+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(); >@@ -198,7 +205,7 @@ if ($canceltransfer){ > } > > # 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); >@@ -221,11 +228,11 @@ 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'; > > # 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'}; > >@@ -283,7 +290,7 @@ if ($barcode) { > } > } > } >- elsif ( !$messages->{'BadBarcode'} ) { >+ elsif ( !$messages->{'BadBarcode'} and ! $messages->{'Wrongbranch'} ) { > $input{duedate} = 0; > $returneditems{0} = $barcode; > $riduedate{0} = 0; >@@ -301,6 +308,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; >@@ -324,9 +343,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) >@@ -382,7 +405,7 @@ if ( $messages->{'ResFound'}) { > reserved => 1, > ); > } >- >+ my $debarred = CheckBorrowerDebarred($reserve->{borrowernumber}); > # same params for Waiting or Reserved > $template->param( > found => 1, >@@ -399,17 +422,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 ) { >@@ -435,6 +459,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; >@@ -456,7 +509,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 >@@ -470,14 +522,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; >@@ -492,14 +540,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; >@@ -525,7 +588,6 @@ if ($borrower) { > push( @flagloop, \%flaginfo ); > } > $template->param( >- flagset => $flagset, > flagloop => \@flagloop, > riborrowernumber => $borrower->{'borrowernumber'}, > riborcnum => $borrower->{'cardnumber'}, >@@ -536,7 +598,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; > my $shelflocations = GetKohaAuthorisedValues('items.location',''); >@@ -564,9 +626,9 @@ 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'}; >@@ -575,14 +637,16 @@ foreach ( sort { $a <=> $b } keys %returneditems ) { > $ri{itemcallnumber} = $biblio->{'itemcallnumber'}; > $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; > >- $ri{location} = $biblio->{'location'}; >- my $shelfcode = $ri{'location'}; >- $ri{'location'} = $shelflocations->{$shelfcode} if ( defined( $shelfcode ) && defined($shelflocations) && exists( $shelflocations->{$shelfcode} ) ); >- >+ my $shelflocations = GetKohaAuthorisedValues('items.location',''); >+ $ri{itemlocation} = $shelflocations->{$biblio->{'location'}}; > } > else { > last; >diff --git a/installer/data/mysql/de-DE/mandatory/sysprefs.sql b/installer/data/mysql/de-DE/mandatory/sysprefs.sql >index 6df6a9f..8b40cde 100755 >--- a/installer/data/mysql/de-DE/mandatory/sysprefs.sql >+++ b/installer/data/mysql/de-DE/mandatory/sysprefs.sql >@@ -317,4 +317,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0, 'If ON Openlibrary book covers will be show',NULL,'YesNo'); >- >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/en/mandatory/sysprefs.sql b/installer/data/mysql/en/mandatory/sysprefs.sql >index 84075059..0e39dd1 100755 >--- a/installer/data/mysql/en/mandatory/sysprefs.sql >+++ b/installer/data/mysql/en/mandatory/sysprefs.sql >@@ -317,3 +317,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0,'If ON Openlibrary book covers will be show',NULL,'YesNo'); >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql b/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql >index d46502c..eb9f8e1 100755 >--- a/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql >+++ b/installer/data/mysql/fr-FR/1-Obligatoire/unimarc_standard_systemprefs.sql >@@ -318,4 +318,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0,'If ON Openlibrary book covers will be show',NULL,'YesNo'); >- >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/it-IT/necessari/sysprefs.sql b/installer/data/mysql/it-IT/necessari/sysprefs.sql >index 3448738..c767f67 100755 >--- a/installer/data/mysql/it-IT/necessari/sysprefs.sql >+++ b/installer/data/mysql/it-IT/necessari/sysprefs.sql >@@ -305,3 +305,4 @@ INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0,'If ON Openlibrary book covers will be show',NULL,'YesNo'); > >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql >index e9edf25..217658c 100644 >--- a/installer/data/mysql/kohastructure.sql >+++ b/installer/data/mysql/kohastructure.sql >@@ -976,9 +976,13 @@ CREATE TABLE `issuingrules` ( > `maxissueqty` int(4) default NULL, > `issuelength` int(4) default NULL, > `hardduedate` date default NULL, >- `hardduedatecompare` tinyint NOT NULL default "0", >- `renewalsallowed` smallint(6) NOT NULL default "0", >- `reservesallowed` smallint(6) NOT NULL default "0", >+ `holdspickupdelay` int(11) default NULL, >+ `renewalperiod` int(11) default NULL, >+ `allowonshelfholds` tinyint(1) default NULL, >+ `holdrestricted` tinyint(1) default NULL, >+ `hardduedatecompare` tinyint NULL default "0", >+ `renewalsallowed` smallint(6) NULL default "0", >+ `reservesallowed` smallint(6) NULL default "0", > `branchcode` varchar(10) NOT NULL default '', > PRIMARY KEY (`branchcode`,`categorycode`,`itemtype`), > KEY `categorycode` (`categorycode`), >@@ -1364,6 +1368,7 @@ CREATE TABLE `old_issues` ( > -- > DROP TABLE IF EXISTS `old_reserves`; > CREATE TABLE `old_reserves` ( >+ `reservenumber` int(11) NOT NULL AUTO_INCREMENT, > `borrowernumber` int(11) default NULL, > `reservedate` date default NULL, > `biblionumber` int(11) default NULL, >@@ -1380,6 +1385,7 @@ CREATE TABLE `old_reserves` ( > `waitingdate` date default NULL, > `expirationdate` DATE DEFAULT NULL, > `lowestPriority` tinyint(1) NOT NULL, >+ PRIMARY KEY (`reservenumber`), > KEY `old_reserves_borrowernumber` (`borrowernumber`), > KEY `old_reserves_biblionumber` (`biblionumber`), > KEY `old_reserves_itemnumber` (`itemnumber`), >@@ -1539,6 +1545,7 @@ CREATE TABLE `reserveconstraints` ( > > DROP TABLE IF EXISTS `reserves`; > CREATE TABLE `reserves` ( >+ `reservenumber` int(11) NOT NULL AUTO_INCREMENT, > `borrowernumber` int(11) NOT NULL default 0, > `reservedate` date default NULL, > `biblionumber` int(11) NOT NULL default 0, >@@ -1555,6 +1562,7 @@ CREATE TABLE `reserves` ( > `waitingdate` date default NULL, > `expirationdate` DATE DEFAULT NULL, > `lowestPriority` tinyint(1) NOT NULL, >+ PRIMARY KEY (`reservenumber`), > KEY `borrowernumber` (`borrowernumber`), > KEY `biblionumber` (`biblionumber`), > KEY `itemnumber` (`itemnumber`), >diff --git a/installer/data/mysql/nb-NO/1-Obligatorisk/sysprefs.sql b/installer/data/mysql/nb-NO/1-Obligatorisk/sysprefs.sql >index ac4c3e2..c820c45 100644 >--- a/installer/data/mysql/nb-NO/1-Obligatorisk/sysprefs.sql >+++ b/installer/data/mysql/nb-NO/1-Obligatorisk/sysprefs.sql >@@ -324,3 +324,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0,'If ON Openlibrary book covers will be show',NULL,'YesNo'); >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/pl-PL/mandatory/sysprefs.sql b/installer/data/mysql/pl-PL/mandatory/sysprefs.sql >index 3087f3c..0bf93a1 100755 >--- a/installer/data/mysql/pl-PL/mandatory/sysprefs.sql >+++ b/installer/data/mysql/pl-PL/mandatory/sysprefs.sql >@@ -316,3 +316,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0, 'If ON Openlibrary book covers will be show',NULL,'YesNo'); >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/ru-RU/mandatory/system_preferences_full_optimal_for_install_only.sql b/installer/data/mysql/ru-RU/mandatory/system_preferences_full_optimal_for_install_only.sql >index c0912ba..e674336 100755 >--- a/installer/data/mysql/ru-RU/mandatory/system_preferences_full_optimal_for_install_only.sql >+++ b/installer/data/mysql/ru-RU/mandatory/system_preferences_full_optimal_for_install_only.sql >@@ -371,3 +371,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0,'If ON Openlibrary book covers will be show',NULL,'YesNo'); >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/uk-UA/mandatory/system_preferences_full_optimal_for_install_only.sql b/installer/data/mysql/uk-UA/mandatory/system_preferences_full_optimal_for_install_only.sql >index d334469..a2976ab 100755 >--- a/installer/data/mysql/uk-UA/mandatory/system_preferences_full_optimal_for_install_only.sql >+++ b/installer/data/mysql/uk-UA/mandatory/system_preferences_full_optimal_for_install_only.sql >@@ -396,4 +396,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ( > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); > INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); > INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0, 'If ON Openlibrary book covers will be show',NULL,'YesNo'); >- >+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('ReservesControlBranch','PatronLibrary','Which branch to use do check holds limits','ItemHomeLibrary|PatronLibrary','Choice'); >diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl >index 720bd8c..1fc2c1c 100755 >--- a/installer/data/mysql/updatedatabase.pl >+++ b/installer/data/mysql/updatedatabase.pl >@@ -4398,6 +4398,59 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) { > print "Add state and country to cities table corresponding to new columns in borrowers\n"; > SetVersion($DBversion); > } >+$DBversion = '3.05.00.005'; >+if (C4::Context->preference("Version") < TransformToNum($DBversion)) { >+ $dbh->do("ALTER TABLE issuingrules ADD holdspickupdelay int(11) default NULL"); >+ $dbh->do("ALTER TABLE issuingrules ADD renewalperiod int(11) default NULL"); >+ $dbh->do("ALTER TABLE issuingrules ADD allowonshelfholds tinyint(1) default NULL"); >+ $dbh->do("ALTER TABLE issuingrules ADD holdrestricted tinyint(1) default NULL"); >+ >+ my $sth = $dbh->prepare("SELECT value from systempreferences where variable = 'AllowOnShelfHolds';"); >+ $sth->execute(); >+ my $data = $sth->fetchrow_hashref(); >+ >+ if ($data->{value}){ >+ my $updsth = $dbh->prepare("UPDATE issuingrules SET allowonshelfholds = ?"); >+ $updsth->execute( $data->{value} ); >+ } >+ $dbh->do( >+ 'INSERT IGNORE INTO issuingrules (branchcode, categorycode, itemtype,holdrestricted,maxissueqty) >+ SELECT "*","*","*",holdallowed,maxissueqty >+ FROM default_circ_rules defaults >+ ON DUPLICATE KEY update maxissueqty=defaults.maxissueqty, holdrestricted=defaults.holdallowed' >+ ); >+ $dbh->do( >+ 'INSERT IGNORE INTO issuingrules (branchcode, itemtype, categorycode,maxissueqty) >+ SELECT "*","*",categorycode,maxissueqty from default_borrower_circ_rules defaults >+ ON DUPLICATE KEY update maxissueqty=defaults.maxissueqty' >+ ); >+ $dbh->do( >+ 'INSERT IGNORE INTO issuingrules (branchcode, categorycode, itemtype,holdrestricted,maxissueqty) >+ SELECT branchcode,"*","*",holdallowed,maxissueqty from default_branch_circ_rules defaults >+ ON DUPLICATE KEY update maxissueqty=defaults.maxissueqty, holdrestricted=defaults.holdallowed' >+ ); >+ $dbh->do( >+ 'INSERT IGNORE INTO issuingrules (branchcode, categorycode, itemtype,holdrestricted) >+ SELECT "*","*",itemtype,holdallowed from default_branch_item_rules defaults >+ ON DUPLICATE KEY update holdrestricted=defaults.holdallowed' >+ ); >+ $dbh->do('ALTER TABLE issuingrules CHANGE reservesallowed reservesallowed smallint(6) NULL'); >+ $dbh->do('ALTER TABLE issuingrules CHANGE renewalsallowed renewalsallowed smallint(6) NULL'); >+# for my $tablename qw(default_circ_rules default_branch_circ_rules default_branch_item_rules default_borrower_circ_rules) { >+# $dbh->do("DROP TABLE $tablename"); >+# } >+ >+ $dbh->do("INSERT INTO systempreferences (variable,value, options, explanation, type) VALUES('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Which branch to use do check holds limits','Choice')"); >+ $dbh->do(q{ >+ ALTER TABLE reserves ADD `reservenumber` int(11) NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`reservenumber`); >+}); >+ $dbh->do(q{ >+ ALTER TABLE old_reserves ADD `reservenumber` int(11) NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`reservenumber`); >+}); >+ >+ print "Upgrade to $DBversion done (Improved circulation rules : holds and renewals at granular level)\n"; >+ SetVersion ($DBversion); >+} > > =head1 FUNCTIONS > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css >index 3c5d257..f5439a2 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css >+++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css >@@ -2081,3 +2081,6 @@ fieldset.rows+h3 {clear:both;padding-top:.5em;} > color : #cc0000; > } > >+.herited { >+ color: #ccc; >+} >diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable b/koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable >new file mode 160000 >index 0000000..29b7003 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable >@@ -0,0 +1 @@ >+Subproject commit 29b70035bbd60fc9412df3a5186367034114c9b2 >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >index 0edcece..a046642 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >@@ -2,13 +2,135 @@ > <title>Koha › Administration › Circulation and Fine Rules</title> > [% INCLUDE 'doc-head-close.inc' %] > >+<link href="/content/ui-lightness/jquery-ui-1.8.1.custom.css" rel="stylesheet" /> >+<script src="/scripts/jquery-ui-1.8.1.custom.min.js" type="text/javascript"></script> >+<script type="text/javascript" src="/intranet-tmpl/prog/en/lib/jquery/plugins/jquery.uitablefilter.js"></script> >+<script type="text/javascript" src="/intranet-tmpl/prog/en/lib/jquery/plugins/ui.tabs.js"></script> >+<script src="/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable/jquery.jeditable.js" type="text/javascript" charset="utf-8"></script> >+<script src="/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable/jquery.jeditable.datepicker.js" type="text/javascript" charset="utf-8"></script> >+<script src="/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable/js/jquery.datePicker.js" type="text/javascript" charset="utf-8"></script> >+<script src="/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable/js/date.js" type="text/javascript" charset="utf-8"></script> >+ >+<link rel="stylesheet" type="text/css" href="/intranet-tmpl/prog/en/lib/jquery/plugins/jeditable/css/datePicker.css" /> >+<link rel="stylesheet" type="text/css" href="/intranet-tmpl/prog/en/lib/jquery/plugins/ui.tabs.css" /> >+ > <script type="text/javascript"> > //<![CDATA[ > $(document).ready(function() { > $('#selectlibrary').find("input:submit").hide(); >- $('#branch').change(function() { >+ $('#branchcode').change(function() { > $('#selectlibrary').submit(); > }); >+ $('#filter').keyup(function() { >+ $.uiTableFilter( $('#smartrules'), this.value ); >+ }); >+ $('#nofilter').click(function() { >+ $('.issues').show(); >+ $('.fines').show(); >+ $('.reserves').show(); >+ $('.issues :input').removeAttr('disabled',true); >+ $('.fines :input').removeAttr('disabled',true); >+ $('.reserves :input').removeAttr('disabled',true); >+ $(this).parent().attr('class','ui-tabs-selected'); >+ $('#issuesfilter').parent().attr('class',null); >+ $('#finesfilter').parent().attr('class',null); >+ $('#reservesfilter').parent().attr('class',null); >+ }); >+ $('#issuesfilter').click(function() { >+ $('.issues').show(); >+ $('.fines').hide(); >+ $('.reserves').hide(); >+ $('.issues :input').removeAttr('disabled',true); >+ $('.fines :input').attr('disabled',true); >+ $('.reserves :input').attr('disabled',true); >+ $(this).parent().attr('class','ui-tabs-selected'); >+ $('#nofilter').parent().attr('class',null); >+ $('#finesfilter').parent().attr('class',null); >+ $('#reservesfilter').parent().attr('class',null); >+ }); >+ $('#finesfilter').click(function() { >+ $('.issues').hide(); >+ $('.fines').show(); >+ $('.reserves').hide(); >+ $('.issues :input').attr('disabled',true); >+ $('.fines :input').removeAttr('disabled',true); >+ $('.reserves :input').attr('disabled',true); >+ $(this).parent().attr('class','ui-tabs-selected'); >+ $('#issuesfilter').parent().attr('class',null); >+ $('#nofilter').parent().attr('class',null); >+ $('#reservesfilter').parent().attr('class',null); >+ }); >+ $('#reservesfilter').click(function() { >+ $('.issues').hide(); >+ $('.fines').hide(); >+ $('.reserves').show(); >+ $('.issues :input').attr('disabled',true); >+ $('.fines :input').attr('disabled',true); >+ $('.reserves :input').removeAttr('disabled',true); >+ $(this).parent().attr('class','ui-tabs-selected'); >+ $('#issuesfilter').parent().attr('class',null); >+ $('#finesfilter').parent().attr('class',null); >+ $('#nofilter').parent().attr('class',null); >+ }); >+ $('.editabledatepicker').editable('/cgi-bin/koha/admin/smart-rules-service.pl', { >+ submitdata : function(){ >+ return { >+ branchcode : $('#branchcode').val(), >+ categorycode : $(this).parent().find('td.categorycode > span:first').text(), >+ itemtype : $(this).parent().find('td.itemtype > span:first').text(), >+ varname : $(this).attr('varname') >+ }; >+ }, >+ name : 'inputvalue', >+ type: 'datepicker', >+ onblur: 'submit', >+ tooltip: _("Click to edit....") >+ }); >+ $('.datecompare').editable('/cgi-bin/koha/admin/smart-rules-service.pl', { >+ submitdata : function(){ >+ return { >+ branchcode : $('#branchcode').val(), >+ categorycode : $(this).parent().find('td.categorycode > span:first').text(), >+ itemtype : $(this).parent().find('td.itemtype > span:first').text(), >+ varname : $(this).attr('varname') >+ }; >+ }, >+ name : 'inputvalue', >+ data : "{'':'','1':"+_("'After'")+",'-1':"+_("'Before'")+",0:"+_("'Exactly On'")+"}", >+ type : 'select', >+ submit : 'OK' >+ }); >+ $('.editable').editable('/cgi-bin/koha/admin/smart-rules-service.pl', { >+ submitdata : function(){ >+ return { >+ branchcode : $('#branchcode').val(), >+ categorycode : $(this).parent().find('td.categorycode > span:first').text(), >+ itemtype : $(this).parent().find('td.itemtype > span:first').text(), >+ varname : $(this).attr('varname') >+ }; >+ }, >+ name : 'inputvalue', >+ data: function(value, settings) { >+ var retval = value.replace(/^\s+/gi, '').replace(/\s+$/gi, ''); >+ return retval; >+ }, >+ submit : 'OK' >+ }); >+ $('.triple').editable('/cgi-bin/koha/admin/smart-rules-service.pl', { >+ submitdata : function(){ >+ return { >+ branchcode : $('#branchcode').val(), >+ categorycode : $(this).parent().find('td.categorycode > span:first').text(), >+ itemtype : $(this).parent().find('td.itemtype > span:first').text(), >+ varname : $(this).attr('varname') >+ }; >+ }, >+ name : 'inputvalue', >+ data : "{'':'','1':"+_("'Yes'")+",'0':"+_("'No'")+"}", >+ type : 'select', >+ tooltip: _("Click to edit...."), >+ submit : 'OK' >+ }); > }); > //]]> > </script> >@@ -37,24 +159,10 @@ $(document).ready(function() { > Defining circulation and fine rules for all libraries > [% END %] > </h1> >- <div class="help"> >- <p>The rules are applied from most specific to less specific, using the first found in this order:</p> >- <ul> >- <li>same library, same patron type, same item type</li> >- <li>same library, same patron type, all item types</li> >- <li>same library, all patron types, same item type</li> >- <li>same library, all patron types, all item types</li> >- <li>all libraries, same patron type, same item type</li> >- <li>all libraries, same patron type, all item types</li> >- <li>all libraries, all patron types, same item type</li> >- <li>all libraries, all patron types, all item types</li> >- </ul> >- <p>To modify a rule, create a new one with the same patron type and item type.</p> >- </div> >- <div> >+ <div id="bloc100"> > <form method="get" action="/cgi-bin/koha/admin/smart-rules.pl" id="selectlibrary"> > Select a library : >- <select name="branch" id="branch" style="width:20em;"> >+ <select name="branchcode" id="branchcode" style="width:20em;"> > <option value="*">All libraries</option> > [% FOREACH branchloo IN branchloop %] > [% IF ( branchloo.selected ) %]<option value="[% branchloo.value %]" selected="selected">[% branchloo.branchname %]</option>[% ELSE %]<option value="[% branchloo.value %]">[% branchloo.branchname %]</option>[% END %] >@@ -64,304 +172,243 @@ $(document).ready(function() { > [% IF ( definedbranch ) %]<form action="/cgi-bin/koha/admin/clone-rules.pl" method="post"><label > for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidden" name="frombranch" value="[% current_branch %]" /> > <select name="tobranch" id="tobranch">[% FOREACH branchloo IN branchloop %]<option value="[% branchloo.value %]">[% branchloo.branchname %]</option>[% END %]</select> <input type="submit" value="Clone" /></form>[% END %]</fieldset> >- >- <form method="post" action="/cgi-bin/koha/admin/smart-rules.pl"> >- <input type="hidden" name="op" value="add" /> >- <table> >- <tr> >- <th>Patron Category</th> >- <th>Item Type</th> >- <th>Current Checkouts Allowed</th> >- <th>Loan Period (day)</th> >- <th>Hard Due Date</th> >- <th>Fine Amount</th> >- <th>Fine Charging Interval</th> >- <th>Fine Grace period (day)</th> >- <th>Suspension in Days (day)</th> >- <th>Renewals Allowed (count)</th> >- <th>Holds Allowed (count)</th> >- <th>Rental Discount (%)</th> >- <th> </th> >- </tr> >- [% FOREACH rule IN rules %] >- [% UNLESS ( loop.odd ) %] >- <tr class="highlight"> >- [% ELSE %] >- <tr> >- [% END %] >- <td>[% IF ( rule.default_humancategorycode ) %] >- <em>All</em> >- [% ELSE %] >- [% rule.humancategorycode %] >- [% END %] >- </td> >- <td>[% IF ( rule.default_humanitemtype ) %] >- <em>All</em> >- [% ELSE %] >- [% rule.humanitemtype %] >- [% END %] >- </td> >- <td>[% IF ( rule.unlimited_maxissueqty ) %] >- Unlimited >- [% ELSE %] >- [% rule.maxissueqty %] >- [% END %] >- </td> >- <td>[% rule.issuelength %]</td> >- <td>[% IF ( rule.hardduedate ) %] >- [% IF ( rule.hardduedatebefore ) %]before [% rule.hardduedate %]</td> >- [% ELSE %][% IF ( rule.hardduedateexact ) %]on [% rule.hardduedate %]</td> >- [% ELSE %][% IF ( rule.hardduedateafter ) %]after [% rule.hardduedate %]</td>[% END %] >- [% END %] >- [% END %] >- [% ELSE %]None defined[% END %] >- <td>[% rule.fine %]</td> >- <td>[% rule.chargeperiod %]</td> >- <td>[% rule.firstremind %]</td> >- <td>[% rule.finedays %]</td> >- <td>[% rule.renewalsallowed %]</td> >- <td>[% rule.reservesallowed %]</td> >- <td>[% rule.rentaldiscount %]</td> >- <td> >- <a class="button" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete&itemtype=[% rule.itemtype %]&categorycode=[% rule.categorycode %]&branch=[% rule.current_branch %]">Delete</a> >- </td> >- </tr> >- [% END %] >- <tr> >- <td> >- <select name="categorycode"> >- <option value="*">All</option> >- [% FOREACH categoryloo IN categoryloop %] >- <option value="[% categoryloo.categorycode %]">[% categoryloo.description %]</option> >- [% END %] >- </select> >- </td> >- <td> >- <select name="itemtype" style="width:13em;"> >- <option value="*">All</option> >- [% FOREACH itemtypeloo IN itemtypeloop %] >- <option value="[% itemtypeloo.itemtype %]">[% itemtypeloo.description %]</option> >- [% END %] >- </select> >- </td> >- <td><input name="maxissueqty" size="3" /></td> >- <td><input name="issuelength" size="3" /> </td> >- <td><select name="hardduedatecompare"> >- <option value="-1">Before</option> >- <option value="0">Exactly on</option> >- <option value="1">After</option> >- </select> >- <input type="text" size="10" id="hardduedate" name="hardduedate" value="[% hardduedate %]" /> >- [% INCLUDE 'date-format.inc' %] >- <img src="[% themelang %]/lib/calendar/cal.gif" alt="Show Calendar" border="0" id="CalendarDueDate" style="cursor: pointer;"/> >- <script language="JavaScript" type="text/javascript"> >- function refocus(calendar) { >- document.getElementById('hardduedate').focus(); >- calendar.hide(); >- }; >- Calendar.setup( >- { >- inputField : "hardduedate", >- ifFormat : "%m/%d/%Y", >- button : "CalendarDueDate", >- onClose: refocus >- } >- ); >- </script> >- </td> >- <td><input name="fine" size="4" /></td> >- <td><input name="chargeperiod" size="2" /></td> >- <td><input name="firstremind" size="2" /> </td> >- <td><input name="finedays" size="3" /> </td> >- <td><input name="renewalsallowed" size="2" /></td> >- <td><input name="reservesallowed" size="2" /></td> >- <td><input name="rentaldiscount" size="2" /></td> >- <td><input type="hidden" name="branch" value="[% current_branch %]"/><input type="submit" value="Add" class="submit" /></td> >- </tr> >- </table> >- </form> >- </div> >- <div id="defaults-for-this-library" class="container"> >- <h3>Default checkout and hold policy for [% IF ( humanbranch ) %][% humanbranch %][% ELSE %]all libraries[% END %]</h3> >- <p>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.</p> >- <form method="post" action="/cgi-bin/koha/admin/smart-rules.pl"> >- <input type="hidden" name="op" value="set-branch-defaults" /> >- <input type="hidden" name="branch" value="[% current_branch %]"/> >- <table> >- <tr> >- <th> </th> >- <th>Total Current Checkouts Allowed</th> >- <th>Hold Policy</th> >- <th> </th> >- <th> </th> >- </tr> >- <tr> >- <td><em>Defaults[% UNLESS ( default_rules ) %] (not set)[% END %]</em></td> >- <td><input type="text" name="maxissueqty" size="3" value="[% default_maxissueqty %]"/></td> >- <td> >- <select name="holdallowed"> >- [% IF ( default_holdallowed_any ) %] >- <option value="2" selected="selected"> >- [% ELSE %] >- <option value="2"> >+ <div class="toptabs"> >+ <ul class="ui-tabs-nav"> >+ <li class="ui-tabs-selected"><a id="nofilter">All</a></li> >+ <li><a id="issuesfilter">Issues</a></li> >+ <li><a id="finesfilter">Fines</a></li> >+ <li><a id="reservesfilter">Holds</a></li> >+ <div style="text-align:right;"> >+ <label for="filter" style="float:none;">Filter: </label> >+ <input type="text" name="filter" id="filter" title="Dynamic filter on itemtype/patron category"/> >+ </div> >+ </ul> >+ <div class="tabs-container"> >+ <table id="smartrules" width="100%"> >+ <thead> >+ <tr> >+ <th>Patron category</th> >+ <th>Item type</th> >+ <th class="issues">Current checkouts allowed</th> >+ <th class="issues">Loan period (day)</th> >+ <th class="issues">Hard due date compare</th> >+ <th class="issues">Hard due date</th> >+ <th class="fines">Fine amount</th> >+ <th class="fines">Fine charging interval</th> >+ <th class="fines">Grace period (day)</th> >+ <th class="fines">Suspension duration (day)</th> >+ <th class="issues">Renewals allowed (count)</th> >+ <th class="issues">Renewals period (days)</th> >+ <th class="reserves">Holds allowed (count)</th> >+ <th class="reserves">Holds pickup delay (day)</th> >+ <th class="reserves">Allow on shelf holds</th> >+ <th class="reserves">Holds restricted to library</th> >+ <th class="issues">Rental discount (%)</th> >+ <th> </th> >+ </tr> >+ </thead> >+ <tbody> >+ [% FOREACH rule IN rules %] >+ <tr> >+ <td class="categorycode"> >+ <span style="display:none;">[% rule.categorycode %]</span> >+ [% IF ( rule.default_humancategorycode ) %] >+ <em>Default</em> >+ [% ELSE %] >+ [% rule.humancategorycode %] >+ [% END %] >+ </td> >+ <td class="itemtype"> >+ <span style="display:none;">[% rule.itemtype %]</span> >+ [% IF ( rule.default_humanitemtype ) %] >+ <em>Default</em> >+ [% ELSE %] >+ [% rule.humanitemtype %] >+ [% END %] >+ </td> >+ [% IF ( rule.herited_maxissueqty ) %] >+ <td class="maxissueqty editable issues herited" varname="maxissueqty"> >+ [% ELSE %] >+ <td class="issues editable" varname="maxissueqty"> >+ [% END %] >+ [% IF ( rule.unlimited_maxissueqty ) %] >+ Unlimited >+ [% ELSE %] >+ [% rule.maxissueqty %] >+ [% END %] >+ </td> >+ [% IF ( rule.herited_issuelength ) %] >+ <td class="issuelength editable issues herited" varname="issuelength"> >+ [% ELSE %] >+ <td class="issuelength editable issues" varname="issuelength"> >+ [% END %][% rule.issuelength %] >+ </td> >+ [% IF ( rule.herited_hardduedatecompare ) %] >+ <td class="hardduedatecompare issues datecompare herited" varname="hardduedatecompare"> >+ [% ELSE %] >+ <td class="hardduedatecompare issues datecompare" varname="hardduedatecompare"> >+ [% END %] >+ [% rule.hardduedatecompare %] >+ </td> >+ [% IF (rule.herited_hardduedate ) %] >+ <td class="hardduedate editabledatepicker issues herited" varname="hardduedate"> >+ [% ELSE %] >+ <td class="hardduedate editabledatepicker issues" varname="hardduedate"> >+ [% END %] >+ [% rule.hardduedate %] >+ </td> >+ [% IF (rule.herited_fine ) %] >+ <td class="fine editable fines herited" varname="fine"> >+ [% ELSE %] >+ <td class="fine editable fines" varname="fine"> >+ [% END %] >+ [% fine %] >+ </td> >+ [% IF ( rule.herited_chargeperiod ) %] >+ <td class="chargeperiod editable fines herited" varname="chargeperiod"> >+ [% ELSE %] >+ <td class="chargeperiod editable fines" varname="chargeperiod"> >+ [% END %] >+ [% rule.chargeperiod %] >+ </td> >+ [% IF ( rule.herited_firstremind ) %] >+ <td class="firstremind editable fines herited" varname="firstremind"> >+ [% ELSE %] >+ <td class="firstremind editable fines" varname="firstremind"> >+ [% END %] >+ [% rule.firstremind %] >+ </td> >+ [% IF ( rule.herited_finedays ) %] >+ <td class="finedays editable fines herited" varname="finedays"> >+ [% ELSE %] >+ <td class="finedays editable fines" varname="finedays"> >+ [% END %] >+ [% rule.finedays %] >+ </td> >+ [% IF ( rule.herited_renewalsallowed ) %] >+ <td class="renewalsallowed editable issues herited" varname="renewalsallowed"> >+ [% ELSE %] >+ <td class="renewalsallowed editable issues" varname="renewalsallowed"> >+ [% END %] >+ [% rule.renewalsallowed %] >+ </td> >+ [% IF ( rule.herited_renewalperiod ) %] >+ <td class="renewalperiod editable issues herited" varname="renewalperiod"> >+ [% ELSE %] >+ <td class="renewalperiod editable issues" varname="renewalperiod"> >+ [% END %] >+ [% rule.renewalperiod %] >+ </td> >+ [% IF ( rule.herited_reservesallowed ) %] >+ <td class="reservesallowed editable reserves herited" varname="reservesallowed"> >+ [% ELSE %] >+ <td class="reservesallowed editable reserves" varname="reservesallowed"> >+ [% END %] >+ [% rule.reservesallowed %] >+ </td> >+ [% IF ( rule.herited_holdspickupdelay ) %] >+ <td class="holdspickupdelay editable reserves herited" varname="holdspickupdelay"> >+ [% ELSE %] >+ <td class="holdspickupdelay editable reserves" varname="holdspickupdelay"> >+ [% END %] >+ [% rule.holdspickupdelay %] >+ </td> >+ [% IF ( rule.herited_allowonshelfholds ) %] >+ <td class="allowonshelfholds reserves triple herited" varname="allowonshelfholds"> >+ [% ELSE %] >+ <td class="allowonshelfholds reserves triple" varname="allowonshelfholds"> >+ [% END %] >+ [% IF ( rule.allowonshelfholds ) %] >+ Yes >+ [% ELSE %] >+ No >+ [% END %] >+ </td> >+ [% IF ( rule.herited_holdrestricted ) %] >+ <td class="holdrestricted reserves triple herited" varname="holdrestricted"> >+ [% ELSE %] >+ <td class="holdrestricted reserves triple" varname="holdrestricted"> >+ [% END %] >+ [% IF ( rule.holdrestricted ) %] >+ Yes >+ [% ELSE %] >+ No >+ [% END %] >+ </td> >+ [% IF ( rule.herited_rentaldiscount ) %] >+ <td class="rentaldiscount editable issues herited" varname="rentaldiscount"> >+ [% ELSE %] >+ <td class="rentaldiscount editable issues" varname="rentaldiscount"> >+ [% END %] >+ [% rule.rentaldiscount %] >+ </td> >+ <td> >+ <a class="button" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete&itemtype=[% rule.itemtype %]&categorycode=[% rule.categorycode %]&branchcode=[% rule.branchcode %]">Delete</a> >+ </td> >+ </tr> > [% END %] >- From Any Library >- </option> >- [% IF ( default_holdallowed_same ) %] >- <option value="1" selected="selected"> >- [% ELSE %] >- <option value="1"> >- [% END %] >- From Home Library >- </option> >- [% IF ( default_holdallowed_none ) %] >- <option value="0" selected="selected"> >- [% ELSE %] >- <option value="0"> >- [% END %] >- No Holds Allowed >- </option> >- </select> >- </td> >- <td><input type="submit" value="Save" class="submit" /></td> >- <td> >- <a class="button" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete-branch-cat&categorycode=*&branch=[% current_branch %]">Unset</a> >- </td> >- </tr> >- </table> >- </form> >- </div> >- [% IF ( show_branch_cat_rule_form ) %] >- <div id="holds-policy-by-patron-category" class="container"> >- <h3>Checkout limit by patron category for [% IF ( humanbranch ) %][% humanbranch %][% ELSE %]all libraries[% END %]</h3> >- <p>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. >- </p> >- <p>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. >- </p> >- <form method="post" action="/cgi-bin/koha/admin/smart-rules.pl"> >- <input type="hidden" name="op" value="add-branch-cat" /> >- <input type="hidden" name="branch" value="[% current_branch %]"/> >- <table> >- <tr> >- <th>Patron Category</th> >- <th>Total Current Checkouts Allowed</th> >- <th> </th> >- </tr> >- [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %] >- [% UNLESS ( loop.odd ) %] >- <tr class="highlight"> >- [% ELSE %] >- <tr> >- [% END %] >- <td>[% IF ( branch_cat_rule_loo.default_humancategorycode ) %] >- <em>Default</em> >- [% ELSE %] >- [% branch_cat_rule_loo.humancategorycode %] >- [% END %] >- </td> >- <td>[% IF ( branch_cat_rule_loo.unlimited_maxissueqty ) %] >- Unlimited >- [% ELSE %] >- [% branch_cat_rule_loo.maxissueqty %] >- [% END %] >- </td> >- <td> >- <a class="button" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete-branch-cat&categorycode=[% branch_cat_rule_loo.categorycode %]&branch=[% branch_cat_rule_loo.current_branch %]">Delete</a> >- </td> >- </tr> >- [% END %] >- <tr> >- <td> >- <select name="categorycode"> >- [% FOREACH categoryloo IN categoryloop %] >- <option value="[% categoryloo.categorycode %]">[% categoryloo.description %]</option> >- [% END %] >- </select> >- </td> >- <td><input name="maxissueqty" size="3" /></td> >- <td><input type="submit" value="Add" class="submit" /></td> >- </tr> >- </table> >- </form> >- </div> >- [% END %] >- <div id="holds-policy-by-item-type" class="container"> >- <h3>Holds policy by item type for [% IF ( humanbranch ) %][% humanbranch %][% ELSE %]all libraries[% END %]</h3> >- <p> >- For this library, you can edit rules for given itemtypes, regardless >- of the patron's category. >- </p> >- <p> >- Currently, this means hold policies. >- The various policies have the following effects: >- </p> >- <ul> >- <li><strong>From Any Library:</strong> Patrons from any library may put this item on hold. <cite>(default if none is defined)</cite></li> >- <li><strong>From Home Library:</strong> Only patrons from the item's home library may put this book on hold.</li> >- <li><strong>No Holds Allowed:</strong> No patron may put this book on hold.</li> >- </ul> >- <p> >- Note that if the system preference >- <code>AllowHoldPolicyOverride</code> is enabled, these policies can >- be overridden by your circulation staff. Also, these policies are >- based on the patron's home library, <em>not</em> the library where the hold is being placed.. >- </p> >- >- <form method="post" action="/cgi-bin/koha/admin/smart-rules.pl"> >- <input type="hidden" name="op" value="add-branch-item" /> >- <input type="hidden" name="branch" value="[% current_branch %]"/> >- <table> >- <tr> >- <th>Item Type</th> >- <th>Hold Policy</th> >- <th> </th> >- </tr> >- [% FOREACH branch_item_rule_loo IN branch_item_rule_loop %] >- [% UNLESS ( loop.odd ) %] >- <tr class="highlight"> >- [% ELSE %] >- <tr> >- [% END %] >- <td>[% IF ( branch_item_rule_loo.default_humanitemtype ) %] >- <em>Default</em> >- [% ELSE %] >- [% branch_item_rule_loo.humanitemtype %] >- [% END %] >- </td> >- <td>[% IF ( branch_item_rule_loo.holdallowed_any ) %] >- From Any Library >- [% ELSIF ( branch_item_rule_loo.holdallowed_same ) %] >- From Home Library >- [% ELSE %] >- No Holds Allowed >- [% END %] >- </td> >- <td> >- <a class="button" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete-branch-item&itemtype=[% branch_item_rule_loo.itemtype %]&branch=[% branch_item_rule_loo.current_branch %]">Delete</a> >- </td> >- </tr> >- [% END %] >- <tr> >- <td> >- <select name="itemtype"> >- [% FOREACH itemtypeloo IN itemtypeloop %] >- <option value="[% itemtypeloo.itemtype %]">[% itemtypeloo.description %]</option> >- [% END %] >- </select> >- </td> >- <td> >- <select name="holdallowed"> >- <option value="2">From Any Library</option> >- <option value="1">From Home Library</option> >- <option value="0">No Holds Allowed</option> >- </select> >- </td> >- <td><input type="submit" value="Add" class="submit" /></td> >- </tr> >- </table> >- </form> >+ </tbody> >+ <form method="post" name="inputrules" action="/cgi-bin/koha/admin/smart-rules.pl"> >+ <input type="hidden" name="op" value="add" /> >+ <tfoot> >+ <tr> >+ <td> >+ <select name="categorycode" id="categorycode" style="width:12em;"> >+ <option value="*">Default</option> >+ [% FOREACH category IN categoryloop %] >+ [% IF ( category.selected ) %] >+ <option value="[% category.categorycode %]" selected="selected">[% category.description %]</option> >+ [% ELSE %] <option value="[% category.categorycode %]" >[% category.description %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </td> >+ <td> >+ <select name="itemtype" style="width:12em;"> >+ <option value="*">Default</option> >+ [% FOREACH itemtypeL IN itemtypeloop %] >+ [% IF itemtypeL.selected %] >+ <option value="[% itemtypeL.itemtype %]" selected="selected">[% itemtypeL.description %]</option> >+ [% ELSE %] <option value="[% itemtypeL.itemtype %]">[% itemtypeL.description %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ </td> >+ <td class="issues"><input name="maxissueqty" size="3" value="[% maxissueqty %]"/></td> >+ <td class="issues"><input name="issuelength" size="3" value="[% issuelength %]"/> </td> >+ <td class="issues"><select name="hardduedatecompare" > >+ <option value="-1">Before</option> >+ <option value="0">Exactly on</option> >+ <option value="1">After</option> >+ </select> >+ </td> >+ <td class="issues"> >+ <input type="text" size="10" name="hardduedate" value="[% hardduedate %]" /> >+ </td> >+ <td class="fines"><input name="fine" size="4" value="[% fine %]"/></td> >+ <td class="fines"><input name="chargeperiod" size="2" value="[% chargeperiod %]"/></td> >+ <td class="fines"><input name="firstremind" size="2" value="[% firstremind %]"/> </td> >+ <td class="fines"><input name="finedays" size="3" value="[% finedays %]"/> </td> >+ <td class="issues"><input name="renewalsallowed" size="2" value="[% renewalsallowed %]"/></td> >+ <td class="issues"><input name="renewalperiod" size="2" value="[% renewalperiod %]"/></td> >+ <td class="reserves"><input name="reservesallowed" size="2" value="[% reservesallowed %]"/></td> >+ <td class="reserves"><input name="holdspickupdelay" size="2" value="[% holdspickupdelay %]"/></td> >+ <td class="reserves"> >+ <select name="allowonshelfholds"> >+ <option value=""></option> >+ <option value="1">Yes</option> >+ <option value="0">No</option> >+ </select> >+ </td> >+ <td class="reserves">[% IF ( holdrestricted ) %]<input type="checkbox" name="holdrestricted" checked="checked" value="1"/> [% ELSE %]<input type="checkbox" name="holdrestricted" value="1"/> [% END %]</td> >+ <td class="issues"><input name="rentaldiscount" size="2" /></td> >+ <td><input type="hidden" name="branchcode" value="[% branchcode %]"/><input type="submit" value="Add" class="submit" /></td> >+ </tr> >+ </tfoot> >+ </form> >+ </table> >+ </div><!-- tabs-container --> >+ </div><!-- toptabs --> > </div> > </div> > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt >index 8b68cb9..5594188 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt >@@ -144,6 +144,7 @@ function Dopop(link) { > <input type="hidden" name="diffBranch" value="[% destbranch %]" /> > <input type="hidden" name="exemptfine" value="[% exemptfine %]" /> > <input type="hidden" name="dropboxmode" value="[% dropboxmode %]" /> >+ <input type="hidden" name="reservenumber" value="[% reservenumber %]" /> > </form> > </div> > [% END %] >@@ -180,6 +181,7 @@ function Dopop(link) { > <input type="hidden" name="diffBranch" value="[% destbranch %]" /> > <input type="hidden" name="exemptfine" value="[% exemptfine %]" /> > <input type="hidden" name="dropboxmode" value="[% dropboxmode %]" /> >+ <input type="hidden" name="reservenumber" value="[% reservenumber %]" /> > <input type="hidden" name="barcode" value="0" /> > </form> > </div> >@@ -281,6 +283,7 @@ function Dopop(link) { > <input type="hidden" name="diffBranch" value="[% destbranch %]" /> > <input type="hidden" name="exemptfine" value="[% exemptfine %]" /> > <input type="hidden" name="dropboxmode" value="[% dropboxmode %]" /> >+ <input type="hidden" name="reservenumber" value="[% reservenumber %]" /> > </form> > </div> > [% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt >index cbcfc55..6f960c9 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt >@@ -23,10 +23,14 @@ function check() { > var count_reserv = 0; > var alreadyreserved = 0; > >+ var hold_type=""; > // check if we have checkitem form > if (document.form.checkitem){ > for (i=0;i<document.form.checkitem.length;i++){ > if (document.form.checkitem[i].checked == true) { >+ if (hold_type=="" || hold_type=="single"){ >+ hold_type=document.form.hold_type[i].value; >+ } > count_reserv++ ; > } > } >@@ -49,11 +53,11 @@ function check() { > if (count_reserv == "0"){ > msg += (_("- Please select an item to place a hold") + "\n"); > } >- if (count_reserv >= "2"){ >+ if (count_reserv >= "2" && hold_type != "multiple"){ > msg += (_("- You may only place a hold on one item at a time") + "\n"); > } > >- if (alreadyreserved > "0"){ >+ if (alreadyreserved > "0" && hold_type != "multiple"){ > msg += (_("- This patron had already placed a hold on this item") + "\n" + _("Please cancel the previous hold first") + "\n"); > } > >@@ -395,14 +399,33 @@ function checkMultiHold() { > [% UNLESS ( itemloo.hide ) %] > <tr class="[% itemloo.backgroundcolor %]"> > <td> >- [% IF ( itemloo.available ) %] >- <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" /> >+ [% IF ( itemloo.canholdmultiple ) %] >+ <input type="hidden" name="hold_type" value="multiple" /> >+ [% ELSIF ( itemloo.available ) %] >+ <input type="hidden" name="hold_type" value="single" /> > [% ELSIF ( itemloo.override ) %] >- <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" /> >- <img src="/intranet-tmpl/[% theme %]/img/famfamfam/silk/error.png" alt="Requires override of hold policy" /> >+ <input type="hidden" name="hold_type" value="single" /> >+ [% END %] >+ [% IF ( itemloo.cannothold ) %] >+ <input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" /> >+ <img src="/intranet-tmpl/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" /> > [% ELSE %] >- <input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" /> >- <img src="/intranet-tmpl/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" /> >+ [% IF ( itemloo.canholdmultiple ) %] >+ <input type="checkbox" name="checkitem" value="[% itemnumber %]" /> >+ [% ELSIF ( itemloo.available ) %] >+ [% IF ( itemloo.available ) %] >+ <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" /> >+ [% ELSIF ( itemloo.override ) %] >+ <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" /> >+ <img src="/intranet-tmpl/[% theme %]/img/famfamfam/silk/error.png" alt="Requires override of hold policy" /> >+ [% ELSE %] >+ <input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" /> >+ <img src="/intranet-tmpl/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" /> >+ [% END %] >+ [% ELSE %] >+ <input disabled="disabled" type="radio" name="checkitem" value="[% itemnumber %]" /> >+ <img src="/intranet-tmpl/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" /> >+ [% END %] > [% END %] > </td> > [% IF ( item_level_itypes ) %] >@@ -713,7 +736,7 @@ function checkMultiHold() { > [% END %] > > <td> >- <a title="Cancel Hold" href="request.pl?action=cancel&borrowernumber=[% reserveloo.borrowernumber %]&biblionumber=[% reserveloo.biblionumber %]&date=[% reserveloo.date %]"> >+ <a title="Cancel Hold" href="request.pl?action=cancel&reservenumber=[% reserveloo.reservenumber %]&biblionumber=[% reserveloo.biblionumber %]"> > <img src="/intranet-tmpl/[% theme %]/img/x.png" border="0" alt="Cancel" /> > </a> > </td> >diff --git a/kohaversion.pl b/kohaversion.pl >index 8f9c3f8..a780d8e 100644 >--- a/kohaversion.pl >+++ b/kohaversion.pl >@@ -16,7 +16,7 @@ the kohaversion is divided in 4 parts : > use strict; > > sub kohaversion { >- our $VERSION = '3.05.00.008'; >+ our $VERSION = '3.05.00.XXX'; > # version needs to be set this way > # so that it can be picked up by Makefile.PL > # during install >diff --git a/opac/opac-modrequest.pl b/opac/opac-modrequest.pl >index 7e2f2e5..0003079 100755 >--- a/opac/opac-modrequest.pl >+++ b/opac/opac-modrequest.pl >@@ -42,7 +42,9 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( > ); > > my $biblionumber = $query->param('biblionumber'); >+my $reservenumber = $query->param('reservenumber'); >+ > if ($biblionumber and $borrowernumber) { >- CancelReserve($biblionumber, '', $borrowernumber); >+ CancelReserve( $reservenumber, $biblionumber ); > } > print $query->redirect("/cgi-bin/koha/opac-user.pl#opac-user-holds"); >diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl >index a92a976..7d1ced8 100755 >--- a/opac/opac-reserve.pl >+++ b/opac/opac-reserve.pl >@@ -1,5 +1,7 @@ > #!/usr/bin/perl > >+# Parts Copyright Biblibre 2010 >+ > # This file is part of Koha. > # > # Koha is free software; you can redistribute it and/or modify it under the >@@ -28,11 +30,12 @@ use C4::Output; > use C4::Dates qw/format_date/; > use C4::Context; > use C4::Members; >+use C4::Overdues; > use C4::Branch; # GetBranches > use C4::Debug; >+use C4::Items; > # use Data::Dumper; > >-my $MAXIMUM_NUMBER_OF_RESERVES = C4::Context->preference("maxreserves"); > > my $query = new CGI; > my ( $template, $borrowernumber, $cookie ) = get_template_and_user( >@@ -77,7 +80,9 @@ if ((! $biblionumbers) && (! $query->param('place_reserve'))) { > > # Pass the numbers to the page so they can be fed back > # when the hold is confirmed. TODO: Not necessary? >-$template->param( biblionumbers => $biblionumbers ); >+$template->param( biblionumbers => $biblionumbers, >+ OPACPickupLocation => "" . C4::Context->preference("OPACPickupLocation"), >+ ); > > # Each biblio number is suffixed with '/', e.g. "1/2/3/" > my @biblionumbers = split /\//, $biblionumbers; >@@ -91,6 +96,7 @@ if (($#biblionumbers < 0) && (! $query->param('place_reserve'))) { > my $branch = $query->param('branch') || $borr->{'branchcode'} || C4::Context->userenv->{branch} || '' ; > ($branches->{$branch}) or $branch = ""; # Confirm branch is real > $template->param( branch => $branch ); >+$template->param( branchname => $branches->{$branch}->{branchname} ); > > # make branch selection options... > my $CGIbranchloop = GetBranchesLoop($branch); >@@ -252,7 +258,7 @@ if ( $borr->{lost} && ($borr->{lost} eq 1) ) { > lost => 1 > ); > } >-if ( $borr->{debarred} && ($borr->{debarred} eq 1) ) { >+if ( CheckBorrowerDebarred($borrowernumber) ) { > $noreserves = 1; > $template->param( > message => 1, >@@ -262,11 +268,6 @@ if ( $borr->{debarred} && ($borr->{debarred} eq 1) ) { > > my @reserves = GetReservesFromBorrowernumber( $borrowernumber ); > $template->param( RESERVES => \@reserves ); >-if ( $MAXIMUM_NUMBER_OF_RESERVES && (scalar(@reserves) >= $MAXIMUM_NUMBER_OF_RESERVES) ) { >- $template->param( message => 1 ); >- $noreserves = 1; >- $template->param( too_many_reserves => scalar(@reserves)); >-} > foreach my $res (@reserves) { > foreach my $biblionumber (@biblionumbers) { > if ( $res->{'biblionumber'} == $biblionumber && $res->{'borrowernumber'} == $borrowernumber) { >@@ -342,10 +343,20 @@ foreach my $biblioNum (@biblionumbers) { > } > } > >+ $biblioLoopIter{itemtype} = $biblioData->{itemtype}; >+ $biblioLoopIter{itemTypeDescription} = $itemTypes->{ $biblioData->{itemtype} }{description}; > $biblioLoopIter{itemLoop} = []; > my $numCopiesAvailable = 0; >- foreach my $itemInfo (@{$biblioData->{itemInfos}}) { >- my $itemNum = $itemInfo->{itemnumber}; >+ >+ # $canReserveMultiple is set to "yes" ( >0 ) if multiple items can be reserved >+ # it is set to 0 otherwise. depends on item-level_itypes syspref >+ # and the list of itypes that can be multiple reserved >+ my $canReserveMultiple = 0; >+ unless ( C4::Context->preference("item-level_itypes") ) { >+ $canReserveMultiple = CanHoldMultipleItems( $biblioLoopIter{itemtype} ); >+ } >+ foreach my $itemInfo ( @{ $biblioData->{itemInfos} } ) { >+ my $itemNum = $itemInfo->{itemnumber}; > my $itemLoopIter = {}; > > $itemLoopIter->{itemnumber} = $itemNum; >@@ -359,6 +370,16 @@ foreach my $biblioNum (@biblionumbers) { > $itemLoopIter->{imageurl} = $itemInfo->{imageurl}; > } > >+ # check if the itype is one that can be multiple reserved >+ if ( C4::Context->preference("item-level_itypes") ) { >+ >+ # sum canReserveMultiple : if at least one item can be multiple reserved, then the flag will be >0 >+ # FIXME : there can be complex & strange cases, where some items can be multiple reserved, and some can't >+ # this case is not managed. Note it may be only theoric, and have no real case >+ $itemLoopIter->{canholdmultiple} = CanHoldMultipleItems( $itemInfo->{itype} ); >+ $canReserveMultiple = $canReserveMultiple + CanHoldMultipleItems( $itemInfo->{itype} ); >+ } >+ > # If the holdingbranch is different than the homebranch, we show the > # holdingbranch of the document too. > if ( $itemInfo->{homebranch} ne $itemInfo->{holdingbranch} ) { >@@ -375,9 +396,8 @@ foreach my $biblioNum (@biblionumbers) { > } > > # checking reserve >- my ($reservedate,$reservedfor,$expectedAt) = GetReservesFromItemnumber($itemNum); >- my $ItemBorrowerReserveInfo = GetMemberDetails( $reservedfor, 0); >- >+ my ( $reservedate, $reservedfor, $expectedAt ) = GetReservesFromItemnumber($itemNum); >+ my $ItemBorrowerReserveInfo = GetMemberDetails( $reservedfor, 0 ); > if ( defined $reservedate ) { > $itemLoopIter->{backgroundcolor} = 'reserved'; > $itemLoopIter->{reservedate} = format_date($reservedate); >@@ -385,6 +405,8 @@ foreach my $biblioNum (@biblionumbers) { > $itemLoopIter->{ReservedForSurname} = $ItemBorrowerReserveInfo->{'surname'}; > $itemLoopIter->{ReservedForFirstname} = $ItemBorrowerReserveInfo->{'firstname'}; > $itemLoopIter->{ExpectedAtLibrary} = $expectedAt; >+ $itemLoopIter->{ReservedForThisBorrower} = ( $reservedfor eq $borrowernumber ); >+ warn "ReservedForThisBorrower: " . $itemLoopIter->{ReservedForThisBorrower}; > } > > $itemLoopIter->{notforloan} = $itemInfo->{notforloan}; >@@ -422,17 +444,7 @@ foreach my $biblioNum (@biblionumbers) { > # If there is no loan, return and transfer, we show a checkbox. > $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0; > >- my $branch = C4::Circulation::_GetCircControlBranch($itemLoopIter, $borr); >- >- my $branchitemrule = GetBranchItemRule( $branch, $itemInfo->{'itype'} ); >- my $policy_holdallowed = 1; >- >- if ( $branchitemrule->{'holdallowed'} == 0 || >- ( $branchitemrule->{'holdallowed'} == 1 && $borr->{'branchcode'} ne $itemInfo->{'homebranch'} ) ) { >- $policy_holdallowed = 0; >- } >- >- if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum)) { >+ if (IsAvailableForItemLevelRequest($itemNum) and CanItemBeReserved($borrowernumber,$itemNum)) { > $itemLoopIter->{available} = 1; > $numCopiesAvailable++; > } >@@ -461,13 +473,16 @@ foreach my $biblioNum (@biblionumbers) { > $biblioLoopIter{holdable} = 1; > $anyholdable = 1; > } >- if ($biblioLoopIter{already_reserved}) { >+# $biblioLoopIter{multi} = $canReserveMultiple; >+ if ( $biblioLoopIter{already_reserved} && !$canReserveMultiple ) { > $biblioLoopIter{holdable} = undef; > $anyholdable = undef; > } > if(not CanBookBeReserved($borrowernumber,$biblioNum)){ >+ $template->param( message => 1 ); >+ $noreserves = 1; >+ $template->param( too_many_reserves => scalar(@reserves) ); > $biblioLoopIter{holdable} = undef; >- $anyholdable = undef; > } > > push @$biblioLoop, \%biblioLoopIter; >diff --git a/reserve/modrequest.pl b/reserve/modrequest.pl >index eecd86b..d65cdc3 100755 >--- a/reserve/modrequest.pl >+++ b/reserve/modrequest.pl >@@ -41,12 +41,13 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( > } > ); > >-my @rank=$query->param('rank-request'); >-my @biblionumber=$query->param('biblionumber'); >-my @borrower=$query->param('borrowernumber'); >-my @branch=$query->param('pickup'); >-my @itemnumber=$query->param('itemnumber'); >-my $multi_hold = $query->param('multi_hold'); >+my @rank = $query->param('rank-request'); >+my @reservenumber = $query->param('reservenumber'); >+my @biblionumber = $query->param('biblionumber'); >+my @borrower = $query->param('borrowernumber'); >+my @branch = $query->param('pickup'); >+my @itemnumber = $query->param('itemnumber'); >+my $multi_hold = $query->param('multi_hold'); > my $biblionumbers = $query->param('biblionumbers'); > my $count=@rank; > >@@ -54,9 +55,9 @@ my $CancelBiblioNumber=$query->param('CancelBiblioNumber'); > my $CancelBorrowerNumber=$query->param('CancelBorrowerNumber'); > my $CancelItemnumber=$query->param('CancelItemnumber'); > >-# 2 possibilitys : cancel an item reservation, or modify or cancel the queded list >+# 2 possibilitys : cancel an item reservation, or modify or cancel the queued list > >-# 1) cancel an item reservation by fonction ModReserveCancelAll (in reserves.pm) >+# 1) cancel an item reservation by function ModReserveCancelAll (in reserves.pm) > if ($CancelBorrowerNumber) { > ModReserveCancelAll($CancelItemnumber, $CancelBorrowerNumber); > $biblionumber[0] = $CancelBiblioNumber, >@@ -66,7 +67,7 @@ if ($CancelBorrowerNumber) { > else { > for (my $i=0;$i<$count;$i++){ > undef $itemnumber[$i] unless $itemnumber[$i] ne ''; >- ModReserve($rank[$i],$biblionumber[$i],$borrower[$i],$branch[$i],$itemnumber[$i]); #from C4::Reserves >+ ModReserve($rank[$i],$biblionumber[$i],$borrower[$i],$branch[$i],$itemnumber[$i],$reservenumber[$i]); #from C4::Reserves > } > } > my $from=$query->param('from'); >diff --git a/reserve/placerequest.pl b/reserve/placerequest.pl >index 19d76be..54d30f8 100755 >--- a/reserve/placerequest.pl >+++ b/reserve/placerequest.pl >@@ -3,7 +3,6 @@ > #script to place reserves/requests > #writen 2/1/00 by chris@katipo.oc.nz > >- > # Copyright 2000-2002 Katipo Communications > # > # This file is part of Koha. >@@ -37,34 +36,34 @@ my $input = CGI->new(); > > my ($user, $cookie, $sesion_id, $flags) = checkauth($input, 0, { reserveforothers => 'place_holds' }, 'intranet'); > >-my @bibitems=$input->param('biblioitem'); >+my @bibitems = $input->param('biblioitem'); > # FIXME I think reqbib does not exist anymore, it's used in line 82, to AddReserve of contraint type 'o' > # I bet it's a 2.x feature, reserving a given biblioitem, that is useless in Koha 3.0 > # we can remove this line, the AddReserve of constrainttype 'o', > # and probably remove the reserveconstraint table as well, I never could fill anything in this table. >-my @reqbib=$input->param('reqbib'); >-my $biblionumber=$input->param('biblionumber'); >-my $borrowernumber=$input->param('borrowernumber'); >-my $notes=$input->param('notes'); >-my $branch=$input->param('pickup'); >-my $startdate=$input->param('reserve_date') || ''; >-my @rank=$input->param('rank-request'); >-my $type=$input->param('type'); >-my $title=$input->param('title'); >-my $borrower=GetMember('borrowernumber'=>$borrowernumber); >-my $checkitem=$input->param('checkitem'); >+#my @reqbib = $input->param('reqbib'); >+my $biblionumber = $input->param('biblionumber'); >+my $borrowernumber = $input->param('borrowernumber'); >+my $notes = $input->param('notes'); >+my $branch = $input->param('pickup'); >+my $startdate = $input->param('reserve_date') || ''; >+my @rank = $input->param('rank-request'); >+my $type = $input->param('type'); >+my $title = $input->param('title'); >+my $borrower = GetMember( 'borrowernumber' => $borrowernumber ); >+my @checkitems = $input->param('checkitem'); > my $expirationdate = $input->param('expiration_date'); > >-my $multi_hold = $input->param('multi_hold'); >-my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/'); >-my $bad_bibs = $input->param('bad_bibs'); >+my $multi_hold = $input->param('multi_hold'); >+my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ( $biblionumber . '/' ); >+my $bad_bibs = $input->param('bad_bibs'); > > my %bibinfos = (); > my @biblionumbers = split '/', $biblionumbers; > foreach my $bibnum (@biblionumbers) { > my %bibinfo = (); >- $bibinfo{title} = $input->param("title_$bibnum"); >- $bibinfo{rank} = $input->param("rank_$bibnum"); >+ $bibinfo{title} = $input->param("title_$bibnum"); >+ $bibinfo{rank} = $input->param("rank_$bibnum"); > $bibinfos{$bibnum} = \%bibinfo; > } > >@@ -72,27 +71,27 @@ my $found; > > # if we have an item selectionned, and the pickup branch is the same as the holdingbranch > # of the document, we force the value $rank and $found . >-if ($checkitem ne ''){ >- $rank[0] = '0' unless C4::Context->preference('ReservesNeedReturns'); >- my $item = $checkitem; >- $item = GetItem($item); >- if ( $item->{'holdingbranch'} eq $branch ){ >- $found = 'W' unless C4::Context->preference('ReservesNeedReturns'); >+for my $item (@checkitems){ >+ if ( $item ne '' ) { >+ $rank[0] = '0' unless C4::Context->preference('ReservesNeedReturns'); >+ my $itemdata = GetItem($item); >+ if ( $itemdata->{'holdingbranch'} eq $branch ) { >+ $found = 'W' unless C4::Context->preference('ReservesNeedReturns'); >+ } > } > } > >-if ($type eq 'str8' && $borrower){ >- >- foreach my $biblionumber (keys %bibinfos) { >- my $count=@bibitems; >- @bibitems=sort @bibitems; >- my $i2=1; >+if ( $type eq 'str8' && $borrowernumber ne '' ) { >+ foreach my $biblionumber ( keys %bibinfos ) { >+ my $count = @bibitems; >+ @bibitems = sort @bibitems; >+ my $i2 = 1; > my @realbi; >- $realbi[0]=$bibitems[0]; >- for (my $i=1;$i<$count;$i++) { >- my $i3=$i2-1; >- if ($realbi[$i3] ne $bibitems[$i]) { >- $realbi[$i2]=$bibitems[$i]; >+ $realbi[0] = $bibitems[0]; >+ for ( my $i = 1 ; $i < $count ; $i++ ) { >+ my $i3 = $i2 - 1; >+ if ( $realbi[$i3] ne $bibitems[$i] ) { >+ $realbi[$i2] = $bibitems[$i]; > $i2++; > } > } >@@ -100,18 +99,19 @@ if ($type eq 'str8' && $borrower){ > > if ($multi_hold) { > my $bibinfo = $bibinfos{$biblionumber}; >+ > AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,'a',[$biblionumber], >- $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found); >+ $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitems[0],$found); > } else { >- if ($input->param('request') eq 'any'){ >+ if ( lc($input->param('request')) eq 'any' ) { > # place a request on 1st available >- AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem,$found); >- } elsif ($reqbib[0] ne ''){ >- # FIXME : elsif probably never reached, (see top of the script) >- # place a request on a given item >- AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,'o',\@reqbib,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem, $found); >+ AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,undef,$found); > } else { >- AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem, $found); >+ for my $item (@checkitems){ >+ AddReserve( $branch, $borrower->{'borrowernumber'}, >+ $biblionumber, 'a', \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title, $item, $found ); >+ $rank[0]++; >+ } > } > } > } >@@ -124,10 +124,8 @@ if ($type eq 'str8' && $borrower){ > } else { > print $input->redirect("request.pl?biblionumber=$biblionumber"); > } >-} elsif ($borrower eq ''){ >- print $input->header(); >- print "Invalid borrower number please try again"; >-# Not sure that Dump() does HTML escaping. Use firebug or something to trace >-# instead. >-# print $input->Dump; >+} elsif ( $borrowernumber eq '' ) { >+ print $input->header(); >+ print "Invalid card number please try again"; >+ print $input->Dump; > } >diff --git a/reserve/renewscript.pl b/reserve/renewscript.pl >index f4c7fb4..a0304b8 100755 >--- a/reserve/renewscript.pl >+++ b/reserve/renewscript.pl >@@ -28,6 +28,13 @@ use C4::Circulation; > use C4::Auth; > use URI::Escape; > use C4::Dates qw/format_date_in_iso/; >+use C4::Context; >+use C4::Overdues; #CheckBorrowerDebarred >+use C4::Members; #GetMemberDetail >+use C4::Items; >+use C4::Branch; >+use JSON; >+use C4::Reserves; > my $input = new CGI; > > #Set Up User_env >@@ -48,6 +55,7 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( > # find items to renew, all items or a selection of items > # > >+my $branches = GetBranches(); > my @data; > if ($input->param('renew_all')) { > @data = $input->param('all_items[]'); >@@ -80,12 +88,12 @@ my $override_limit = $input->param("override_limit") || 0; > my $failedrenews = q{}; > foreach my $itemno (@data) { > # check status before renewing issue >- my ($renewokay,$error) = CanBookBeRenewed($borrowernumber,$itemno,$override_limit); >- if ($renewokay){ >+ my ($renewokay,$error) = CanBookBeRenewed($borrowernumber,$itemno); >+ if ($renewokay||$override_limit){ > AddRenewal($borrowernumber,$itemno,$branch,$datedue); > } > else { >- $failedrenews.="&failedrenew=$itemno"; >+ $failedrenews.="&failedrenew=$itemno&renewerror=".encode_json($error); > } > } > my $failedreturn = q{}; >@@ -93,7 +101,48 @@ foreach my $barcode (@barcodes) { > # check status before renewing issue > my ( $returned, $messages, $issueinformation, $borrower ) = > AddReturn($barcode, $branch, $exemptfine); >- $failedreturn.="&failedreturn=$barcode" unless ($returned); >+ my $itemnumber=GetItemnumberFromBarcode($barcode); >+ if ($returned){ >+ if (my ($reservetype,$reserve)=C4::Reserves::CheckReserves(undef,$barcode)){ >+ if ($reservetype eq "Waiting" || $reservetype eq "Reserved"){ >+ my $transfer=C4::Context->userenv->{branch} ne $reserve->{branchcode}; >+ ModReserveAffect( $itemnumber, $reserve->{borrowernumber}, $transfer, $reserve->{"reservenumber"} ); >+ my ( $message_reserve, $nextreservinfo ) = GetOtherReserves($itemnumber); >+ >+ my ($borr) = GetMemberDetails( $nextreservinfo, 0 ); >+ $messages->{reservesdata}={ >+ found => 1, >+ currentbranch => $branches->{C4::Context->userenv->{branch}}->{'branchname'}, >+ destbranchname => $branches->{ $reserve->{'branchcode'} }->{'branchname'}, >+ name => $borr->{'surname'} . ", " . $borr->{'title'} . " " . $borr->{'firstname'}, >+ borfirstname => $borr->{'firstname'}, >+ borsurname => $borr->{'surname'}, >+ bortitle => $borr->{'title'}, >+ borphone => $borr->{'phone'}, >+ boremail => $borr->{'email'}, >+ boraddress => $borr->{'address'}, >+ boraddress2 => $borr->{'address2'}, >+ borcity => $borr->{'city'}, >+ borzip => $borr->{'zipcode'}, >+ borcnum => $borr->{'cardnumber'}, >+ debarred => CheckBorrowerDebarred($borr->{borrowernumber}), >+ gonenoaddress => $borr->{'gonenoaddress'}, >+ barcode => $barcode, >+ transfertodo => $transfer, >+ destbranch => $reserve->{'branchcode'}, >+ reservenumber => $reserve->{'reservenumber'}, >+ borrowernumber => $reserve->{'borrowernumber'}, >+ itemnumber => $reserve->{'itemnumber'}, >+ biblionumber => $reserve->{'biblionumber'}, >+ reservenotes => $reserve->{'reservenotes'}, >+ }; >+ >+ } >+ } >+ } >+ if (!$returned ||$messages){ >+ $failedreturn.="&failedreturn=$barcode&returnerror=".encode_json($messages); >+ } > } > > # >diff --git a/reserve/request.pl b/reserve/request.pl >index 79ff6b5..7406928 100755 >--- a/reserve/request.pl >+++ b/reserve/request.pl >@@ -38,6 +38,7 @@ use C4::Biblio; > use C4::Items; > use C4::Koha; > use C4::Circulation; >+use C4::IssuingRules; > use C4::Dates qw/format_date/; > use C4::Members; > use C4::Search; # enabled_staff_search_views >@@ -92,7 +93,7 @@ my $warnings; > my $messages; > > my $date = C4::Dates->today('iso'); >-my $action = $input->param('action'); >+my $action = $input->param('action') || ''; > > if ( $action eq 'move' ) { > my $where = $input->param('where'); >@@ -102,9 +103,9 @@ if ( $action eq 'move' ) { > AlterPriority( $where, $borrowernumber, $biblionumber ); > > } elsif ( $action eq 'cancel' ) { >- my $borrowernumber = $input->param('borrowernumber'); >+ my $reservenumber = $input->param('reservenumber'); > my $biblionumber = $input->param('biblionumber'); >- CancelReserve( $biblionumber, '', $borrowernumber ); >+ CancelReserve( $reservenumber, $biblionumber ); > } elsif ( $action eq 'setLowestPriority' ) { > my $borrowernumber = $input->param('borrowernumber'); > my $biblionumber = $input->param('biblionumber'); >@@ -138,19 +139,21 @@ if ($borrowernumber_hold) { > # we check the reserves of the borrower, and if he can reserv a document > # FIXME At this time we have a simple count of reservs, but, later, we could improve the infos "title" ... > >- my $number_reserves = >- GetReserveCount( $borrowerinfo->{'borrowernumber'} ); >+ my $number_reserves = GetReserveCount( $borrowerinfo->{'borrowernumber'} ); > >- if ( C4::Context->preference('maxreserves') && ($number_reserves >= C4::Context->preference('maxreserves')) ) { >- $warnings = 1; >+ if ( not CanBookBeReserved( $borrowerinfo->{borrowernumber}, $input->param('biblionumber') ) ) { >+ $warnings = 1; > $maxreserves = 1; > } >+ if ( not CanBookBeReserved( $borrowerinfo->{borrowernumber}, $input->param('biblionumber') ) ) { >+ $warnings = 1; >+ } > > # we check the date expiry of the borrower (only if there is an expiry date, otherwise, set to 1 (warn) > my $expiry_date = $borrowerinfo->{dateexpiry}; > my $expiry = 0; # flag set if patron account has expired > if ($expiry_date and $expiry_date ne '0000-00-00' and >- Date_to_Days(split /-/,$date) > Date_to_Days(split /-/,$expiry_date)) { >+ Date_to_Days(split (/-/,$date)) > Date_to_Days(split (/-/,$expiry_date))) { > $messages = $expiry = 1; > } > >@@ -224,7 +227,7 @@ my $borrowerinfo = GetMemberDetails( $borrowernumber_hold ); > my @biblionumbers = (); > my $biblionumbers = $input->param('biblionumbers'); > if ($multihold) { >- @biblionumbers = split '/', $biblionumbers; >+ @biblionumbers = split ('/', $biblionumbers); > } else { > push @biblionumbers, $input->param('biblionumber'); > } >@@ -236,7 +239,6 @@ foreach my $biblionumber (@biblionumbers) { > my $maxreserves; > > my $dat = GetBiblioData($biblionumber); >- > unless ( CanBookBeReserved($borrowerinfo->{borrowernumber}, $biblionumber) ) { > $warnings = 1; > $maxreserves = 1; >@@ -250,8 +252,11 @@ foreach my $biblionumber (@biblionumbers) { > if ( defined $res->{found} && $res->{found} eq 'W' ) { > $count--; > } >- >- if ( defined $borrowerinfo && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) ) { >+ if ($res->{itemnumber} && not $res->{itemtype}){ >+ $res->{itemtype}=GetItem($res->{itemnumber},$res->{biblionumber})->{itype}; >+ } >+ next if CanHoldMultipleItems($res->{itemtype}); >+ if ( defined $borrowerinfo && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) && !CanHoldMultipleItems($res->{itemtype}) ) { > $warnings = 1; > $alreadyreserved = 1; > $biblioloopiter{warn} = 1; >@@ -419,24 +424,16 @@ foreach my $biblionumber (@biblionumbers) { > } > } > } >+ my $branchitemrule = GetIssuingRule( $borrowerinfo->{categorycode}, $item->{'itype'}, GetReservesControlBranch( $borrowerinfo, $item ) ); >+ my $policy_holdrestricted = $branchitemrule->{'reservesallowed'}; >+ # prevent item to be reserved if holdrestricted & branch not the librarian branch & syspref to override not set >+ $item->{'holdrestricted'} = $branchitemrule->{'holdrestricted'}; > >- my $branch = C4::Circulation::_GetCircControlBranch($item, $borrowerinfo); >- >- my $branchitemrule = GetBranchItemRule( $branch, $item->{'itype'} ); >- my $policy_holdallowed = 1; >- >- $item->{'holdallowed'} = $branchitemrule->{'holdallowed'}; >- >- if ( $branchitemrule->{'holdallowed'} == 0 || >- ( $branchitemrule->{'holdallowed'} == 1 && >- $borrowerinfo->{'branchcode'} ne $item->{'homebranch'} ) ) { >- $policy_holdallowed = 0; >- } >- >- if (IsAvailableForItemLevelRequest($itemnumber) and >- not $item->{cantreserve} and >- CanItemBeReserved($borrowerinfo->{borrowernumber}, $itemnumber) ) { >- if ( $policy_holdallowed ) { >+ if (IsAvailableForItemLevelRequest($itemnumber) and not $item->{cantreserve} and CanItemBeReserved($borrowerinfo->{borrowernumber}, $itemnumber) ) { >+ if ( not $policy_holdrestricted and C4::Context->preference( 'AllowHoldPolicyOverride' ) ) { >+ $item->{override} = 1; >+ $num_override++; >+ } elsif ( $policy_holdrestricted ) { > $item->{available} = 1; > $num_available++; > } >@@ -458,10 +455,16 @@ foreach my $biblionumber (@biblionumbers) { > while (my $wait_hashref = $sth2->fetchrow_hashref) { > $item->{waitingdate} = format_date($wait_hashref->{waitingdate}); > } >+ >+ if ( BorrowerHasReserve( $borrowerinfo->{'borrowernumber'}, '', $itemnumber ) ) { >+ $item->{available} = 0; >+ $item->{override} = 0; >+ } >+ $item->{canholdmultiple}=CanHoldMultipleItems($item->{itype}) && $item->{available}; >+ > push @{ $biblioitem->{itemloop} }, $item; > } >- >- if ( $num_override == scalar( @{ $biblioitem->{itemloop} } ) ) { # That is, if all items require an override >+ if ( $num_override > 0) { > $template->param( override_required => 1 ); > } elsif ( $num_available == 0 ) { > $template->param( none_available => 1 ); >@@ -483,6 +486,9 @@ foreach my $biblionumber (@biblionumbers) { > $a_found cmp $b_found; > } @$reserves ) { > my %reserve; >+ >+ $reserve{'reservenumber'} = $res->{'reservenumber'}; >+ > my @optionloop; > for ( my $i = 1 ; $i <= $totalcount ; $i++ ) { > push( >diff --git a/t/db_dependent/lib/KohaTest/Circulation.pm b/t/db_dependent/lib/KohaTest/Circulation.pm >index 7d5e69d..8236bcb 100644 >--- a/t/db_dependent/lib/KohaTest/Circulation.pm >+++ b/t/db_dependent/lib/KohaTest/Circulation.pm >@@ -20,8 +20,6 @@ sub methods : Test( 1 ) { > CanBookBeIssued > AddIssue > GetLoanLength >- GetIssuingRule >- GetBranchBorrowerCircRule > AddReturn > MarkIssueReturned > _FixOverduesOnReturn >diff --git a/t/db_dependent/lib/KohaTest/Members/GetMemberDetails.pm b/t/db_dependent/lib/KohaTest/Members/GetMemberDetails.pm >index e93742b..d75c60e 100644 >--- a/t/db_dependent/lib/KohaTest/Members/GetMemberDetails.pm >+++ b/t/db_dependent/lib/KohaTest/Members/GetMemberDetails.pm >@@ -50,12 +50,10 @@ sub startup_create_detailed_borrower : Test( startup => 2 ) { > $description = 'Test account'; > $type = 'M'; > $amount = 5.00; >- $user = ''; >+ $note = ''; > > my $acct_added = >- C4::Accounts::manualinvoice( $borrowernumber, undef, $description, $type, $amount, >- $user ); >- >+ C4::Accounts::manualinvoice( $borrowernumber, undef, $description, $type, $amount, $note ); > ok( $acct_added == 0, 'added account for borrower' ); > > $self->{amountoutstanding} = $amount; >diff --git a/t/db_dependent/lib/KohaTest/Overdues.pm b/t/db_dependent/lib/KohaTest/Overdues.pm >index 949c670..02b907e 100644 >--- a/t/db_dependent/lib/KohaTest/Overdues.pm >+++ b/t/db_dependent/lib/KohaTest/Overdues.pm >@@ -23,7 +23,6 @@ sub methods : Test( 1 ) { > BorType > ReplacementCost > GetFine >- GetIssuingRules > ReplacementCost2 > GetNextIdNotify > NumberNotifyId >-- >1.7.4.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 5872
:
3310
|
3329
|
3394
|
3432
|
4741
|
4795
|
4811
| 4854