Bug 14695 - Add ability to place multiple item holds on a given record per patron
Signed-off-by: Nick Clemens <nick@bywatersolutions.com> Signed-off-by: Jason M. Burds <JBurds@dubuque.lib.ia.us> Signed-off-by: Benjamin Rokseth <benjamin.rokseth@kul.oslo.kommune.no>
This commit is contained in:
parent
abeed5b5f1
commit
bc39f0392b
137
C4/Reserves.pm
137
C4/Reserves.pm
|
|
@ -44,6 +44,7 @@ use Koha::Holds;
|
|||
use Koha::Libraries;
|
||||
use Koha::Items;
|
||||
use Koha::ItemTypes;
|
||||
use Koha::Patrons;
|
||||
|
||||
use List::MoreUtils qw( firstidx any );
|
||||
use Carp;
|
||||
|
|
@ -143,6 +144,8 @@ BEGIN {
|
|||
&GetReservesControlBranch
|
||||
|
||||
IsItemOnHoldAndFound
|
||||
|
||||
GetMaxPatronHoldsForRecord
|
||||
);
|
||||
@EXPORT_OK = qw( MergeHolds );
|
||||
}
|
||||
|
|
@ -170,12 +173,7 @@ sub AddReserve {
|
|||
$title, $checkitem, $found, $itemtype
|
||||
) = @_;
|
||||
|
||||
if ( Koha::Holds->search( { borrowernumber => $borrowernumber, biblionumber => $biblionumber } )->count() > 0 ) {
|
||||
carp("AddReserve: borrower $borrowernumber already has a hold for biblionumber $biblionumber");
|
||||
return;
|
||||
}
|
||||
|
||||
my $dbh = C4::Context->dbh;
|
||||
my $dbh = C4::Context->dbh;
|
||||
|
||||
$resdate = output_pref( { str => dt_from_string( $resdate ), dateonly => 1, dateformat => 'iso' })
|
||||
or output_pref({ dt => dt_from_string, dateonly => 1, dateformat => 'iso' });
|
||||
|
|
@ -464,7 +462,8 @@ sub CanItemBeReserved {
|
|||
|
||||
my $dbh = C4::Context->dbh;
|
||||
my $ruleitemtype; # itemtype of the matching issuing rule
|
||||
my $allowedreserves = 0;
|
||||
my $allowedreserves = 0; # Total number of holds allowed across all records
|
||||
my $holds_per_record = 1; # Total number of holds allowed for this one given record
|
||||
|
||||
# we retrieve borrowers and items informations #
|
||||
# item->{itype} will come for biblioitems if necessery
|
||||
|
|
@ -477,26 +476,16 @@ sub CanItemBeReserved {
|
|||
if ( $item->{damaged}
|
||||
&& !C4::Context->preference('AllowHoldsOnDamagedItems') );
|
||||
|
||||
#Check for the age restriction
|
||||
# Check for the age restriction
|
||||
my ( $ageRestriction, $daysToAgeRestriction ) =
|
||||
C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
|
||||
return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
|
||||
|
||||
my $controlbranch = C4::Context->preference('ReservesControlBranch');
|
||||
# Check that the patron doesn't have an item level hold on this item already
|
||||
return 'itemAlreadyOnHold'
|
||||
if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
|
||||
|
||||
# we retrieve user rights on this itemtype and branchcode
|
||||
my $sth = $dbh->prepare(
|
||||
q{
|
||||
SELECT categorycode, itemtype, branchcode, reservesallowed
|
||||
FROM issuingrules
|
||||
WHERE (categorycode in (?,'*') )
|
||||
AND (itemtype IN (?,'*'))
|
||||
AND (branchcode IN (?,'*'))
|
||||
ORDER BY categorycode DESC,
|
||||
itemtype DESC,
|
||||
branchcode DESC
|
||||
}
|
||||
);
|
||||
my $controlbranch = C4::Context->preference('ReservesControlBranch');
|
||||
|
||||
my $querycount = q{
|
||||
SELECT count(*) AS count
|
||||
|
|
@ -520,15 +509,27 @@ sub CanItemBeReserved {
|
|||
}
|
||||
|
||||
# we retrieve rights
|
||||
$sth->execute( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode );
|
||||
if ( my $rights = $sth->fetchrow_hashref() ) {
|
||||
$ruleitemtype = $rights->{itemtype};
|
||||
$allowedreserves = $rights->{reservesallowed};
|
||||
if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode ) ) {
|
||||
$ruleitemtype = $rights->{itemtype};
|
||||
$allowedreserves = $rights->{reservesallowed};
|
||||
$holds_per_record = $rights->{holds_per_record};
|
||||
}
|
||||
else {
|
||||
$ruleitemtype = '*';
|
||||
}
|
||||
|
||||
my $item = Koha::Items->find( $itemnumber );
|
||||
my $holds = Koha::Holds->search(
|
||||
{
|
||||
borrowernumber => $borrowernumber,
|
||||
biblionumber => $item->biblionumber,
|
||||
found => undef, # Found holds don't count against a patron's holds limit
|
||||
}
|
||||
);
|
||||
if ( $holds->count() >= $holds_per_record ) {
|
||||
return "tooManyHoldsForThisRecord";
|
||||
}
|
||||
|
||||
# we retrieve count
|
||||
|
||||
$querycount .= "AND $branchfield = ?";
|
||||
|
|
@ -758,8 +759,8 @@ sub GetReservesToBranch {
|
|||
my $dbh = C4::Context->dbh;
|
||||
my $sth = $dbh->prepare(
|
||||
"SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
|
||||
FROM reserves
|
||||
WHERE priority='0'
|
||||
FROM reserves
|
||||
WHERE priority='0'
|
||||
AND branchcode=?"
|
||||
);
|
||||
$sth->execute( $frombranch );
|
||||
|
|
@ -784,7 +785,7 @@ sub GetReservesForBranch {
|
|||
|
||||
my $query = "
|
||||
SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
|
||||
FROM reserves
|
||||
FROM reserves
|
||||
WHERE priority='0'
|
||||
AND found='W'
|
||||
";
|
||||
|
|
@ -1414,7 +1415,7 @@ sub ModReserveMinusPriority {
|
|||
my $dbh = C4::Context->dbh;
|
||||
my $query = "
|
||||
UPDATE reserves
|
||||
SET priority = 0 , itemnumber = ?
|
||||
SET priority = 0 , itemnumber = ?
|
||||
WHERE reserve_id = ?
|
||||
";
|
||||
my $sth_upd = $dbh->prepare($query);
|
||||
|
|
@ -1651,7 +1652,7 @@ sub ToggleLowestPriority {
|
|||
|
||||
my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
|
||||
$sth->execute( $reserve_id );
|
||||
|
||||
|
||||
_FixPriority({ reserve_id => $reserve_id, rank => '999999' });
|
||||
}
|
||||
|
||||
|
|
@ -1835,7 +1836,7 @@ sub _FixPriority {
|
|||
$priority[$j]->{'reserve_id'}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
|
||||
$sth->execute();
|
||||
|
||||
|
|
@ -2073,7 +2074,7 @@ sub _koha_notify_reserve {
|
|||
if (! $notification_sent) {
|
||||
&$send_notification('print', 'HOLD');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
=head2 _ShiftPriorityByDateAndPriority
|
||||
|
|
@ -2502,6 +2503,76 @@ sub IsItemOnHoldAndFound {
|
|||
return $found;
|
||||
}
|
||||
|
||||
=head2 GetMaxPatronHoldsForRecord
|
||||
|
||||
my $holds_per_record = ReservesControlBranch( $borrowernumber, $biblionumber );
|
||||
|
||||
For multiple holds on a given record for a given patron, the max
|
||||
number of record level holds that a patron can be placed is the highest
|
||||
value of the holds_per_record rule for each item if the record for that
|
||||
patron. This subroutine finds and returns the highest holds_per_record
|
||||
rule value for a given patron id and record id.
|
||||
|
||||
=cut
|
||||
|
||||
sub GetMaxPatronHoldsForRecord {
|
||||
my ( $borrowernumber, $biblionumber ) = @_;
|
||||
|
||||
my $patron = Koha::Patrons->find($borrowernumber);
|
||||
my @items = Koha::Items->search( { biblionumber => $biblionumber } );
|
||||
|
||||
my $controlbranch = C4::Context->preference('ReservesControlBranch');
|
||||
|
||||
my $categorycode = $patron->categorycode;
|
||||
my $branchcode;
|
||||
$branchcode = $patron->branchcode if ( $controlbranch eq "PatronLibrary" );
|
||||
|
||||
my $max = 0;
|
||||
foreach my $item (@items) {
|
||||
my $itemtype = $item->effective_itemtype();
|
||||
|
||||
$branchcode = $item->homebranch if ( $controlbranch eq "ItemHomeLibrary" );
|
||||
|
||||
my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
|
||||
my $holds_per_record = $rule ? $rule->{holds_per_record} : 0;
|
||||
$max = $holds_per_record if $holds_per_record > $max;
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
|
||||
=head2 GetHoldRule
|
||||
|
||||
my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
|
||||
|
||||
Returns the matching hold related issuingrule fields for a given
|
||||
patron category, itemtype, and library.
|
||||
|
||||
=cut
|
||||
|
||||
sub GetHoldRule {
|
||||
my ( $categorycode, $itemtype, $branchcode ) = @_;
|
||||
|
||||
my $dbh = C4::Context->dbh;
|
||||
|
||||
my $sth = $dbh->prepare(
|
||||
q{
|
||||
SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record
|
||||
FROM issuingrules
|
||||
WHERE (categorycode in (?,'*') )
|
||||
AND (itemtype IN (?,'*'))
|
||||
AND (branchcode IN (?,'*'))
|
||||
ORDER BY categorycode DESC,
|
||||
itemtype DESC,
|
||||
branchcode DESC
|
||||
}
|
||||
);
|
||||
|
||||
$sth->execute( $categorycode, $itemtype, $branchcode );
|
||||
|
||||
return $sth->fetchrow_hashref();
|
||||
}
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Koha Development Team <http://koha-community.org/>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,38 @@ sub waiting {
|
|||
return $self->search( { found => 'W' } );
|
||||
}
|
||||
|
||||
=head3 forced_hold_level
|
||||
|
||||
If a patron has multiple holds for a single record,
|
||||
those holds must be either all record level holds,
|
||||
or they must all be item level holds.
|
||||
|
||||
This method should be used with Hold sets where all
|
||||
Hold objects share the same patron and record.
|
||||
|
||||
This method will return 'item' if the patron has
|
||||
at least one item level hold. It will return 'record'
|
||||
if the patron has holds but none are item level,
|
||||
Finally, if the patron has no holds, it will return
|
||||
undef which indicateds the patron may select either
|
||||
record or item level holds, barring any other rules
|
||||
that would prevent one or the other.
|
||||
=cut
|
||||
|
||||
sub forced_hold_level {
|
||||
my ($self) = @_;
|
||||
|
||||
my $force_hold_level;
|
||||
|
||||
if ( $self->count() ) {
|
||||
my $has_item_level_holds;
|
||||
map { $has_item_level_holds ||= $_->itemnumber } $self->as_list();
|
||||
$force_hold_level = $has_item_level_holds ? 'item' : 'record';
|
||||
}
|
||||
|
||||
return $force_hold_level;
|
||||
}
|
||||
|
||||
=head3 type
|
||||
|
||||
=cut
|
||||
|
|
|
|||
|
|
@ -49,49 +49,39 @@ $(document).ready(function() {
|
|||
});
|
||||
|
||||
function check() {
|
||||
var msg = "";
|
||||
var count_reserv = 0;
|
||||
var alreadyreserved = 0;
|
||||
var msg = "";
|
||||
var count_reserv = 0;
|
||||
|
||||
// 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) {
|
||||
count_reserv++ ;
|
||||
}
|
||||
count_reserv++ ;
|
||||
}
|
||||
}
|
||||
// for only one item, check the checkitem without consider the loop checkitem
|
||||
if (i==0){
|
||||
if (document.form.checkitem.checked == true) {
|
||||
count_reserv++;
|
||||
}
|
||||
}
|
||||
if (document.form.checkitem.checked == true) {
|
||||
count_reserv++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (document.form.request.checked == true){
|
||||
count_reserv++ ;
|
||||
}
|
||||
|
||||
if (document.form.alreadyreserved && document.form.alreadyreserved.value == "1"){
|
||||
alreadyreserved++ ;
|
||||
if (document.form.requestany.checked == true){
|
||||
count_reserv++ ;
|
||||
}
|
||||
|
||||
if (count_reserv == "0"){
|
||||
msg += (_("- Please select an item to place a hold") + "\n");
|
||||
}
|
||||
if (count_reserv >= "2"){
|
||||
msg += (_("- You may only place a hold on one item at a time") + "\n");
|
||||
msg += (_("- Please select an item to place a hold") + "\n");
|
||||
}
|
||||
|
||||
if (alreadyreserved > "0"){
|
||||
msg += (_("- This patron had already placed a hold on this item") + "\n" + _("Please cancel the previous hold first") + "\n");
|
||||
if (msg == "") {
|
||||
$('#hold-request-form').preventDoubleFormSubmit();
|
||||
return(true);
|
||||
} else {
|
||||
alert(msg);
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (msg == "") return(true);
|
||||
else {
|
||||
alert(msg);
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
function checkMultiHold() {
|
||||
|
|
@ -117,6 +107,8 @@ function checkMultiHold() {
|
|||
$("#multi_hold_bibs").val(biblionumbers);
|
||||
$("#bad_bibs").val(badBibs);
|
||||
|
||||
$('#hold-request-form').preventDoubleFormSubmit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +156,6 @@ function checkMultiHold() {
|
|||
$("#" + fieldID).val("");
|
||||
});
|
||||
|
||||
$('#hold-request-form').preventDoubleFormSubmit();
|
||||
|
||||
[% UNLESS ( borrowernumber || borrowers || noitems ) %]
|
||||
[% IF ( CircAutocompl ) %]
|
||||
|
|
@ -271,7 +262,7 @@ function checkMultiHold() {
|
|||
[% IF ( exceeded_maxreserves ) %]
|
||||
<li><strong>Too many holds: </strong> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %] </a> can only place a maximum of [% maxreserves %] total holds.</li>
|
||||
[% ELSIF ( alreadypossession ) %]
|
||||
<li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</li>
|
||||
<li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</lie
|
||||
[% ELSIF ( alreadyreserved ) %]
|
||||
<li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
|
||||
[% ELSIF ( ageRestricted ) %]
|
||||
|
|
@ -402,11 +393,34 @@ function checkMultiHold() {
|
|||
</li>
|
||||
|
||||
[% UNLESS ( multi_hold ) %]
|
||||
<li> <label for="requestany">Place a hold on the next available item </label>
|
||||
<input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
|
||||
<li> <label for="requestany">Hold next available item </label>
|
||||
[% IF force_hold_level == 'item' %]
|
||||
<input type="checkbox" id="requestany" name="request" disabled="true" />
|
||||
[% ELSIF force_hold_level == 'record' %]
|
||||
<input type="checkbox" id="requestany" checked="checked" value="Any" disabled="true"/>
|
||||
<input type="hidden" name="request" value="Any"/>
|
||||
[% ELSE %]
|
||||
<input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
|
||||
[% END %]
|
||||
<input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
|
||||
<input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
|
||||
</li>
|
||||
|
||||
[% IF max_holds_for_record > 1 %]
|
||||
[% SET count = 1 %]
|
||||
<li>
|
||||
<label for="holds_to_place_count">Holds to place (count)</label>
|
||||
<select name="holds_to_place_count" id="holds_to_place_count">
|
||||
[% WHILE count <= max_holds_for_record %]
|
||||
<option value="[% count %]">[% count %]</option>
|
||||
[% SET count = count + 1 %]
|
||||
[% END %]
|
||||
|
||||
</select>
|
||||
</li>
|
||||
[% ELSE %]
|
||||
<input type="hidden" name="holds_to_place_count" value="1";
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
</ol>
|
||||
|
|
@ -431,7 +445,12 @@ function checkMultiHold() {
|
|||
[% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
|
||||
</ol>
|
||||
|
||||
<h2 style="padding: 0 1em;">Place a hold on a specific item</h2>
|
||||
<h2 style="padding: 0 1em;">
|
||||
Place a hold on a specific item
|
||||
[% IF bibitemloo.force_hold_level == 'item' %]
|
||||
<span class="error"><i>(Required)</i></span>
|
||||
[% END %]
|
||||
</h2>
|
||||
<table id="requestspecific">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -451,18 +470,44 @@ function checkMultiHold() {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[% SET selected = 0 %]
|
||||
[% FOREACH itemloo IN bibitemloo.itemloop %]
|
||||
[% UNLESS ( itemloo.hide ) %]
|
||||
<tr class="[% itemloo.backgroundcolor %]">
|
||||
<td>
|
||||
[% IF ( itemloo.available ) %]
|
||||
[% IF itemloo.force_hold_level == 'record' # Patron has placed a record level hold previously for this record %]
|
||||
<span class="error">
|
||||
<i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
|
||||
Hold must be record level
|
||||
</span>
|
||||
[% ELSIF ( 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="[% interface %]/[% theme %]/img/famfamfam/silk/error.png" alt="Requires override of hold policy" />
|
||||
<i class="fa fa-exclamation-triangle fa-lg" style="color:gold" alt="Requires override of hold policy"/></i>
|
||||
[% ELSE %]
|
||||
<input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
|
||||
<img src="[% interface %]/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" />
|
||||
<span class="error">
|
||||
<i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
|
||||
[% IF itemloo.not_holdable %]
|
||||
[% IF itemloo.not_holdable == 'damaged' %]
|
||||
Item damaged
|
||||
[% ELSIF itemloo.not_holdable == 'ageRestricted' %]
|
||||
Age restricted
|
||||
[% ELSIF itemloo.not_holdable == 'tooManyHoldsForThisRecord' %]
|
||||
Exceeded max holds per record
|
||||
[% ELSIF itemloo.not_holdable == 'tooManyReserves' %]
|
||||
Too many holds
|
||||
[% ELSIF itemloo.not_holdable == 'notReservable' %]
|
||||
Not holdable
|
||||
[% ELSIF itemloo.not_holdable == 'cannotReserveFromOtherBranches' %]
|
||||
Patron is from different library
|
||||
[% ELSIF itemloo.not_holdable == 'itemAlreadyOnHold' %]
|
||||
Patron already has hold for this item
|
||||
[% ELSE %]
|
||||
[% itemloo.not_holdable %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
</span>
|
||||
[% END %]
|
||||
</td>
|
||||
[% IF ( item_level_itypes ) %]
|
||||
|
|
|
|||
|
|
@ -359,17 +359,6 @@ unless ( $noreserves ) {
|
|||
}
|
||||
}
|
||||
|
||||
foreach my $res (@reserves) {
|
||||
foreach my $biblionumber (@biblionumbers) {
|
||||
if ( $res->{'biblionumber'} == $biblionumber && $res->{'borrowernumber'} == $borrowernumber) {
|
||||
# $template->param( message => 1 );
|
||||
# $noreserves = 1;
|
||||
# $template->param( already_reserved => 1 );
|
||||
$biblioDataHash{$biblionumber}->{already_reserved} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unless ($noreserves) {
|
||||
$template->param( select_item_types => 1 );
|
||||
}
|
||||
|
|
@ -468,9 +457,6 @@ foreach my $biblioNum (@biblionumbers) {
|
|||
|
||||
# the item could be reserved for this borrower vi a host record, flag this
|
||||
$reservedfor //= '';
|
||||
if ($reservedfor eq $borrowernumber){
|
||||
$itemLoopIter->{already_reserved} = 1;
|
||||
}
|
||||
|
||||
if ( defined $reservedate ) {
|
||||
$itemLoopIter->{backgroundcolor} = 'reserved';
|
||||
|
|
@ -515,12 +501,12 @@ foreach my $biblioNum (@biblionumbers) {
|
|||
$itemLoopIter->{nocancel} = 1;
|
||||
}
|
||||
|
||||
# if the items belongs to a host record, show link to host record
|
||||
if ($itemInfo->{biblionumber} ne $biblioNum){
|
||||
$biblioLoopIter{hostitemsflag} = 1;
|
||||
$itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
|
||||
$itemLoopIter->{hosttitle} = GetBiblioData($itemInfo->{biblionumber})->{title};
|
||||
}
|
||||
# if the items belongs to a host record, show link to host record
|
||||
if ( $itemInfo->{biblionumber} ne $biblioNum ) {
|
||||
$biblioLoopIter{hostitemsflag} = 1;
|
||||
$itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
|
||||
$itemLoopIter->{hosttitle} = GetBiblioData( $itemInfo->{biblionumber} )->{title};
|
||||
}
|
||||
|
||||
# If there is no loan, return and transfer, we show a checkbox.
|
||||
$itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
|
||||
|
|
@ -569,6 +555,24 @@ foreach my $biblioNum (@biblionumbers) {
|
|||
|
||||
$biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
|
||||
|
||||
# For multiple holds per record, if a patron has previously placed a hold,
|
||||
# the patron can only place more holds of the same type. That is, if the
|
||||
# patron placed a record level hold, all the holds the patron places must
|
||||
# be record level. If the patron placed an item level hold, all holds
|
||||
# the patron places must be item level
|
||||
my $forced_hold_level = Koha::Holds->search(
|
||||
{
|
||||
borrowernumber => $borrowernumber,
|
||||
biblionumber => $biblioNum,
|
||||
found => undef,
|
||||
}
|
||||
)->forced_hold_level();
|
||||
if ($forced_hold_level) {
|
||||
$biblioLoopIter{force_hold} = 1 if $forced_hold_level eq 'item';
|
||||
$biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record';
|
||||
}
|
||||
|
||||
|
||||
push @$biblioLoop, \%biblioLoopIter;
|
||||
|
||||
$anyholdable = 1 if $biblioLoopIter{holdable};
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ my $borrower = GetMember( 'borrowernumber' => $borrowernumber );
|
|||
my $multi_hold = $input->param('multi_hold');
|
||||
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
|
||||
my $bad_bibs = $input->param('bad_bibs');
|
||||
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
|
||||
|
||||
my %bibinfos = ();
|
||||
my @biblionumbers = split '/', $biblionumbers;
|
||||
|
|
@ -79,30 +80,28 @@ if (defined $checkitem && $checkitem ne ''){
|
|||
}
|
||||
}
|
||||
|
||||
if ($type eq 'str8' && $borrower){
|
||||
if ( $type eq 'str8' && $borrower ) {
|
||||
|
||||
foreach my $biblionumber (keys %bibinfos) {
|
||||
my $count=@bibitems;
|
||||
@bibitems=sort @bibitems;
|
||||
my $i2=1;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $checkitem && $checkitem ne ''){
|
||||
my $item = GetItem($checkitem);
|
||||
if ($item->{'biblionumber'} ne $biblionumber) {
|
||||
$biblionumber = $item->{'biblionumber'};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( defined $checkitem && $checkitem ne '' ) {
|
||||
my $item = GetItem($checkitem);
|
||||
if ( $item->{'biblionumber'} ne $biblionumber ) {
|
||||
$biblionumber = $item->{'biblionumber'};
|
||||
}
|
||||
}
|
||||
|
||||
if ($multi_hold) {
|
||||
my $bibinfo = $bibinfos{$biblionumber};
|
||||
|
|
@ -110,7 +109,11 @@ if ($type eq 'str8' && $borrower){
|
|||
$bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
|
||||
} else {
|
||||
# place a request on 1st available
|
||||
AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem,$found, $itemtype);
|
||||
for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) {
|
||||
AddReserve( $branch, $borrower->{'borrowernumber'},
|
||||
$biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
|
||||
$checkitem, $found, $itemtype );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,13 +122,16 @@ if ($type eq 'str8' && $borrower){
|
|||
$biblionumbers .= $bad_bibs;
|
||||
}
|
||||
print $input->redirect("request.pl?biblionumbers=$biblionumbers&multi_hold=1");
|
||||
} else {
|
||||
}
|
||||
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 ( $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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ script to place reserves/requests
|
|||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Modern::Perl;
|
||||
|
||||
use C4::Branch;
|
||||
use CGI qw ( -utf8 );
|
||||
use List::MoreUtils qw/uniq/;
|
||||
|
|
@ -235,38 +235,44 @@ foreach my $biblionumber (@biblionumbers) {
|
|||
$biblioloopiter{$canReserve} = 1;
|
||||
}
|
||||
|
||||
my $force_hold_level;
|
||||
if ( $borrowerinfo->{borrowernumber} ) {
|
||||
# For multiple holds per record, if a patron has previously placed a hold,
|
||||
# the patron can only place more holds of the same type. That is, if the
|
||||
# patron placed a record level hold, all the holds the patron places must
|
||||
# be record level. If the patron placed an item level hold, all holds
|
||||
# the patron places must be item level
|
||||
my $holds = Koha::Holds->search(
|
||||
{
|
||||
borrowernumber => $borrowerinfo->{borrowernumber},
|
||||
biblionumber => $biblionumber,
|
||||
found => undef,
|
||||
}
|
||||
);
|
||||
$force_hold_level = $holds->forced_hold_level();
|
||||
$biblioloopiter{force_hold_level} = $force_hold_level;
|
||||
$template->param( force_hold_level => $force_hold_level );
|
||||
|
||||
# For a librarian to be able to place multiple record holds for a patron for a record,
|
||||
# we must find out what the maximum number of holds they can place for the patron is
|
||||
my $max_holds_for_record = GetMaxPatronHoldsForRecord( $borrowerinfo->{borrowernumber}, $biblionumber );
|
||||
$max_holds_for_record = $max_holds_for_record - $holds->count();
|
||||
$biblioloopiter{max_holds_for_record} = $max_holds_for_record;
|
||||
$template->param( max_holds_for_record => $max_holds_for_record );
|
||||
}
|
||||
|
||||
# Check to see if patron is allowed to place holds on records where the
|
||||
# patron already has an item from that record checked out
|
||||
my $alreadypossession;
|
||||
if (not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowerinfo->{borrowernumber},$biblionumber)) {
|
||||
$alreadypossession = 1;
|
||||
if ( !C4::Context->preference('AllowHoldsOnPatronsPossessions')
|
||||
&& CheckIfIssuedToPatron( $borrowerinfo->{borrowernumber}, $biblionumber ) )
|
||||
{
|
||||
$template->param( alreadypossession => $alreadypossession, );
|
||||
}
|
||||
|
||||
# get existing reserves .....
|
||||
my $reserves = GetReservesFromBiblionumber({ biblionumber => $biblionumber, all_dates => 1 });
|
||||
my $count = scalar( @$reserves );
|
||||
|
||||
my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
|
||||
my $totalcount = $count;
|
||||
my $holds_count = 0;
|
||||
my $alreadyreserved = 0;
|
||||
|
||||
foreach my $res (@$reserves) {
|
||||
if ( defined $res->{found} ) { # found can be 'W' or 'T'
|
||||
$count--;
|
||||
}
|
||||
|
||||
if ( defined $borrowerinfo && defined($borrowerinfo->{borrowernumber}) && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) ) {
|
||||
$holds_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $holds_count ) {
|
||||
$alreadyreserved = 1;
|
||||
$biblioloopiter{warn} = 1;
|
||||
$biblioloopiter{alreadyres} = 1;
|
||||
}
|
||||
|
||||
$template->param(
|
||||
alreadyreserved => $alreadyreserved,
|
||||
alreadypossession => $alreadypossession,
|
||||
);
|
||||
|
||||
# FIXME think @optionloop, is maybe obsolete, or must be switchable by a systeme preference fixed rank or not
|
||||
# make priorities options
|
||||
|
|
@ -329,6 +335,8 @@ foreach my $biblionumber (@biblionumbers) {
|
|||
my $num_override = 0;
|
||||
my $hiddencount = 0;
|
||||
|
||||
$biblioitem->{force_hold_level} = $force_hold_level;
|
||||
|
||||
if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
|
||||
$biblioitem->{hostitemsflag} = 1;
|
||||
}
|
||||
|
|
@ -348,6 +356,8 @@ foreach my $biblionumber (@biblionumbers) {
|
|||
foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } ) {
|
||||
my $item = $iteminfos_of->{$itemnumber};
|
||||
|
||||
$item->{force_hold_level} = $force_hold_level;
|
||||
|
||||
unless (C4::Context->preference('item-level_itypes')) {
|
||||
$item->{itype} = $biblioitem->{itemtype};
|
||||
}
|
||||
|
|
@ -445,13 +455,14 @@ foreach my $biblionumber (@biblionumbers) {
|
|||
|
||||
$item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
|
||||
|
||||
my $can_item_be_reserved = CanItemBeReserved( $borrowerinfo->{borrowernumber}, $itemnumber );
|
||||
$item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
|
||||
|
||||
if (
|
||||
!$item->{cantreserve}
|
||||
&& !$exceeded_maxreserves
|
||||
&& IsAvailableForItemLevelRequest($item, $borrowerinfo)
|
||||
&& CanItemBeReserved(
|
||||
$borrowerinfo->{borrowernumber}, $itemnumber
|
||||
) eq 'OK'
|
||||
&& $can_item_be_reserved eq 'OK'
|
||||
)
|
||||
{
|
||||
$item->{available} = 1;
|
||||
|
|
|
|||
Loading…
Reference in New Issue