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>.
22 use constant PULL_INTERVAL => 2;
29 use C4::Items qw( ModItem ModItemTransfer );
30 use C4::Reserves qw( ModReserveCancelAll );
34 use DateTime::Duration;
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",
50 flagsrequired => { circulate => "circulate_remaining_permissions" },
56 if ( $op eq 'cancel_reserve' and $reserve_id ) {
57 my $hold = Koha::Holds->find( $reserve_id );
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(
73 letter_code => 'CANCEL_HOLD_ON_LOST',
74 branchcode => $patron->branchcode,
75 lang => $patron->lang,
77 branches => $library->branchcode,
78 borrowers => $patron->borrowernumber,
79 items => $item->itemnumber,
80 biblio => $hold->biblionumber,
81 biblioitems => $hold->biblionumber,
82 reserves => $hold->unblessed,
86 my $admin_email_address = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');
88 C4::Letters::EnqueueLetter(
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' };
100 push @messages, { type => 'error', code => 'no_template_notice' };
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
111 eval { $assignments = YAML::Load($yaml); };
113 warn "Unable to parse UpdateItemWhenLostFromHoldList syspref : $@" if $@;
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;
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 );
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 );
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;
167 $sqldatewhere .= " AND reservedate <= ?";
168 push @query_params, $enddate_iso;
171 my $item_type = C4::Context->preference('item-level_itypes') ? "items.itype" : "biblioitems.itemtype";
174 if ( ! C4::Context->preference('AllowHoldsOnDamagedItems') ) {
175 $sqldatewhere .= " AND damaged = 0";
179 "SELECT min(reservedate) as l_reservedate,
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,
189 reserves.reservenotes,
192 GROUP_CONCAT(DISTINCT $item_type
193 ORDER BY items.itemnumber SEPARATOR '|') l_item_type,
194 GROUP_CONCAT(DISTINCT items.location
195 ORDER BY items.itemnumber SEPARATOR '|') l_location,
196 GROUP_CONCAT(DISTINCT replace(items.itemcallnumber,' ',' ')
197 ORDER BY items.itemnumber SEPARATOR '<br/>') l_itemcallnumber,
198 GROUP_CONCAT(DISTINCT replace(items.enumchron,' ',' ')
199 ORDER BY items.itemnumber SEPARATOR '<br/>') l_enumchron,
200 GROUP_CONCAT(DISTINCT replace(items.copynumber,' ',' ')
201 ORDER BY items.itemnumber SEPARATOR '<br/>') l_copynumber,
204 count(DISTINCT items.itemnumber) as icount,
205 count(DISTINCT reserves.borrowernumber) as rcount,
209 LEFT JOIN items ON items.biblionumber=reserves.biblionumber
210 LEFT JOIN biblio ON reserves.biblionumber=biblio.biblionumber
211 LEFT JOIN biblioitems ON biblio.biblionumber=biblioitems.biblionumber
212 LEFT JOIN branchtransfers ON items.itemnumber=branchtransfers.itemnumber
213 LEFT JOIN issues ON items.itemnumber=issues.itemnumber
214 LEFT JOIN borrowers ON reserves.borrowernumber=borrowers.borrowernumber
216 reserves.found IS NULL
218 AND (reserves.itemnumber IS NULL OR reserves.itemnumber = items.itemnumber)
219 AND items.itemnumber NOT IN (SELECT itemnumber FROM branchtransfers where datearrived IS NULL)
220 AND items.itemnumber NOT IN (select itemnumber FROM reserves where found IS NOT NULL)
221 AND issues.itemnumber IS NULL
222 AND reserves.priority <> 0
223 AND reserves.suspend = 0
224 AND notforloan >= 0 AND itemlost = 0 AND withdrawn = 0
226 # GROUP BY reserves.biblionumber allows only items that are not checked out, else multiples occur when
227 # multiple patrons have a hold on an item
230 if (C4::Context->preference('IndependentBranches')){
231 $strsth .= " AND items.holdingbranch=? ";
232 push @query_params, C4::Context->userenv->{'branch'};
234 $strsth .= " GROUP BY reserves.biblionumber,reserves.reserve_id ORDER BY biblio.title ";
236 my $sth = $dbh->prepare($strsth);
237 $sth->execute(@query_params);
239 while ( my $data = $sth->fetchrow_hashref ) {
240 my $record = Koha::Biblios->find($data->{biblionumber});
242 $data->{subtitle} = [ $record->subtitles ];
246 reservedate => $data->{l_reservedate},
247 firstname => $data->{firstname} || '',
248 surname => $data->{surname},
249 title => $data->{title},
250 subtitle => $data->{subtitle},
251 author => $data->{author},
252 borrowernumber => $data->{borrowernumber},
253 biblionumber => $data->{biblionumber},
254 holdingbranches => [split('\|', $data->{l_holdingbranch})],
255 branch => $data->{l_branch},
256 itemcallnumber => $data->{l_itemcallnumber},
257 enumchron => $data->{l_enumchron},
258 copyno => $data->{l_copynumber},
259 count => $data->{icount},
260 rcount => $data->{rcount},
261 pullcount => $data->{icount} <= $data->{rcount} ? $data->{icount} : $data->{rcount},
262 itemTypes => [split('\|', $data->{l_item_type})],
263 locations => [split('\|', $data->{l_location})],
264 reserve_id => $data->{reserve_id},
265 holdingbranch => $data->{holdingbranch},
266 homebranch => $data->{homebranch},
267 itemnumber => $data->{itemnumber},
268 timestamp => $data->{timestamp}, # FFZG
269 reservenotes => $data->{reservenotes}, # FFZG
276 todaysdate => $today,
279 reserveloop => \@reservedata,
280 "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
281 HoldsToPullStartDate => C4::Context->preference('HoldsToPullStartDate') || PULL_INTERVAL,
282 HoldsToPullEndDate => C4::Context->preference('ConfirmFutureHolds') || 0,
283 messages => \@messages,
286 output_html_with_http_headers $input, $cookie, $template->output;