From 1301705150193531eeabcfaa7a12a4faa588d3b8 Mon Sep 17 00:00:00 2001 From: Jesse Weaver Date: Thu, 15 Jan 2009 15:26:13 -0600 Subject: [PATCH] Add holds policies This adds holds policy creation support to smart rules and read support to C4/Circulation.pm, and the two reservation pages. It also adds a system preference, AllowHoldPolicyOverride, to control whether the staff can override these policies. Signed-off-by: Galen Charlton Signed-off-by: Daniel Sweeney Signed-off-by: Galen Charlton --- C4/Circulation.pm | 65 +++++ C4/Reserves.pm | 1 + admin/smart-rules.pl | 222 ++++++++++++++++-- admin/systempreferences.pl | 1 + .../prog/en/css/staff-global.css | 9 +- .../prog/en/modules/admin/smart-rules.tmpl | 125 +++++++++- .../prog/en/modules/reserve/request.tmpl | 61 ++++- .../prog/img/famfamfam/silk/cross.png | Bin 0 -> 655 bytes .../prog/img/famfamfam/silk/error.png | Bin 0 -> 666 bytes .../prog/en/modules/opac-reserve.tmpl | 17 +- opac/opac-reserve.pl | 17 +- reserve/request.pl | 84 ++++--- 12 files changed, 543 insertions(+), 59 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/img/famfamfam/silk/cross.png create mode 100644 koha-tmpl/intranet-tmpl/prog/img/famfamfam/silk/error.png diff --git a/C4/Circulation.pm b/C4/Circulation.pm index c79ca05df4..372b5e8e7f 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -72,6 +72,7 @@ BEGIN { &GetIssuingCharges &GetIssuingRule &GetBranchBorrowerCircRule + &GetBranchItemRule &GetBiblioIssues &AnonymiseIssueHistory ); @@ -1226,6 +1227,70 @@ sub GetBranchBorrowerCircRule { }; } +=head2 GetBranchItemRule + +=over 4 + +my $branch_item_rule = GetBranchItemRule($branchcode, $itemtype); + +=back + +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, + }; +} + =head2 AddReturn ($doreturn, $messages, $iteminformation, $borrower) = diff --git a/C4/Reserves.pm b/C4/Reserves.pm index 58086fa1f3..05015289e3 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -1175,6 +1175,7 @@ sub IsAvailableForItemLevelRequest { $item->{wthdrawn} or $notforloan_per_itemtype; + if (C4::Context->preference('AllowOnShelfHolds')) { return $available_per_item; } else { diff --git a/admin/smart-rules.pl b/admin/smart-rules.pl index 558bd42813..ce7dc9dbd3 100755 --- a/admin/smart-rules.pl +++ b/admin/smart-rules.pl @@ -74,6 +74,28 @@ elsif ($op eq 'delete-branch-cat') { $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=?"); @@ -100,6 +122,50 @@ elsif ($op eq 'add') { $sth_insert->execute($br,$bor,$cat,$maxissueqty,$issuelength,$fine,$firstremind,$chargeperiod); } } +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'); @@ -180,6 +246,86 @@ elsif ($op eq "add-branch-cat") { } } } +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 $branches = GetBranches(); my @branchloop; @@ -258,36 +404,74 @@ while (my $row = $sth_branch_cat->fetchrow_hashref) { } my @sorted_branch_cat_rules = sort { $a->{'humancategorycode'} cmp $b->{'humancategorycode'} } @branch_cat_rules; -my $sth_branch_default; +# 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}); +} + +my @sorted_row_loop = sort by_category_and_itemtype @row_loop; + +my $sth_branch_item; if ($branch eq "*") { - # add global default - $sth_branch_default = $dbh->prepare("SELECT maxissueqty - FROM default_circ_rules"); - $sth_branch_default->execute(); + $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 { - # add default for branch - $sth_branch_default = $dbh->prepare("SELECT maxissueqty - FROM default_branch_circ_rules - WHERE branchcode = ?"); - $sth_branch_default->execute($branch); + $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); } -if (my ($default_maxissueqty) = $sth_branch_default->fetchrow_array()) { - push @sorted_branch_cat_rules, { - default_humancategorycode => 1, - categorycode => '*', - maxissueqty => $default_maxissueqty, - }; +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 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}); +# 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); +} + +my $defaults = $sth_defaults->fetchrow_hashref; + +if ($defaults) { + $template->param(default_holdallowed_none => 1) if($defaults->{holdallowed} == 0); + $template->param(default_holdallowed_same => 1) if($defaults->{holdallowed} == 1); + $template->param(default_holdallowed_any => 1) if($defaults->{holdallowed} == 2); + $template->param(default_maxissueqty => $defaults->{maxissueqty}); +} + +$template->param(default_rules => ($defaults ? 1 : 0)); + $template->param(categoryloop => \@category_loop, itemtypeloop => \@itemtypes, rules => \@sorted_row_loop, diff --git a/admin/systempreferences.pl b/admin/systempreferences.pl index b71d8d2438..d323a51776 100755 --- a/admin/systempreferences.pl +++ b/admin/systempreferences.pl @@ -156,6 +156,7 @@ $tabsysprefs{StaticHoldsQueueWeight} = "Circulation"; $tabsysprefs{AllowOnShelfHolds} = "Circulation"; $tabsysprefs{AllowHoldsOnDamagedItems} = "Circulation"; $tabsysprefs{UseBranchTransferLimits} = "Circulation"; +$tabsysprefs{AllowHoldPolicyOverride} = "Circulation"; # Staff Client $tabsysprefs{TemplateEncoding} = "StaffClient"; 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 c9f302d208..18199c917b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css @@ -86,7 +86,7 @@ strong em { font-style : italic; } -em { +em, cite { font-style : italic; } @@ -1083,7 +1083,7 @@ td input.approve { .dialog input:hover { background-color : #ffc; } - + div.alert { background : #FFC url(../../img/alert-bg.gif) repeat-x left 0; text-align : center; @@ -1412,6 +1412,11 @@ ul li input.submit { padding : 2px; } +input.warning { + background : #FFF url(../../img/famfamfam/silk/error.png) no-repeat 4px center; + padding : 0.25em 0.25em 0.25em 25px; +} + .searchhighlightblob { font-size:75%; font-style : italic; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl index 643cb97bb1..f24961ed95 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tmpl @@ -126,6 +126,57 @@ $(document).ready(function() { +
+

Defaults for this branch

+

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.

+
+
+
+ + "/> + + + + + + + + + + + + + + +
 Total Current Checkouts AllowedHold Policy 
Defaults (not set)"/> + + + ">Unset +
+
+

For this library, you can specify the maximum number of loans that @@ -167,7 +218,6 @@ $(document).ready(function() { + "/> + + + + + + + + + + + + + + + + + + +
Item TypeHold Policy 
+ Default + + + + + From Any Library + + From Home Library + + No Holds Allowed + + + &branch=">Delete +
+ + + +
+ +

diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tmpl index fcce2b0330..3842667f5a 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tmpl +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tmpl @@ -2,7 +2,16 @@ Koha › Circulation › Holds › Place a hold on <!-- TMPL_VAR NAME="title" escape="html" -->