Bug 18936: (follow-up) Add cloning of circulation rules back to Koha
[koha.git] / circ / pendingreserves.pl
blob9f62b28b22639a56abc45abd560a8cf743567d29
1 #!/usr/bin/perl
3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use Modern::Perl;
22 use constant PULL_INTERVAL => 2;
24 use C4::Context;
25 use C4::Output;
26 use CGI qw ( -utf8 );
27 use C4::Auth;
28 use C4::Debug;
29 use C4::Items qw( ModItem ModItemTransfer );
30 use C4::Reserves qw( ModReserveCancelAll );
31 use Koha::Biblios;
32 use Koha::DateUtils;
33 use Koha::Holds;
34 use DateTime::Duration;
36 my $input = new CGI;
37 my $startdate = $input->param('from');
38 my $enddate = $input->param('to');
39 my $theme = $input->param('theme'); # only used if allowthemeoverride is set
40 my $op = $input->param('op') || '';
41 my $borrowernumber = $input->param('borrowernumber');
42 my $reserve_id = $input->param('reserve_id');
44 my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
46 template_name => "circ/pendingreserves.tt",
47 query => $input,
48 type => "intranet",
49 authnotrequired => 0,
50 flagsrequired => { circulate => "circulate_remaining_permissions" },
51 debug => 1,
55 my @messages;
56 if ( $op eq 'cancel_reserve' and $reserve_id ) {
57 my $hold = Koha::Holds->find( $reserve_id );
58 if ( $hold ) {
59 $hold->cancel;
60 push @messages, { type => 'message', code => 'hold_cancelled' };
62 } elsif ( $op =~ m|^mark_as_lost| ) {
63 my $hold = Koha::Holds->find( $reserve_id );
64 die "wrong reserve_id" unless $hold; # This is a bit rude, but we are not supposed to get a wrong reserve_id
65 my $item = $hold->item;
66 if ( $item and C4::Context->preference('CanMarkHoldsToPullAsLost') =~ m|^allow| ) {
67 my $patron = $hold->borrower;
68 C4::Circulation::LostItem( $item->itemnumber, "pendingreserves" );
69 if ( $op eq 'mark_as_lost_and_notify' and C4::Context->preference('CanMarkHoldsToPullAsLost') eq 'allow_and_notify' ) {
70 my $library = $hold->branch;
71 my $letter = C4::Letters::GetPreparedLetter(
72 module => 'reserves',
73 letter_code => 'CANCEL_HOLD_ON_LOST',
74 branchcode => $patron->branchcode,
75 lang => $patron->lang,
76 tables => {
77 branches => $library->branchcode,
78 borrowers => $patron->borrowernumber,
79 items => $item->itemnumber,
80 biblio => $hold->biblionumber,
81 biblioitems => $hold->biblionumber,
82 reserves => $hold->unblessed,
85 if ( $letter ) {
86 my $admin_email_address = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');
88 C4::Letters::EnqueueLetter(
89 { letter => $letter,
90 borrowernumber => $patron->borrowernumber,
91 message_transport_type => 'email',
92 from_address => $admin_email_address,
95 unless ( $patron->notice_email_address ) {
96 push @messages, {type => 'alert', code => 'no_email_address', };
98 push @messages, { type => 'message', code => 'letter_enqueued' };
99 } else {
100 push @messages, { type => 'error', code => 'no_template_notice' };
103 $hold->cancel;
104 if ( $item->homebranch ne $item->holdingbranch ) {
105 C4::Items::ModItemTransfer( $item->itemnumber, $item->holdingbranch, $item->homebranch );
108 if ( my $yaml = C4::Context->preference('UpdateItemWhenLostFromHoldList') ) {
109 $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt
110 my $assignments;
111 eval { $assignments = YAML::Load($yaml); };
112 if ($@) {
113 warn "Unable to parse UpdateItemWhenLostFromHoldList syspref : $@" if $@;
115 else {
116 eval {
117 C4::Items::ModItem( $assignments, undef, $item->itemnumber );
119 warn "Unable to modify item itemnumber=" . $item->itemnumber . ": $@" if $@;
123 } elsif ( not $item ) {
124 push @messages, { type => 'alert', code => 'hold_placed_at_biblio_level'};
125 } # else the url parameters have been modified and the user is not allowed to continue
129 my $today = dt_from_string;
131 if ( $startdate ) {
132 $startdate =~ s/^\s+//;
133 $startdate =~ s/\s+$//;
134 $startdate = eval{dt_from_string( $startdate )};
136 unless ( $startdate ){
137 # changed from delivered range of 10 years-yesterday to 2 days ago-today
138 # Find two days ago for the default shelf pull start date, unless HoldsToPullStartDate sys pref is set.
139 $startdate = $today - DateTime::Duration->new( days => C4::Context->preference('HoldsToPullStartDate') || PULL_INTERVAL );
142 if ( $enddate ) {
143 $enddate =~ s/^\s+//;
144 $enddate =~ s/\s+$//;
145 $enddate = eval{dt_from_string( $enddate )};
147 unless ( $enddate ) {
148 #similarly: calculate end date with ConfirmFutureHolds (days)
149 $enddate = $today + DateTime::Duration->new( days => C4::Context->preference('ConfirmFutureHolds') || 0 );
152 my @reservedata;
153 my $dbh = C4::Context->dbh;
154 my $sqldatewhere = "";
155 my $startdate_iso = output_pref({ dt => $startdate, dateformat => 'iso', dateonly => 1 });
156 my $enddate_iso = output_pref({ dt => $enddate, dateformat => 'iso', dateonly => 1 });
158 $debug and warn $startdate_iso. "\n" . $enddate_iso;
160 my @query_params = ();
162 if ($startdate_iso) {
163 $sqldatewhere .= " AND reservedate >= ?";
164 push @query_params, $startdate_iso;
166 if ($enddate_iso) {
167 $sqldatewhere .= " AND reservedate <= ?";
168 push @query_params, $enddate_iso;
171 my $item_type = C4::Context->preference('item-level_itypes') ? "items.itype" : "biblioitems.itemtype";
173 # Bug 21320
174 if ( ! C4::Context->preference('AllowHoldsOnDamagedItems') ) {
175 $sqldatewhere .= " AND damaged = 0";
178 my $strsth =
179 "SELECT min(reservedate) as l_reservedate,
180 reserves.reserve_id,
181 reserves.borrowernumber as borrowernumber,
183 GROUP_CONCAT(DISTINCT items.holdingbranch
184 ORDER BY items.itemnumber SEPARATOR '|') l_holdingbranch,
185 reserves.biblionumber,
186 reserves.branchcode as l_branch,
187 reserves.itemnumber,
188 items.holdingbranch,
189 items.homebranch,
190 GROUP_CONCAT(DISTINCT $item_type
191 ORDER BY items.itemnumber SEPARATOR '|') l_item_type,
192 GROUP_CONCAT(DISTINCT items.location
193 ORDER BY items.itemnumber SEPARATOR '|') l_location,
194 GROUP_CONCAT(DISTINCT items.itemcallnumber
195 ORDER BY items.itemnumber SEPARATOR '|') l_itemcallnumber,
196 GROUP_CONCAT(DISTINCT items.enumchron
197 ORDER BY items.itemnumber SEPARATOR '|') l_enumchron,
198 GROUP_CONCAT(DISTINCT items.copynumber
199 ORDER BY items.itemnumber SEPARATOR '|') l_copynumber,
200 biblio.title,
201 biblio.subtitle,
202 biblio.medium,
203 biblio.part_number,
204 biblio.part_name,
205 biblio.author,
206 count(DISTINCT items.itemnumber) as icount,
207 count(DISTINCT reserves.borrowernumber) as rcount,
208 borrowers.firstname,
209 borrowers.surname
210 FROM reserves
211 LEFT JOIN items ON items.biblionumber=reserves.biblionumber
212 LEFT JOIN biblio ON reserves.biblionumber=biblio.biblionumber
213 LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber
214 LEFT JOIN branchtransfers ON items.itemnumber=branchtransfers.itemnumber
215 LEFT JOIN issues ON items.itemnumber=issues.itemnumber
216 LEFT JOIN borrowers ON reserves.borrowernumber=borrowers.borrowernumber
217 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 )
218 WHERE
219 reserves.found IS NULL
220 $sqldatewhere
221 AND (reserves.itemnumber IS NULL OR reserves.itemnumber = items.itemnumber)
222 AND items.itemnumber NOT IN (SELECT itemnumber FROM branchtransfers where datearrived IS NULL)
223 AND items.itemnumber NOT IN (select itemnumber FROM reserves where found IS NOT NULL)
224 AND issues.itemnumber IS NULL
225 AND reserves.priority <> 0
226 AND reserves.suspend = 0
227 AND notforloan = 0 AND itemlost = 0 AND withdrawn = 0
228 AND ( circulation_rules.rule_value IS NULL OR circulation_rules.rule_value != 0 )
230 # GROUP BY reserves.biblionumber allows only items that are not checked out, else multiples occur when
231 # multiple patrons have a hold on an item
234 if (C4::Context->preference('IndependentBranches')){
235 $strsth .= " AND items.holdingbranch=? ";
236 push @query_params, C4::Context->userenv->{'branch'};
238 $strsth .= " GROUP BY reserves.biblionumber ORDER BY biblio.title ";
240 my $sth = $dbh->prepare($strsth);
241 $sth->execute(@query_params);
243 while ( my $data = $sth->fetchrow_hashref ) {
244 push(
245 @reservedata, {
246 reservedate => $data->{l_reservedate},
247 firstname => $data->{firstname} || '',
248 surname => $data->{surname},
249 title => $data->{title},
250 subtitle => $data->{subtitle},
251 medium => $data->{medium},
252 part_number => $data->{part_number},
253 part_name => $data->{part_name},
254 author => $data->{author},
255 borrowernumber => $data->{borrowernumber},
256 biblionumber => $data->{biblionumber},
257 holdingbranches => [split('\|', $data->{l_holdingbranch})],
258 branch => $data->{l_branch},
259 itemcallnumber => [split('\|', $data->{l_itemcallnumber})],
260 enumchron => [split('\|', $data->{l_enumchron})],
261 copyno => [split('\|', $data->{l_copynumber})],
262 count => $data->{icount},
263 rcount => $data->{rcount},
264 pullcount => $data->{icount} <= $data->{rcount} ? $data->{icount} : $data->{rcount},
265 itemTypes => [split('\|', $data->{l_item_type})],
266 locations => [split('\|', $data->{l_location})],
267 reserve_id => $data->{reserve_id},
268 holdingbranch => $data->{holdingbranch},
269 homebranch => $data->{homebranch},
270 itemnumber => $data->{itemnumber},
274 $sth->finish;
276 $template->param(
277 todaysdate => $today,
278 from => $startdate,
279 to => $enddate,
280 reserveloop => \@reservedata,
281 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
282 HoldsToPullStartDate => C4::Context->preference('HoldsToPullStartDate') || PULL_INTERVAL,
283 HoldsToPullEndDate => C4::Context->preference('ConfirmFutureHolds') || 0,
284 messages => \@messages,
287 output_html_with_http_headers $input, $cookie, $template->output;