[Koha-patches] [PATCH] Add holds policies

Daniel Sweeney daniel.sweeney at liblime.com
Thu Jan 15 22:26:13 CET 2009


From: Jesse Weaver <jesse.weaver at liblime.com>

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 <galen.charlton at liblime.com>
Signed-off-by: Daniel Sweeney <daniel.sweeney at liblime.com>
---
 C4/Circulation.pm                                  |   65 ++++++
 C4/Reserves.pm                                     |    1 +
 admin/smart-rules.pl                               |  222 ++++++++++++++++++--
 admin/systempreferences.pl                         |    1 +
 .../intranet-tmpl/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
 .../opac-tmpl/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 c79ca05..372b5e8 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 58086fa..0501528 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 558bd42..ce7dc9d 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 b71d8d2..d323a51 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 c9f302d..18199c9 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 643cb97..f24961e 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() {
             </table>
         </form>
     </div>
+    <div class="help">
+        <h4>Defaults for this branch</h4>
+        <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>
+    </div>
+    <div>
+        <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="<!-- TMPL_VAR NAME="branch" -->"/>
+            <table>
+                <tr>
+                    <th>&nbsp;</th>
+                    <th>Total Current Checkouts Allowed</th>
+                    <th>Hold Policy</th>
+                    <th>&nbsp;</th>
+                </tr>
+                <tr>
+                    <td><em>Defaults<!-- TMPL_UNLESS NAME="default_rules" --> (not set)<!-- /TMPL_IF --></em></td>
+                    <td><input name="maxissueqty" size="3" value="<!-- TMPL_VAR NAME="default_maxissueqty" -->"/></td>
+                    <td>
+                        <select name="holdallowed">
+                            <!-- TMPL_IF NAME="default_holdallowed_any" -->
+                            <option value="2" selected="selected">
+                            <!-- TMPL_ELSE -->
+                            <option value="2">
+                            <!-- /TMPL_IF -->
+                                From Any Library
+                            </option>
+                            <!-- TMPL_IF NAME="default_holdallowed_same" -->
+                            <option value="1" selected="selected">
+                            <!-- TMPL_ELSE -->
+                            <option value="1">
+                            <!-- /TMPL_IF -->
+                                From Home Library
+                            </option>
+                            <!-- TMPL_IF NAME="default_holdallowed_none" -->
+                            <option value="0" selected="selected">
+                            <!-- TMPL_ELSE -->
+                            <option value="0">
+                            <!-- /TMPL_IF -->
+                                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&amp;categorycode=*&amp;branch=<!-- TMPL_VAR NAME="branch" -->">Unset</a>
+                    </td>
+                </tr>
+            </table>
+        </form>
+    </div>
     <!-- TMPL_IF NAME="show_branch_cat_rule_form" -->
     <div class="help">
         <p>For this library, you can specify the maximum number of loans that 
@@ -167,7 +218,6 @@ $(document).ready(function() {
                 <tr>
                     <td>
                         <select name="categorycode">
-                            <option value="*">Default</option>
                         <!-- TMPL_LOOP NAME="categoryloop" -->
                             <option value="<!-- TMPL_VAR NAME="categorycode" -->"><!-- TMPL_VAR NAME="description" --></option>
                         <!-- /TMPL_LOOP -->
@@ -180,6 +230,79 @@ $(document).ready(function() {
         </form>
     </div>
     <!-- /TMPL_IF -->
+    <div class="help">
+        <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 branch, <em>not</em> the branch that
+            the reserving staff member is from.
+        </p>
+    </div>
+    <div>
+        <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="<!-- TMPL_VAR NAME="branch" -->"/>
+            <table>
+                <tr>
+                    <th>Item Type</th>
+                    <th>Hold Policy</th>
+                    <th>&nbsp;</th>
+                </tr>
+                <!-- TMPL_LOOP NAME="branch_item_rule_loop" -->
+                    <tr>
+                        <td><!-- TMPL_IF NAME="default_humanitemtype" -->
+                                <em>Default</em>
+                            <!-- TMPL_ELSE -->
+                                <!-- TMPL_VAR NAME="humanitemtype" -->
+                            <!-- /TMPL_IF -->
+                        </td>
+                        <td><!-- TMPL_IF NAME="holdallowed_any" -->
+                                From Any Library
+                            <!-- TMPL_ELSIF NAME="holdallowed_same" -->
+                                From Home Library
+                            <!-- TMPL_ELSE -->
+                                No Holds Allowed
+                            <!-- /TMPL_IF -->
+                        </td>
+                        <td>
+                            <a class="button" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete-branch-item&amp;itemtype=<!-- TMPL_VAR NAME="itemtype" -->&amp;branch=<!-- TMPL_VAR NAME="branch" -->">Delete</a>
+                        </td>
+                    </tr>
+                <!-- /TMPL_LOOP -->
+                <tr>
+                    <td>
+                        <select name="itemtype">
+                        <!-- TMPL_LOOP NAME="itemtypeloop" -->
+                            <option value="<!-- TMPL_VAR NAME="itemtype" -->"><!-- TMPL_VAR NAME="description" --></option>
+                        <!-- /TMPL_LOOP -->
+                        </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>
+    </div>
 </div>
 
 </div>
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 fcce2b0..3842667 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 @@
 <title>Koha &rsaquo; Circulation &rsaquo; Holds &rsaquo; Place a hold on <!-- TMPL_VAR NAME="title" escape="html" --></title>
 <!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
 <script type="text/javascript">
-// <![CDATA[ 
+    // <![CDATA[
+var patron_homebranch = "<!-- TMPL_VAR NAME="borrower_branchname" ESCAPE="JS" -->";
+var override_items = {<!-- TMPL_LOOP NAME="bibitemloop" --><!-- TMPL_LOOP NAME="itemloop" --><!-- TMPL_IF NAME="override" -->
+    <!-- TMPL_VAR NAME="itemnumber" -->: {
+        homebranch: "<!-- TMPL_VAR NAME="homebranchname" ESCAPE="JS" -->",
+        holdallowed: <!-- TMPL_VAR NAME="holdallowed" -->
+    },
+<!-- /TMPL_IF --><!-- /TMPL_LOOP --><!-- /TMPL_LOOP -->
+};
+
 function check() {
 	var msg = "";
 	var count_reserv = 0;
@@ -50,6 +59,22 @@ if (alreadyreserved > "0"){
 }
 
  $(document).ready(function() {
+    $("input.needsoverride").click(function() { // This must be before the radio button/checkbox switch logic
+        var itemnumber = this.value;
+        var msg;
+
+        switch (override_items[itemnumber].holdallowed) {
+            case 0: msg = _( 'This item normally cannot be put on hold.' ); break;
+            case 1: msg = _( 'This item normally cannot be put on hold except for patrons from ' ) + override_items[itemnumber].homebranch + '.'; break;
+        }
+
+        msg += "\n\n" + _( 'Place hold on this item?' );
+
+        return confirm(msg);
+    });
+    $("input.warning").click(function() {
+        return confirm( _( 'None of these items can normally be put on hold for this patron.' ) + "\n\n" + _( 'Place hold?' ) );
+    });
  	$("#requestany").click(function() {
 		if(this.checked){
 		$("input[@name=checkitem]").each(function() {
@@ -135,6 +160,11 @@ if (alreadyreserved > "0"){
     <!-- TMPL_IF NAME="alreadyreserved" -->
      <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=<!-- TMPL_VAR NAME="borrowernumber" -->"><!-- TMPL_VAR NAME="borrowerfirstname" --> <!-- TMPL_VAR NAME="borrowersurname" --></a> <strong>already has a hold</strong> on this item </li>
     <!-- /TMPL_IF -->
+
+    <!-- TMPL_IF NAME="none_available" -->
+    <li> <strong>No copies are available</strong> to be placed on hold</li>
+    <!-- /TMPL_IF -->
+
 	</ul></div>
 <!-- /TMPL_IF -->	
 
@@ -147,6 +177,7 @@ if (alreadyreserved > "0"){
 <!-- TMPL_IF NAME="diffbranch" -->
     <li> <strong>Pickup library is different</strong> than <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=<!-- TMPL_VAR NAME="borrowernumber" -->"><!-- TMPL_VAR NAME="borrowerfirstname" --> <!-- TMPL_VAR NAME="borrowersurname" --></a>'s home library (<!-- TMPL_VAR NAME="borrower_branchname" --> / <!-- TMPL_VAR NAME="borrower_branchcode" --> )</li>
     <!-- /TMPL_IF -->
+
 </ul></div>
 <!-- /TMPL_IF -->
 
@@ -191,9 +222,15 @@ if (alreadyreserved > "0"){
         <input type="hidden" name="alreadyreserved" value="<!-- TMPL_VAR NAME="alreadyreserved" -->" />
 </ol>
         <fieldset class="action">
-        <!-- TMPL_IF NAME="cardnumber"-->
-        <input type="submit" value="Place Hold" />
-        <!-- /TMPL_IF -->
+            <!-- TMPL_IF NAME="cardnumber"-->
+                <!-- TMPL_IF NAME="override_required" -->
+                    <input type="submit" class="warning" value="Place Hold" />
+                <!-- TMPL_ELSIF NAME="none_available" -->
+                    <input type="submit" disabled="disabled" value="Place Hold" />
+                <!-- TMPL_ELSE -->
+                    <input type="submit" value="Place Hold" />
+                <!-- /TMPL_IF -->
+            <!-- /TMPL_IF -->
         </fieldset>
         <!-- TMPL_LOOP name="bibitemloop" -->
 <ol>
@@ -221,8 +258,12 @@ if (alreadyreserved > "0"){
             <td>
             <!-- TMPL_IF NAME="available" -->
                 <input type="radio" name="checkitem" value="<!-- TMPL_VAR NAME="itemnumber" -->" />
+            <!-- TMPL_ELSIF NAME="override" -->
+                <input type="radio" name="checkitem" class="needsoverride" value="<!-- TMPL_VAR NAME="itemnumber" -->" />
+                <img src="/intranet-tmpl/<!-- TMPL_VAR NAME="theme" -->/img/famfamfam/silk/error.png" alt="Requires override of hold policy" />
             <!-- TMPL_ELSE -->
                 <input disabled="disabled" type="radio" name="checkitem" value="<!-- TMPL_VAR NAME="itemnumber" -->" /> 
+                <img src="/intranet-tmpl/<!-- TMPL_VAR NAME="theme" -->/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" />
             <!-- /TMPL_IF -->
             </td>
             <!-- TMPL_IF NAME="item-level_itypes" -->
@@ -281,9 +322,15 @@ if (alreadyreserved > "0"){
     <!-- /TMPL_LOOP --> <!-- bibitemloop -->
     
     <fieldset class="action">
-    <!-- TMPL_IF NAME="cardnumber"-->
-        <input type="submit" value="Place Hold" />
-    <!-- /TMPL_IF -->
+        <!-- TMPL_IF NAME="cardnumber"-->
+            <!-- TMPL_IF NAME="override_required" -->
+                <input type="submit" class="warning" value="Place Hold" />
+            <!-- TMPL_ELSIF NAME="none_available" -->
+                <input type="submit" disabled="disabled" value="Place Hold" />
+            <!-- TMPL_ELSE -->
+                <input type="submit" value="Place Hold" />
+            <!-- /TMPL_IF -->
+        <!-- /TMPL_IF -->
     </fieldset>
 	</fieldset>
     </form>
diff --git a/koha-tmpl/intranet-tmpl/prog/img/famfamfam/silk/cross.png b/koha-tmpl/intranet-tmpl/prog/img/famfamfam/silk/cross.png
new file mode 100644
index 0000000000000000000000000000000000000000..1514d51a3cf1b67e1c5b9ada36f1fd474e2d214a
GIT binary patch
literal 655
zcmV;A0&x9_P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!AxT6*R5;6(
zlj}|sQ51!ViOGr2pjL^7$dQ1=8$w$KD6I_$CeZtZUZjAurR}6mp*HFdO?(oc3q-}<
zu+o`!I at 9H@m`W`r;m1zSWX}5bthM(H02sJT)z?DT&OUcv5HmU|-dF4oa at p;T*KAHh
zFQ6^X?4p&<LW?JRAa!4W*8^cRHn#BV<;v&k;?li;fWKno-=RgNB}6ngw>uEoyT++I
zn$b9r%cFfhHe2K68Pk<hKq}2z2~lh9z5zadf|kv-0cZq5T=w`mb%(MY1L(c%58!zI
zxmp(Hu69|_VhN_cj;WfM0p5Rrno6S{41>Bu*@^<$y+7xQ$wJ~;c5aBx$R=xq*41Wo
zhwQus_VOgm0hughj}MhOvs#{>Vg09Y8WxjWUJY5Y<Msg#Hz1}_nVr{4MuGi*zXu>W
zJ?&8eG!59Cz=|E%Ns at 013KLWOLV)CObIIj_5{>{#k%TEAMs_GbdDV`x-iYsG<NRDe
z&F<oo(+wrG4$v4ShYJ*zTAVUyCyUEKWCOJq%P2g2jKiT}-UXn|;?x~V at D+7UK%!5l
zu+-wDffb%iu%q!uYSm;0f+3t(tT-AO#lfvX-T}~N2{oGoN+1Mj)d31iEk4tC0{b>H
z#=Z{USAQA>NY(}X7=3{K8#<xO0&SL1U06cNFs)HoJ!v1_9b#Vw at 2F?RJt3C#MxpR@
plJ)zU4s^HK{`H%}g=4&I{RVQoq{rLsz7zle002ovPDHLkV1j2<C=vhw

literal 0
HcmV?d00001

diff --git a/koha-tmpl/intranet-tmpl/prog/img/famfamfam/silk/error.png b/koha-tmpl/intranet-tmpl/prog/img/famfamfam/silk/error.png
new file mode 100644
index 0000000000000000000000000000000000000000..628cf2dae3d419ae220c8928ac71393b480745a3
GIT binary patch
literal 666
zcmV;L0%iS)P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!EJ;K`R5;6}
zQ%gvc0TBNFhpVpZYs*vuLt9diOv=bL4R1XR>eOSYYtbpBV}~vsBnU!_?2tr-P=|^T
zED<b;h)xArC`LwRuDi0BivEw8uY($aYXuI>%wc9ezHgW at NMb!^uT_|SvCpFLJylbx
zY%bpaTGI8IYXMN$9w<3j9VkA~NYOKEQXsj?6a9_hcwfU$acAhJhB)zb_w at MVUEy@S
zX&I>K-R!bhu3?(6bHWIg$HEl7{9g>>&l_qdd+UYb(1~BCo9LptNq&8>!yoJ3Ui(i5
zRJ|XnYBklL!{@$-7=3mJ>P at 1c=7Oc79e-V7yf+%lD2!I;Y&nXBZ>=B!5?CB>LvEx6
znI%n)qqi$#X#wKB(U7XP2P=+4{b at j#r%9-K(8UqtSDk>0UKzf*HM9yqMZ1D!$2MdZ
zR=`U>0zhOH1XqN?nY at AQqB7)Fp4{v&dKXvb43hZKvnN8;Po;+jY*}~*Z|W9Q0W%{D
z^T}Cc<|r(Su=1K=<sQLMN1ZE<U%m5Ea at 1Tp8to2kJ7-hpAhT}(Dgffk;4r2>P5>Z4
zg`et&Va}tdzBS-G-ZcO)zCWpJvGQwrHZ`@wpM420ac at bI5~KkTFfGEM3sPWO8<ADs
zh7X-F%!~{kwpFCFAaZKyF+vTnL(ye%#)|yqz~B3T5kTqQvq%jMT(v1Wka%_eG)1AJ
z_%n+q5XXb>co4^fI6lPnA)Y{ef%@{+SnoUk0+dW+*{8WvF8}}l07*qoM6N<$g7cXs
A&j0`b

literal 0
HcmV?d00001

diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl b/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl
index f34d4de..510313c 100644
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl
@@ -87,6 +87,11 @@
     <!-- TMPL_IF NAME="bad_biblionumber" -->
     <div class="dialog alert">ERROR: No biblio record found for biblionumber <!-- TMPL_VAR NAME="bad_biblionumber" -->.</div>
     <!-- /TMPL_IF -->
+    <!-- TMPL_IF NAME="none_available" -->
+    <div class="dialog alert">
+        <strong>Sorry</strong>, none of these items can be placed on hold.
+    </div>
+    <!-- /TMPL_IF -->
 </div>
 <!-- /TMPL_IF -->
 
@@ -135,7 +140,12 @@
 
 
 <fieldset class="action">            <input type="hidden" name="biblioitem" value="<!-- TMPL_VAR NAME="biblioitemnumber" -->" />
+
+            <!-- TMPL_IF NAME="none_available" -->
+            <input type="submit" disabled="disabled" value="Place Hold" />
+            <!-- TMPL_ELSE -->
             <input type="submit" value="Place Hold" />
+            <!-- /TMPL_IF -->
             <input type="hidden" name="all" value="1" />
             <input type="hidden" name="place_reserve" value="1" /></fieldset>
 			<!-- /TMPL_IF -->  
@@ -164,6 +174,7 @@
             <input type="radio" name="checkitem" value="<!-- TMPL_VAR NAME="itemnumber" -->" />
             <!-- TMPL_ELSE -->
             <input disabled="disabled" type="radio" name="checkitem" value="<!-- TMPL_VAR NAME="itemnumber" -->" />
+            <img src="/intranet-tmpl/<!-- TMPL_VAR NAME="theme" -->/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" />
             <!-- /TMPL_IF -->
             </td>
             <!-- TMPL_IF NAME="item-level_itypes" -->
@@ -206,7 +217,11 @@
         <!-- /TMPL_LOOP --> <!-- itemloop -->
         </table>
         <!-- /TMPL_LOOP --> <!-- bibitemloop -->
-        <input type="submit" value="Place Hold" />
+            <!-- TMPL_IF NAME="none_available" -->
+            <input type="submit" disabled="disabled" value="Place Hold" />
+            <!-- TMPL_ELSE -->
+            <input type="submit" value="Place Hold" />
+            <!-- /TMPL_IF -->
         </form><!-- /TMPL_IF -->
     <!-- end of the first form -->
 <!-- /TMPL_IF -->
diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl
index 552cffb..445da2b 100755
--- a/opac/opac-reserve.pl
+++ b/opac/opac-reserve.pl
@@ -257,6 +257,8 @@ foreach my $biblioitemnumber (@biblioitemnumbers) {
     $biblioitem->{description} =
       $itemtypes->{ $biblioitem->{itemtype} }{description};
 
+    my $num_available = 0;
+
     foreach
       my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )
     {
@@ -330,8 +332,17 @@ foreach my $biblioitemnumber (@biblioitemnumbers) {
         # If there is no loan, return and transfer, we show a checkbox.
         $item->{notforloan} = $item->{notforloan} || 0;
 
-        if (IsAvailableForItemLevelRequest($itemnumber)) {
+        my $branchitemrule = GetBranchItemRule( $borr->{'branchcode'}, $item->{'itype'} );
+        my $policy_holdallowed = 1;
+
+        if ( $branchitemrule->{'holdallowed'} == 0 ||
+                ( $branchitemrule->{'holdallowed'} == 1 && $borr->{'branchcode'} ne $item->{'homebranch'} ) ) {
+            $policy_holdallowed = 0;
+        }
+
+        if (IsAvailableForItemLevelRequest($itemnumber) and $policy_holdallowed) {
             $item->{available} = 1;
+            $num_available++;
         }
 
 	# FIXME: move this to a pm
@@ -345,6 +356,10 @@ foreach my $biblioitemnumber (@biblioitemnumbers) {
         push @{ $biblioitem->{itemloop} }, $item;
     }
 
+    if ( $num_available == 0 ) {
+        $template->param( none_available => 1, message => 1 );
+    }
+
     push @bibitemloop, $biblioitem;
 }
 
diff --git a/reserve/request.pl b/reserve/request.pl
index 5786ac8..7b074ab 100755
--- a/reserve/request.pl
+++ b/reserve/request.pl
@@ -246,18 +246,22 @@ my @bibitemloop;
 
 foreach my $biblioitemnumber (@biblioitemnumbers) {
     my $biblioitem = $biblioiteminfos_of->{$biblioitemnumber};
+    my $num_available;
+    my $num_override;
 
     $biblioitem->{description} =
       $itemtypes->{ $biblioitem->{itemtype} }{description};
 
-    foreach
-      my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )
-    {
+    foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
         my $item = $iteminfos_of->{$itemnumber};
-    $item->{itypename} = $itemtypes->{ $item->{itype} }{description};
-    $item->{imageurl} = getitemtypeimagelocation( 'intranet', $itemtypes->{ $item->{itype} }{imageurl} );
-        $item->{homebranchname} =
-          $branches->{ $item->{homebranch} }{branchname};
+
+        if (C4::Context->preference('item-level_itypes')) {
+            $item->{itype} = $biblioitem->{itemtype};
+        }
+
+        $item->{itypename} = $itemtypes->{ $item->{itype} }{description};
+        $item->{imageurl} = getitemtypeimagelocation( 'intranet', $itemtypes->{ $item->{itype} }{imageurl} );
+        $item->{homebranchname} = $branches->{ $item->{homebranch} }{branchname};
 
         # if the holdingbranch is different than the homebranch, we show the
         # holdingbranch of the document too
@@ -266,8 +270,8 @@ foreach my $biblioitemnumber (@biblioitemnumbers) {
               $branches->{ $item->{holdingbranch} }{branchname};
         }
         
-#   add information
-    $item->{itemcallnumber} = $item->{itemcallnumber};
+        #   add information
+        $item->{itemcallnumber} = $item->{itemcallnumber};
     
         # if the item is currently on loan, we display its return date and
         # change the background color
@@ -324,31 +328,55 @@ foreach my $biblioitemnumber (@biblioitemnumbers) {
         # If there is no loan, return and transfer, we show a checkbox.
         $item->{notforloan} = $item->{notforloan} || 0;
     
-    # if independent branches is on we need to check if the person can reserve
-    # for branches they arent logged in to
-    if ( C4::Context->preference("IndependantBranches") ) { 
-        if (! C4::Context->preference("canreservefromotherbranches")){
-        # cant reserve items so need to check if item homebranch and userenv branch match if not we cant reserve
-        my $userenv = C4::Context->userenv; 
-        if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
-            $item->{cantreserve} = 1 if ( $item->{homebranch} ne $userenv->{branch} );
-        } 
+        # if independent branches is on we need to check if the person can reserve
+        # for branches they arent logged in to
+        if ( C4::Context->preference("IndependantBranches") ) { 
+            if (! C4::Context->preference("canreservefromotherbranches")){
+                # cant reserve items so need to check if item homebranch and userenv branch match if not we cant reserve
+                my $userenv = C4::Context->userenv;
+                if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
+                    $item->{cantreserve} = 1 if ( $item->{homebranch} ne $userenv->{branch} );
+                }
+            }
         }
-    }
 
-    if (IsAvailableForItemLevelRequest($itemnumber) and not $item->{cantreserve}) {
-        $item->{available} = 1;
-    }
+        my $branchitemrule = GetBranchItemRule( $item->{'homebranch'}, $item->{'itype'} );
+        my $policy_holdallowed = 1;
 
-    # FIXME: move this to a pm
-    my $sth2 = $dbh->prepare("SELECT * FROM reserves WHERE borrowernumber=? AND itemnumber=? AND found='W'");
-    $sth2->execute($item->{ReservedForBorrowernumber},$item->{itemnumber});
-    while (my $wait_hashref = $sth2->fetchrow_hashref) {
-        $item->{waitingdate} = format_date($wait_hashref->{waitingdate});
-    }
+        $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}) {
+            if ( not $policy_holdallowed and C4::Context->preference( 'AllowHoldPolicyOverride' ) ) {
+                $item->{override} = 1;
+                $num_override++;
+            } elsif ( $policy_holdallowed ) {
+                $item->{available} = 1;
+                $num_available++;
+            }
+        }
+        # If none of the conditions hold true, then neither override nor available is set and the item cannot be checked
+
+        # FIXME: move this to a pm
+        my $sth2 = $dbh->prepare("SELECT * FROM reserves WHERE borrowernumber=? AND itemnumber=? AND found='W'");
+        $sth2->execute($item->{ReservedForBorrowernumber},$item->{itemnumber});
+        while (my $wait_hashref = $sth2->fetchrow_hashref) {
+            $item->{waitingdate} = format_date($wait_hashref->{waitingdate});
+        }
         push @{ $biblioitem->{itemloop} }, $item;
     }
 
+    if ( $num_override == scalar( @{ $biblioitem->{itemloop} } ) ) { # That is, if all items require an override
+        $template->param( override_required => 1 );
+    } elsif ( $num_available == 0 ) {
+        $template->param( none_available => 1 );
+        $template->param( warnings => 1 );
+    }
+
     push @bibitemloop, $biblioitem;
 }
 
-- 
1.5.5.GIT



More information about the Koha-patches mailing list