Adds a cron script to be run periodically that populates

the tmp_holdsqueue table. This is an alternative holds
targeting workflow that is more suitable for multi-location
libraries than the default holds picklist report.

Note to documentation writers: this summary should be
added to any holds documentation as an overview of
the avaialable methods for holds fulfillment.

This alternative holds workflow assumes an
expectation that the system should target a specific
item for a given hold request, attempt to fulfill the
hold with that item, and if unable to fulfill, select
an available item at another location to fulfill the
hold.

This is quite different than the default Koha behavior
which uses a 'broadcast' method of hold fulfillment.

How it works:

This script weights available locations for holds based
on options specified in two system preferences:

StaticHoldsQueueWeight
	Allows the library to specify a list of library
	location codes -- if used alone, it will rank the
	list statically, selecting the top-ranking available
	location to be added to the picklist.

RandomizeHoldsQueueWeight
	If RandomizeHoldsQueueWeight and StaticHoldsQueueWeight
	are set, the list of library codes in the
	StaticHoldsQueueWeight syspref are randomized rather
	than statically ranked. If RandomizeHoldsQueueWeight
	alone is set, the list of all available library codes
	is used to randomize the weight.

If neither syspref is set, the list is statically
ranked according to how they are pulled out of the system
database.

NOTE: This has not yet been tested with item-level holds
This commit is contained in:
Joshua Ferraro 2008-05-20 10:58:24 -05:00
parent 725e822aed
commit a9a4965277

View file

@ -0,0 +1,164 @@
#!/usr/bin/perl
#-----------------------------------
# Script Name: build_holds_queue.pl
# Description: builds a holds queue in the tmp_holdsqueue table
#-----------------------------------
use strict;
BEGIN {
# find Koha's Perl modules
# test carefully before changing this
use FindBin;
eval { require "$FindBin::Bin/../kohalib.pl" };
}
use C4::Context;
use C4::Search;
use C4::Items;
use C4::Branch;
# load the branches
my $branches = GetBranches();
# obtain the ranked list of weights for the case of static weighting
my $syspref = C4::Context->preference("StaticHoldsQueueWeight");
my @branch_loop;
@branch_loop = split(/,/, $syspref) if $syspref;
# TODO: Add Randomization Option
# If no syspref is set, use system-order to determine priority
unless ($syspref) {
for my $branch_hash (sort keys %$branches) {
push @branch_loop, {value => "$branch_hash" , branchname => $branches->{$branch_hash}->{'branchname'}, };
}
}
# if Randomization is enabled, randomize this array
@branch_loop = randarray(@branch_loop) if C4::Context->preference("RandomizeHoldsQueueWeight");;
my ($biblionumber,$itemnumber,$barcode,$holdingbranch,$pickbranch,$notes,$cardnumber,$surname,$firstname,$phone,$title,$callno,$rdate,$borrno);
my $dbh = C4::Context->dbh;
$dbh->do("DELETE FROM tmp_holdsqueue"); # clear the old table for new info
my $sth=$dbh->prepare("
SELECT biblionumber,itemnumber,reserves.branchcode,reservenotes,borrowers.borrowernumber,cardnumber,surname,firstname,phone,reservedate
FROM reserves,borrowers
WHERE reserves.found IS NULL
AND reserves.borrowernumber=borrowers.borrowernumber
AND priority=1
AND cancellationdate IS NULL
GROUP BY biblionumber");
my $sth_load=$dbh->prepare("
INSERT INTO tmp_holdsqueue (biblionumber,itemnumber,barcode,surname,firstname,phone,borrowernumber,cardnumber,reservedate,title,itemcallnumber,holdingbranch,pickbranch,notes)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
$sth->execute(); # get the list of biblionumbers for unfilled holds
GETIT:
while (my $data=$sth->fetchrow_hashref){
# get the basic hold info
$biblionumber = $data->{'biblionumber'};
$pickbranch = $data->{'branchcode'};
$notes = $data->{'reservenotes'};
$borrno = $data->{'borrowernumber'};
$cardnumber = $data->{'cardnumber'};
$surname = $data->{'surname'};
$firstname = $data->{'firstname'};
$phone = $data->{'phone'};
$rdate = $data->{'reservedate'};
my @items = GetItemsInfo($biblionumber,''); # get the items for this biblio
my @itemorder; # prepare a new array to hold re-ordered items
# Make sure someone(else) doesn't already have this item waiting for them
my $found_sth = $dbh->prepare("
SELECT found FROM reserves WHERE itemnumber=? AND found = ? AND cancellationdate IS NULL");
# The following lines take the retrieved items and run them through various
# tests to decide if they are to be used and then put them in the preferred
# 'pick' order.
foreach my $itm (@items) {
$found_sth->execute($itm->{itemnumber},"W");
my $found = $found_sth->fetchrow_hashref();
if ($found) {
$itm->{"found"} = $found->{"found"};
}
if ($itm->{"notforloan"}) {
# item is on order
next if $itm->{"notforloan"}== -1;
}
if ( ( (!$itm->{"binding"}) ||
# Item is at not at bindery, not checked out, and not lost
($itm->{"binding"}<1)) && (!$itm->{"found"}) && (!$itm->{"datedue"}) && ( (!$itm->{"itemlost"}) ||
# Item is not lost and not notforloan
($itm->{"itemlost"}==0) ) && ( ($itm->{"notforloan"}==0) ||
# Item is not notforloan
(!$itm->{"notforloan"}) ) ) {
warn "patron requested pickup at $pickbranch for item in ".$itm->{'holdingbranch'};
# This selects items for fulfilment, and weights them based on
# a static list
my $weight=0;
# always prefer a direct match
if ($itm->{'holdingbranch'} eq $pickbranch) {
warn "Found match in pickuplibrary";
$itemorder[$weight]=$itm;
}
else {
for my $branchcode (@branch_loop) {
$weight++;
if ($itm->{'homebranch'} eq $branchcode) {
warn "Match found with weight $weight in ".$branchcode;
$itemorder[$weight]=$itm;
}
}
}
}
}
my $count = @itemorder;
warn "Empty array" if $count<1;
next GETIT if $count<1; # if the re-ordered array is empty, skip to next
PREP:
foreach my $itmlist (@itemorder) {
if ($itmlist) {
$barcode = $itmlist->{'barcode'};
$itemnumber = $itmlist->{'itemnumber'};
$holdingbranch = $itmlist->{'holdingbranch'};
$title = $itmlist->{'title'};
$callno = $itmlist->{'itemcallnumber'};
last PREP; # we only want the first def item in the array
}
}
$sth_load->execute($biblionumber,$itemnumber,$barcode,$surname,$firstname,$phone,$borrno,$cardnumber,$rdate,$title,$callno,$holdingbranch,$pickbranch,$notes);
$sth_load->finish;
}
$sth->finish;
$dbh->disconnect;
sub randarray {
my @array = @_;
my @rand = undef;
my $seed = $#array + 1;
my $randnum = int(rand($seed));
$rand[$randnum] = shift(@array);
while (1) {
my $randnum = int(rand($seed));
if ($rand[$randnum] eq undef) {
$rand[$randnum] = shift(@array);
}
last if ($#array == -1);
}
return @rand;
}
print "finished\n";