Koha/circ/pendingreserves.pl
Andrew Fuerste-Henry fe84c3acba Bug 23485: Show barcode in holds to pull
Updated to only show one barcode number, with either an "only" or an "or any available" depending on whether it's an item or bib hold.
Also incorporating feedback to simplify the TT logic and remove list formatting.

To test:
1 - Place an item level hold on a bib with several items with the same callnumber
2 - View the holds to pull report
3 - Try to guess which one on the shelf is right?
4 - Apply patch
5 - See the barcode in holds to pull report
6 - You can now grab the correct item (but don't yet)
7 - Place a next available hold on the same title
8 - See the report now shows one possible valid barcode with the text "or any available."
9 - Check in a different item that fills the next available hold
10 - Now the report shows the single item for the borrower

Signed-off-by: Michal Denar <black23@gmail.com>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
2020-09-28 16:09:10 +02:00

298 lines
12 KiB
Perl
Executable file

#!/usr/bin/perl
# Copyright 2000-2002 Katipo Communications
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use constant PULL_INTERVAL => 2;
use C4::Context;
use C4::Output;
use CGI qw ( -utf8 );
use C4::Auth;
use C4::Debug;
use C4::Items qw( ModItemTransfer );
use C4::Reserves qw( ModReserveCancelAll );
use Koha::Biblios;
use Koha::DateUtils;
use Koha::Holds;
use DateTime::Duration;
my $input = new CGI;
my $startdate = $input->param('from');
my $enddate = $input->param('to');
my $theme = $input->param('theme'); # only used if allowthemeoverride is set
my $op = $input->param('op') || '';
my $borrowernumber = $input->param('borrowernumber');
my $reserve_id = $input->param('reserve_id');
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{
template_name => "circ/pendingreserves.tt",
query => $input,
type => "intranet",
flagsrequired => { circulate => "circulate_remaining_permissions" },
debug => 1,
}
);
my @messages;
if ( $op eq 'cancel_reserve' and $reserve_id ) {
my $hold = Koha::Holds->find( $reserve_id );
if ( $hold ) {
my $cancellation_reason = $input->param('cancellation-reason');
$hold->cancel({ cancellation_reason => $cancellation_reason });
push @messages, { type => 'message', code => 'hold_cancelled' };
}
} elsif ( $op =~ m|^mark_as_lost| ) {
my $hold = Koha::Holds->find( $reserve_id );
die "wrong reserve_id" unless $hold; # This is a bit rude, but we are not supposed to get a wrong reserve_id
my $item = $hold->item;
if ( $item and C4::Context->preference('CanMarkHoldsToPullAsLost') =~ m|^allow| ) {
my $patron = $hold->borrower;
C4::Circulation::LostItem( $item->itemnumber, "pendingreserves" );
if ( $op eq 'mark_as_lost_and_notify' and C4::Context->preference('CanMarkHoldsToPullAsLost') eq 'allow_and_notify' ) {
my $library = $hold->branch;
my $letter = C4::Letters::GetPreparedLetter(
module => 'reserves',
letter_code => 'CANCEL_HOLD_ON_LOST',
branchcode => $patron->branchcode,
lang => $patron->lang,
tables => {
branches => $library->branchcode,
borrowers => $patron->borrowernumber,
items => $item->itemnumber,
biblio => $hold->biblionumber,
biblioitems => $hold->biblionumber,
reserves => $hold->unblessed,
},
);
if ( $letter ) {
my $admin_email_address = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');
C4::Letters::EnqueueLetter(
{ letter => $letter,
borrowernumber => $patron->borrowernumber,
message_transport_type => 'email',
from_address => $admin_email_address,
}
);
unless ( $patron->notice_email_address ) {
push @messages, {type => 'alert', code => 'no_email_address', };
}
push @messages, { type => 'message', code => 'letter_enqueued' };
} else {
push @messages, { type => 'error', code => 'no_template_notice' };
}
}
$hold->cancel;
if ( $item->homebranch ne $item->holdingbranch ) {
C4::Items::ModItemTransfer( $item->itemnumber, $item->holdingbranch, $item->homebranch, 'LostReserve' );
}
if ( my $yaml = C4::Context->preference('UpdateItemWhenLostFromHoldList') ) {
$yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
my $assignments;
eval { $assignments = YAML::Load($yaml); };
if ($@) {
warn "Unable to parse UpdateItemWhenLostFromHoldList syspref : $@" if $@;
}
else {
eval {
while ( my ( $f, $v ) = each( %$assignments ) ) {
$item->$f($v);
}
$item->store;
};
warn "Unable to modify item itemnumber=" . $item->itemnumber . ": $@" if $@;
}
}
} elsif ( not $item ) {
push @messages, { type => 'alert', code => 'hold_placed_at_biblio_level'};
} # else the url parameters have been modified and the user is not allowed to continue
}
my $today = dt_from_string;
if ( $startdate ) {
$startdate =~ s/^\s+//;
$startdate =~ s/\s+$//;
$startdate = eval{dt_from_string( $startdate )};
}
unless ( $startdate ){
# changed from delivered range of 10 years-yesterday to 2 days ago-today
# Find two days ago for the default shelf pull start date, unless HoldsToPullStartDate sys pref is set.
$startdate = $today - DateTime::Duration->new( days => C4::Context->preference('HoldsToPullStartDate') || PULL_INTERVAL );
}
if ( $enddate ) {
$enddate =~ s/^\s+//;
$enddate =~ s/\s+$//;
$enddate = eval{dt_from_string( $enddate )};
}
unless ( $enddate ) {
#similarly: calculate end date with ConfirmFutureHolds (days)
$enddate = $today + DateTime::Duration->new( days => C4::Context->preference('ConfirmFutureHolds') || 0 );
}
my @reservedata;
my $dbh = C4::Context->dbh;
my $sqldatewhere = "";
my $startdate_iso = output_pref({ dt => $startdate, dateformat => 'iso', dateonly => 1 });
my $enddate_iso = output_pref({ dt => $enddate, dateformat => 'iso', dateonly => 1 });
$debug and warn $startdate_iso. "\n" . $enddate_iso;
my @query_params = ();
if ($startdate_iso) {
$sqldatewhere .= " AND reservedate >= ?";
push @query_params, $startdate_iso;
}
if ($enddate_iso) {
$sqldatewhere .= " AND reservedate <= ?";
push @query_params, $enddate_iso;
}
my $item_type = C4::Context->preference('item-level_itypes') ? "items.itype" : "biblioitems.itemtype";
# Bug 21320
if ( ! C4::Context->preference('AllowHoldsOnDamagedItems') ) {
$sqldatewhere .= " AND damaged = 0";
}
my $strsth =
"SELECT min(reservedate) as l_reservedate,
reserves.reserve_id,
reserves.borrowernumber as borrowernumber,
GROUP_CONCAT(DISTINCT items.holdingbranch
ORDER BY items.itemnumber SEPARATOR '|') l_holdingbranch,
reserves.biblionumber,
reserves.branchcode as l_branch,
reserves.itemnumber,
items.holdingbranch,
items.homebranch,
GROUP_CONCAT(DISTINCT $item_type
ORDER BY items.itemnumber SEPARATOR '|') l_item_type,
GROUP_CONCAT(DISTINCT items.location
ORDER BY items.itemnumber SEPARATOR '|') l_location,
GROUP_CONCAT(DISTINCT items.itemcallnumber
ORDER BY items.itemnumber SEPARATOR '|') l_itemcallnumber,
GROUP_CONCAT(DISTINCT items.enumchron
ORDER BY items.itemnumber SEPARATOR '|') l_enumchron,
GROUP_CONCAT(DISTINCT items.copynumber
ORDER BY items.itemnumber SEPARATOR '|') l_copynumber,
GROUP_CONCAT(DISTINCT items.barcode
ORDER BY items.itemnumber SEPARATOR '|') l_barcode,
biblio.title,
biblio.copyrightdate,
biblioitems.publicationyear,
biblio.subtitle,
biblio.medium,
biblio.part_number,
biblio.part_name,
biblio.author,
biblioitems.editionstatement,
count(DISTINCT items.itemnumber) as icount,
count(DISTINCT reserves.borrowernumber) as rcount,
borrowers.firstname,
borrowers.surname
FROM reserves
LEFT JOIN items ON items.biblionumber=reserves.biblionumber
LEFT JOIN biblio ON reserves.biblionumber=biblio.biblionumber
LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber
LEFT JOIN branchtransfers ON items.itemnumber=branchtransfers.itemnumber
LEFT JOIN issues ON items.itemnumber=issues.itemnumber
LEFT JOIN borrowers ON reserves.borrowernumber=borrowers.borrowernumber
LEFT JOIN circulation_rules ON ( items.itype=circulation_rules.itemtype AND rule_name = 'holdallowed' AND circulation_rules.branchcode IS NULL AND circulation_rules.categorycode IS NULL )
WHERE
reserves.found IS NULL
$sqldatewhere
AND (reserves.itemnumber IS NULL OR reserves.itemnumber = items.itemnumber)
AND items.itemnumber NOT IN (SELECT itemnumber FROM branchtransfers where datearrived IS NULL)
AND items.itemnumber NOT IN (SELECT itemnumber FROM reserves WHERE found IS NOT NULL AND itemnumber IS NOT NULL)
AND issues.itemnumber IS NULL
AND reserves.priority <> 0
AND reserves.suspend = 0
AND notforloan = 0 AND itemlost = 0 AND withdrawn = 0
AND ( circulation_rules.rule_value IS NULL OR circulation_rules.rule_value != 0 )
";
# GROUP BY reserves.biblionumber allows only items that are not checked out, else multiples occur when
# multiple patrons have a hold on an item
#FIXME "found IS NOT NULL AND itemnumber IS NOT NULL" is just a workaround: see BZ 25726
if (C4::Context->preference('IndependentBranches')){
$strsth .= " AND items.holdingbranch=? ";
push @query_params, C4::Context->userenv->{'branch'};
}
$strsth .= " GROUP BY reserves.biblionumber ORDER BY biblio.title ";
my $sth = $dbh->prepare($strsth);
$sth->execute(@query_params);
while ( my $data = $sth->fetchrow_hashref ) {
push(
@reservedata, {
reservedate => $data->{l_reservedate},
firstname => $data->{firstname} || '',
surname => $data->{surname},
title => $data->{title},
editionstatement => $data->{editionstatement},
subtitle => $data->{subtitle},
medium => $data->{medium},
part_number => $data->{part_number},
part_name => $data->{part_name},
author => $data->{author},
borrowernumber => $data->{borrowernumber},
biblionumber => $data->{biblionumber},
holdingbranches => [split('\|', $data->{l_holdingbranch})],
branch => $data->{l_branch},
itemcallnumber => [split('\|', $data->{l_itemcallnumber})],
enumchron => [split('\|', $data->{l_enumchron})],
copyno => [split('\|', $data->{l_copynumber})],
barcode => [split('\|', $data->{l_barcode})],
count => $data->{icount},
rcount => $data->{rcount},
pullcount => $data->{icount} <= $data->{rcount} ? $data->{icount} : $data->{rcount},
itemTypes => [split('\|', $data->{l_item_type})],
locations => [split('\|', $data->{l_location})],
reserve_id => $data->{reserve_id},
holdingbranch => $data->{holdingbranch},
homebranch => $data->{homebranch},
itemnumber => $data->{itemnumber},
publicationyear => C4::Context->preference('marcflavour') eq "MARC21" ? $data->{copyrightdate} : $data->{publicationyear},
}
);
}
$sth->finish;
$template->param(
todaysdate => $today,
from => $startdate,
to => $enddate,
reserveloop => \@reservedata,
"BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
HoldsToPullStartDate => C4::Context->preference('HoldsToPullStartDate') || PULL_INTERVAL,
HoldsToPullEndDate => C4::Context->preference('ConfirmFutureHolds') || 0,
messages => \@messages,
);
output_html_with_http_headers $input, $cookie, $template->output;