Enhancement 7144: Floating Collections (per branch/itemtype)
Adds support for Floating Collections (i.e. items that don't automatically return home when checked in at another branch) on a per branchcode/itemtype basis. This patch adds a new column (returnbranch) to the default_circ_rules, default_branch_item_rules, default_branch_circ_rules and branch_item_rules tables, after the 'holdsallowed' column. While this is coded as a varchar(15), the only currently supported values are 'homebranch', 'holdingbranch', 'noreturn' and NULL. On upgrade, the value of HomeOrHoldingBranchReturn is used to populate the global default (which is stored in default_circ_rules.returnbranch). To access this value, use C4::Circulation::GetBranchItemRule. This subroutine is altered to supply an additional key, "returnbranch", containing this value (or 'homebranch' as a default). No existing usage of GetBranchItemRule should need to be modified. The use of HomeOrHoldingBranchReturn is removed in AddReturn to instead use this subroutine. This will determine, on a more granular level, where the item should be transferred, after all is said and done. If 'noreturn' is specified, then the material will remain at the branch doing the checking in. Signed-off-by: Liz Rea <wizzyrea@gmail.com> Passes prove t xt t/db_dependent I was able to make this feature work as advertised. As noted above, if you want a floating rule applied across all branches, adding a single default rule won't suffice, you'll need to add the rule to all branches. That issue is not related to the functioning of *this* patch however.
This commit is contained in:
parent
d266ccb81f
commit
d92c595dc4
5 changed files with 122 additions and 43 deletions
|
@ -1365,13 +1365,17 @@ sub GetBranchBorrowerCircRule {
|
|||
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:
|
||||
The return value is a hashref containing the following keys:
|
||||
|
||||
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.
|
||||
|
||||
returnbranch => branch to which to return item. Possible values:
|
||||
noreturn: do not return, let item remain where checked in (floating collections)
|
||||
homebranch: return to item's home branch
|
||||
|
||||
This searches branchitemrules in the following order:
|
||||
|
||||
* Same branchcode and itemtype
|
||||
|
@ -1379,7 +1383,7 @@ This searches branchitemrules in the following order:
|
|||
* branchcode '*', same itemtype
|
||||
* branchcode and itemtype '*'
|
||||
|
||||
Neither C<$branchcode> nor C<$categorycode> should be '*'.
|
||||
Neither C<$branchcode> nor C<$itemtype> should be '*'.
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -1389,33 +1393,36 @@ sub GetBranchItemRule {
|
|||
my $result = {};
|
||||
|
||||
my @attempts = (
|
||||
['SELECT holdallowed
|
||||
['SELECT holdallowed, returnbranch
|
||||
FROM branch_item_rules
|
||||
WHERE branchcode = ?
|
||||
AND itemtype = ?', $branchcode, $itemtype],
|
||||
['SELECT holdallowed
|
||||
['SELECT holdallowed, returnbranch
|
||||
FROM default_branch_circ_rules
|
||||
WHERE branchcode = ?', $branchcode],
|
||||
['SELECT holdallowed
|
||||
['SELECT holdallowed, returnbranch
|
||||
FROM default_branch_item_rules
|
||||
WHERE itemtype = ?', $itemtype],
|
||||
['SELECT holdallowed
|
||||
['SELECT holdallowed, returnbranch
|
||||
FROM default_circ_rules'],
|
||||
);
|
||||
|
||||
foreach my $attempt (@attempts) {
|
||||
my ($query, @bind_params) = @{$attempt};
|
||||
my $search_result = $dbh->selectrow_hashref ( $query , {}, @bind_params );
|
||||
|
||||
# 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 ) ) );
|
||||
$result->{'holdallowed'} = $search_result->{'holdallowed'} unless ( defined $result->{'holdallowed'} );
|
||||
$result->{'returnbranch'} = $search_result->{'returnbranch'} unless ( defined $result->{'returnbranch'} );
|
||||
}
|
||||
|
||||
# built-in default circulation rule
|
||||
return {
|
||||
holdallowed => 2,
|
||||
};
|
||||
$result->{'holdallowed'} = 2 unless ( defined $result->{'holdallowed'} );
|
||||
$result->{'returnbranch'} = 'homebranch' unless ( defined $result->{'returnbranch'} );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
=head2 AddReturn
|
||||
|
@ -1532,9 +1539,10 @@ sub AddReturn {
|
|||
my $item = GetItem($itemnumber) or die "GetItem($itemnumber) failed";
|
||||
# full item data, but no borrowernumber or checkout info (no issue)
|
||||
# we know GetItem should work because GetItemnumberFromBarcode worked
|
||||
my $hbr = C4::Context->preference("HomeOrHoldingBranchReturn") || "homebranch";
|
||||
$hbr = $item->{$hbr} || '';
|
||||
# item must be from items table -- issues table has branchcode and issuingbranch, not homebranch nor holdingbranch
|
||||
my $hbr = GetBranchItemRule($item->{'homebranch'}, $item->{'itype'})->{'returnbranch'} || "homebranch";
|
||||
# get the proper branch to which to return the item
|
||||
$hbr = $item->{$hbr} || $branch ;
|
||||
# if $hbr was "noreturn" or any other non-item table value, then it should 'float' (i.e. stay at this branch)
|
||||
|
||||
my $borrowernumber = $borrower->{'borrowernumber'} || undef; # we don't know if we had a borrower or not
|
||||
|
||||
|
|
|
@ -135,6 +135,7 @@ elsif ($op eq "set-branch-defaults") {
|
|||
my $categorycode = $input->param('categorycode');
|
||||
my $maxissueqty = $input->param('maxissueqty');
|
||||
my $holdallowed = $input->param('holdallowed');
|
||||
my $returnbranch = $input->param('returnbranch');
|
||||
$maxissueqty =~ s/\s//g;
|
||||
$maxissueqty = undef if $maxissueqty !~ /^\d+/;
|
||||
$holdallowed =~ s/\s//g;
|
||||
|
@ -144,34 +145,34 @@ elsif ($op eq "set-branch-defaults") {
|
|||
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 (?, ?)");
|
||||
(maxissueqty, holdallowed, returnbranch)
|
||||
VALUES (?, ?, ?)");
|
||||
my $sth_update = $dbh->prepare("UPDATE default_circ_rules
|
||||
SET maxissueqty = ?, holdallowed = ?");
|
||||
SET maxissueqty = ?, holdallowed = ?, returnbranch = ?");
|
||||
|
||||
$sth_search->execute();
|
||||
my $res = $sth_search->fetchrow_hashref();
|
||||
if ($res->{total}) {
|
||||
$sth_update->execute($maxissueqty, $holdallowed);
|
||||
$sth_update->execute($maxissueqty, $holdallowed, $returnbranch);
|
||||
} else {
|
||||
$sth_insert->execute($maxissueqty, $holdallowed);
|
||||
$sth_insert->execute($maxissueqty, $holdallowed, $returnbranch);
|
||||
}
|
||||
} 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 (?, ?, ?)");
|
||||
(branchcode, maxissueqty, holdallowed, returnbranch)
|
||||
VALUES (?, ?, ?, ?)");
|
||||
my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules
|
||||
SET maxissueqty = ?, holdallowed = ?
|
||||
SET maxissueqty = ?, holdallowed = ?, returnbranch = ?
|
||||
WHERE branchcode = ?");
|
||||
$sth_search->execute($branch);
|
||||
my $res = $sth_search->fetchrow_hashref();
|
||||
if ($res->{total}) {
|
||||
$sth_update->execute($maxissueqty, $holdallowed, $branch);
|
||||
$sth_update->execute($maxissueqty, $holdallowed, $returnbranch, $branch);
|
||||
} else {
|
||||
$sth_insert->execute($branch, $maxissueqty, $holdallowed);
|
||||
$sth_insert->execute($branch, $maxissueqty, $holdallowed, $returnbranch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,6 +259,7 @@ elsif ($op eq "add-branch-cat") {
|
|||
elsif ($op eq "add-branch-item") {
|
||||
my $itemtype = $input->param('itemtype');
|
||||
my $holdallowed = $input->param('holdallowed');
|
||||
my $returnbranch = $input->param('returnbranch');
|
||||
$holdallowed =~ s/\s//g;
|
||||
$holdallowed = undef if $holdallowed !~ /^\d+/;
|
||||
|
||||
|
@ -266,34 +268,34 @@ elsif ($op eq "add-branch-item") {
|
|||
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 (?)");
|
||||
(holdallowed, returnbranch)
|
||||
VALUES (?, ?)");
|
||||
my $sth_update = $dbh->prepare("UPDATE default_circ_rules
|
||||
SET holdallowed = ?");
|
||||
SET holdallowed = ?, returnbranch = ?");
|
||||
|
||||
$sth_search->execute();
|
||||
my $res = $sth_search->fetchrow_hashref();
|
||||
if ($res->{total}) {
|
||||
$sth_update->execute($holdallowed);
|
||||
$sth_update->execute($holdallowed, $returnbranch);
|
||||
} else {
|
||||
$sth_insert->execute($holdallowed);
|
||||
$sth_insert->execute($holdallowed, $returnbranch);
|
||||
}
|
||||
} 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 (?, ?)");
|
||||
(itemtype, holdallowed, returnbranch)
|
||||
VALUES (?, ?, ?)");
|
||||
my $sth_update = $dbh->prepare("UPDATE default_branch_item_rules
|
||||
SET holdallowed = ?
|
||||
SET holdallowed = ?, returnbranch = ?
|
||||
WHERE itemtype = ?");
|
||||
$sth_search->execute($itemtype);
|
||||
my $res = $sth_search->fetchrow_hashref();
|
||||
if ($res->{total}) {
|
||||
$sth_update->execute($holdallowed, $itemtype);
|
||||
$sth_update->execute($holdallowed, $returnbranch, $itemtype);
|
||||
} else {
|
||||
$sth_insert->execute($itemtype, $holdallowed);
|
||||
$sth_insert->execute($itemtype, $holdallowed, $returnbranch);
|
||||
}
|
||||
}
|
||||
} elsif ($itemtype eq "*") {
|
||||
|
@ -301,17 +303,17 @@ elsif ($op eq "add-branch-item") {
|
|||
FROM default_branch_circ_rules
|
||||
WHERE branchcode = ?");
|
||||
my $sth_insert = $dbh->prepare("INSERT INTO default_branch_circ_rules
|
||||
(branchcode, holdallowed)
|
||||
VALUES (?, ?)");
|
||||
(branchcode, holdallowed, returnbranch)
|
||||
VALUES (?, ?, ?)");
|
||||
my $sth_update = $dbh->prepare("UPDATE default_branch_circ_rules
|
||||
SET holdallowed = ?
|
||||
SET holdallowed = ?, returnbranch = ?
|
||||
WHERE branchcode = ?");
|
||||
$sth_search->execute($branch);
|
||||
my $res = $sth_search->fetchrow_hashref();
|
||||
if ($res->{total}) {
|
||||
$sth_update->execute($holdallowed, $branch);
|
||||
$sth_update->execute($holdallowed, $returnbranch, $branch);
|
||||
} else {
|
||||
$sth_insert->execute($branch, $holdallowed);
|
||||
$sth_insert->execute($branch, $holdallowed, $returnbranch);
|
||||
}
|
||||
} else {
|
||||
my $sth_search = $dbh->prepare("SELECT count(*) AS total
|
||||
|
@ -319,19 +321,19 @@ elsif ($op eq "add-branch-item") {
|
|||
WHERE branchcode = ?
|
||||
AND itemtype = ?");
|
||||
my $sth_insert = $dbh->prepare("INSERT INTO branch_item_rules
|
||||
(branchcode, itemtype, holdallowed)
|
||||
VALUES (?, ?, ?)");
|
||||
(branchcode, itemtype, holdallowed, returnbranch)
|
||||
VALUES (?, ?, ?, ?)");
|
||||
my $sth_update = $dbh->prepare("UPDATE branch_item_rules
|
||||
SET holdallowed = ?
|
||||
SET holdallowed = ?, returnbranch = ?
|
||||
WHERE branchcode = ?
|
||||
AND itemtype = ?");
|
||||
|
||||
$sth_search->execute($branch, $itemtype);
|
||||
my $res = $sth_search->fetchrow_hashref();
|
||||
if ($res->{total}) {
|
||||
$sth_update->execute($holdallowed, $branch, $itemtype);
|
||||
$sth_update->execute($holdallowed, $returnbranch, $branch, $itemtype);
|
||||
} else {
|
||||
$sth_insert->execute($branch, $itemtype, $holdallowed);
|
||||
$sth_insert->execute($branch, $itemtype, $holdallowed, $returnbranch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -484,6 +486,7 @@ if ($defaults) {
|
|||
$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_returnbranch => $defaults->{returnbranch});
|
||||
}
|
||||
|
||||
$template->param(default_rules => ($defaults ? 1 : 0));
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#! /usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use C4::Context;
|
||||
my $dbh=C4::Context->dbh;
|
||||
|
||||
$dbh->do("ALTER TABLE default_circ_rules ADD
|
||||
COLUMN `returnbranch` varchar(15) default NULL AFTER `holdallowed`");
|
||||
$dbh->do("ALTER TABLE branch_item_rules ADD
|
||||
COLUMN `returnbranch` varchar(15) default NULL AFTER `holdallowed`");
|
||||
$dbh->do("ALTER TABLE default_branch_circ_rules ADD
|
||||
COLUMN `returnbranch` varchar(15) default NULL AFTER `holdallowed`");
|
||||
$dbh->do("ALTER TABLE default_branch_item_rules ADD
|
||||
COLUMN `returnbranch` varchar(15) default NULL AFTER `holdallowed`");
|
||||
|
||||
# set the default rule to the current value of HomeOrHoldingBranchReturn (default to 'homebranch' if need be)
|
||||
my $homeorholdingbranchreturn = C4::Context->prefernce('HomeOrHoldingBranchReturn') || 'homebranch';
|
||||
$dbh->do("UPDATE default_circ_rules SET returnbranch = '$homeorholdingbranchreturn'");
|
||||
|
||||
print "Upgrade done (Adding 'returnbranch' to branch/item issuing rules tables)\n";
|
|
@ -317,6 +317,7 @@ CREATE TABLE `branch_item_rules` (
|
|||
`branchcode` varchar(10) NOT NULL,
|
||||
`itemtype` varchar(10) NOT NULL,
|
||||
`holdallowed` tinyint(1) default NULL,
|
||||
`returnbranch` varchar(15) default NULL,
|
||||
PRIMARY KEY (`itemtype`,`branchcode`),
|
||||
KEY `branch_item_rules_ibfk_2` (`branchcode`),
|
||||
CONSTRAINT `branch_item_rules_ibfk_1` FOREIGN KEY (`itemtype`) REFERENCES `itemtypes` (`itemtype`)
|
||||
|
@ -497,6 +498,7 @@ CREATE TABLE `default_branch_circ_rules` (
|
|||
`branchcode` VARCHAR(10) NOT NULL,
|
||||
`maxissueqty` int(4) default NULL,
|
||||
`holdallowed` tinyint(1) default NULL,
|
||||
`returnbranch` varchar(15) default NULL,
|
||||
PRIMARY KEY (`branchcode`),
|
||||
CONSTRAINT `default_branch_circ_rules_ibfk_1` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
|
@ -509,6 +511,7 @@ DROP TABLE IF EXISTS `default_branch_item_rules`;
|
|||
CREATE TABLE `default_branch_item_rules` (
|
||||
`itemtype` varchar(10) NOT NULL,
|
||||
`holdallowed` tinyint(1) default NULL,
|
||||
`returnbranch` varchar(15) default NULL,
|
||||
PRIMARY KEY (`itemtype`),
|
||||
CONSTRAINT `default_branch_item_rules_ibfk_1` FOREIGN KEY (`itemtype`) REFERENCES `itemtypes` (`itemtype`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
|
@ -523,6 +526,7 @@ CREATE TABLE `default_circ_rules` (
|
|||
`singleton` enum('singleton') NOT NULL default 'singleton',
|
||||
`maxissueqty` int(4) default NULL,
|
||||
`holdallowed` int(1) default NULL,
|
||||
`returnbranch` varchar(15) default NULL,
|
||||
PRIMARY KEY (`singleton`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde
|
|||
<th> </th>
|
||||
<th>Total Current Checkouts Allowed</th>
|
||||
<th>Hold Policy</th>
|
||||
<th>Return Policy</th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
|
@ -223,6 +224,31 @@ for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde
|
|||
</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="returnbranch">
|
||||
[% IF ( default_returnbranch == 'homebranch' ) %]
|
||||
<option value="homebranch" selected="selected">
|
||||
[% ELSE %]
|
||||
<option value="homebranch">
|
||||
[% END %]
|
||||
Item Returns Home
|
||||
</option>
|
||||
[% IF ( default_returnbranch == 'holdingbranch' ) %]
|
||||
<option value="holdingbranch" selected="selected">
|
||||
[% ELSE %]
|
||||
<option value="holdingbranch">
|
||||
[% END %]
|
||||
Item Returns to Issuing Branch
|
||||
</option>
|
||||
[% IF ( default_returnbranch == 'noreturn' ) %]
|
||||
<option value="noreturn" selected="selected">
|
||||
[% ELSE %]
|
||||
<option value="noreturn">
|
||||
[% END %]
|
||||
Item Floats
|
||||
</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>
|
||||
|
@ -316,6 +342,7 @@ for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde
|
|||
<tr>
|
||||
<th>Item Type</th>
|
||||
<th>Hold Policy</th>
|
||||
<th>Return Policy</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
[% FOREACH branch_item_rule_loo IN branch_item_rule_loop %]
|
||||
|
@ -338,6 +365,16 @@ for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde
|
|||
No Holds Allowed
|
||||
[% END %]
|
||||
</td>
|
||||
<td>[% IF ( branch_item_rule_loo.returnbranch == 'homebranch' ) %]
|
||||
Item Returns Home
|
||||
[% ELSIF ( branch_item_rule_loo.returnbranch == 'holdingbranch' ) %]
|
||||
Item Returns to Issuing Branch
|
||||
[% ELSIF ( branch_item_rule_loo.returnbranch == 'noreturn' ) %]
|
||||
Item Floats
|
||||
[% ELSE %]
|
||||
Error - unknown option
|
||||
[% 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=[% current_branch %]">Delete</a>
|
||||
</td>
|
||||
|
@ -358,6 +395,13 @@ for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde
|
|||
<option value="0">No Holds Allowed</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select name="returnbranch">
|
||||
<option value="homebranch">Item Returns Home</option>
|
||||
<option value="holdingbranch">Item Returns to Issuing Branch</option>
|
||||
<option value="noreturn">Item Floats</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="submit" value="Add" class="submit" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
Loading…
Reference in a new issue