3 # This code has been modified by Trendsetters (originally from opac-user.pl)
4 # This code has been modified by rch
5 # Parts Copyright 2010-2011, ByWater Solutions (those related to username/password auth)
7 # This file is part of Koha.
9 # Koha is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # Koha is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 # We're going to authenticate a self-check user. we'll add a flag to borrowers 'selfcheck'
24 # We're not in a controlled environment; we never trust the user.
26 # The checkout permission comes form the CGI cookie/session of a staff user.
27 # The patron is not really logging in here in the same way as they do on the
28 # rest of the OPAC. So don't confuse loggedinuser with the patron user.
29 # The patron id/cardnumber is retrieved from the JWT
35 use C4::Auth qw( in_iprange get_template_and_user checkpw );
36 use C4::Circulation qw( barcodedecode AddReturn CanBookBeIssued AddIssue CanBookBeRenewed AddRenewal );
38 use C4::Output qw( output_html_with_http_headers );
40 use Koha::DateUtils qw( dt_from_string );
41 use Koha::Acquisition::Currencies;
44 use Koha::Patron::Images;
45 use Koha::Patron::Messages;
48 use Koha::CookieManager;
52 unless (C4::Context->preference('WebBasedSelfCheck')) {
53 # redirect to OPAC home if self-check is not enabled
54 print $query->redirect("/cgi-bin/koha/opac-main.pl");
58 unless ( in_iprange(C4::Context->preference('SelfCheckAllowByIPRanges')) ) {
59 # redirect to OPAC home if self-checkout not permitted from current IP
60 print $query->redirect("/cgi-bin/koha/opac-main.pl");
64 $query->param(-name=>'sco_user_login',-values=>[1]);
66 my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
68 template_name => "sco/sco-main.tt",
69 flagsrequired => { self_check => "self_checkout_module" },
75 # Get the self checkout timeout preference, or use 120 seconds as a default
76 my $selfchecktimeout = 120000;
77 if (C4::Context->preference('SelfCheckTimeout')) {
78 $selfchecktimeout = C4::Context->preference('SelfCheckTimeout') * 1000;
80 $template->param( SelfCheckTimeout => $selfchecktimeout );
82 # Checks policy laid out by SCOAllowCheckin, defaults to 'on' if preference is undefined
83 my $allowselfcheckreturns = 1;
84 if (defined C4::Context->preference('SCOAllowCheckin')) {
85 $allowselfcheckreturns = C4::Context->preference('SCOAllowCheckin');
88 my $issuerid = $loggedinuser;
89 my ( $op, $patronlogin, $patronpw, $barcodestr, $confirmed, $newissues, $load_checkouts ) = (
90 $query->param("op") || '',
91 $query->param("patronlogin") || '',
92 $query->param("patronpw") || '',
93 $query->param("barcode") || '',
94 $query->param("confirmed") || '',
95 $query->param("newissues") || '',
96 $query->param("load_checkouts") || '',
99 my $jwt = $query->cookie('JWT');
100 #FIXME: This needs to be changed to a POSTed logout...
101 if ($op eq "logout") {
102 $template->param( loggedout => 1 );
103 $query->param( patronlogin => undef, patronpw => undef );
109 push @$barcodes, split( /\s\n/, $barcodestr );
110 $barcodes = [ map { $_ =~ /^\s*$/ ? () : barcodedecode( $_ ) } @$barcodes ];
113 my @newissueslist = split /,/, $newissues;
114 my $issuenoconfirm = 1; #don't need to confirm on issue.
115 my $issuer = Koha::Patrons->find( $issuerid )->unblessed;
117 my $patronid = $jwt ? Koha::Token->new->decode_jwt({ token => $jwt }) : undef;
118 unless ( $patronid ) {
119 if ( C4::Context->preference('SelfCheckoutByLogin') ) {
120 ( undef, $patronid ) = checkpw( $patronlogin, $patronpw );
122 else { # People should not do that unless they know what they are doing!
123 # SelfCheckAllowByIPRanges MUST be configured
124 $patronid = $query->param('patronid');
126 $jwt = Koha::Token->new->generate_jwt({ id => $patronid }) if $patronid;
131 Koha::Plugins->call( 'patron_barcode_transform', \$patronid );
132 $patron = Koha::Patrons->find( { cardnumber => $patronid } );
135 undef $jwt unless $patron;
137 my $branch = $issuer->{branchcode};
138 my $confirm_required = 0;
141 if ( C4::Context->preference('BatchCheckouts') and $patron ) {
142 my @batch_category_codes = split ',', C4::Context->preference('BatchCheckoutsValidCategories');
143 my $categorycode = $patron->categorycode;
144 if ( $categorycode && grep { $_ eq $categorycode } @batch_category_codes ) {
145 # do nothing - logged in patron is allowed to do batch checkouts
147 # patron category not allowed to do batch checkouts, only allow first barcode
148 while ( scalar @$barcodes > 1 ) {
153 # batch checkouts not enabled, only allow first barcode
154 while ( scalar @$barcodes > 1 ) {
159 if ( $patron && $op eq "cud-returnbook" && $allowselfcheckreturns ) {
162 foreach my $barcode ( @$barcodes ) {
163 my $item = Koha::Items->find( { barcode => $barcode } );
164 if ( $success && C4::Context->preference("CircConfirmItemParts") ) {
166 && $item->materials )
173 # Patron cannot checkin an item they don't own
175 unless $patron->checkouts->find( { itemnumber => $item->itemnumber } );
179 ($success) = AddReturn( $barcode, $branch )
183 returned => $success,
186 } # foreach barcode in barcodes
189 elsif ( $patron && ( $op eq 'cud-checkout' ) ) {
190 foreach my $barcode ( @$barcodes ) {
191 my $item = Koha::Items->find( { barcode => $barcode } );
193 my $needconfirm = {};
194 ( $impossible, $needconfirm ) = CanBookBeIssued(
199 C4::Context->preference("AllowItemsOnHoldCheckoutSCO")
202 if ( $confirm_required = scalar keys %$needconfirm ) {
203 for my $error ( qw( UNKNOWN_BARCODE max_loans_allowed ISSUED_TO_ANOTHER NO_MORE_RENEWALS NOT_FOR_LOAN DEBT WTHDRAWN RESTRICTED RESERVED ITEMNOTSAMEBRANCH EXPIRED DEBARRED CARD_LOST GNA INVALID_DATE UNKNOWN_BARCODE TOO_MANY DEBT_GUARANTEES DEBT_GUARANTORS USERBLOCKEDOVERDUE PATRON_CANT PREVISSUE NOT_FOR_LOAN_FORCING ITEM_LOST ADDITIONAL_MATERIALS ) ) {
204 if ( $needconfirm->{$error} ) {
205 $issue_error = $error;
212 if (scalar keys %$impossible) {
214 my $issue_error = (keys %$impossible)[0]; # FIXME This is wrong, we assume only one error and keys are not ordered
215 my $title = ( $item ) ? $item->biblio->title : '';
218 impossible => $issue_error,
219 "circ_error_$issue_error" => 1,
223 if ($issue_error eq 'DEBT') {
224 $template->param(DEBT => $impossible->{DEBT});
226 if ( $issue_error eq "NO_MORE_RENEWALS" ) {
234 } elsif ( $needconfirm->{RENEW_ISSUE} ){
238 confirm => $item->biblio->title,
239 confirm_renew_issue => 1,
243 } elsif ( $confirm_required && !$confirmed ) {
246 "circ_error_$issue_error" => 1,
249 if ($issue_error eq 'DEBT') {
250 $template->param(DEBT => $needconfirm->{DEBT});
254 if ( $confirmed || $issuenoconfirm ) { # we'll want to call getpatroninfo again to get updated issues.
255 my ( $hold_existed, $item );
256 if ( C4::Context->preference('HoldFeeMode') eq 'any_time_is_collected' ) {
257 # There is no easy way to know if the patron has been charged for this item.
258 # So we check if a hold existed for this item before the check in
259 $item = Koha::Items->find({ barcode => $barcode });
260 $hold_existed = Koha::Holds->search(
263 borrowernumber => $patron->borrowernumber,
265 biblionumber => $item->biblionumber,
266 itemnumber => $item->itemnumber
273 my $new_issue = AddIssue( $patron, $barcode );
274 $template->param( issued => 1, new_issue => $new_issue );
275 push @newissueslist, $barcode unless ( grep /^$barcode$/, @newissueslist );
277 if ( $hold_existed ) {
278 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
280 # If the hold existed before the check in, let's confirm that the charge line exists
281 # Note that this should not be needed but since we do not have proper exception handling here we do it this way
282 patron_has_hold_fee => Koha::Account::Lines->search(
284 borrowernumber => $patron->borrowernumber,
285 debit_type_code => 'RESERVE',
286 description => $item->biblio->title,
287 date => $dtf->format_date(dt_from_string)
293 $confirm_required = 1;
295 confirm => "Issuing title: " . $item->biblio->title,
301 } # foreach barcode in barcodes
304 if ( $patron && ( $op eq 'cud-renew' ) ) {
305 foreach my $barcode ( @$barcodes ) {
306 my $item = Koha::Items->find({ barcode => $barcode });
308 if ( $patron->checkouts->find( { itemnumber => $item->itemnumber } ) ) {
309 my ($status,$renewerror) = CanBookBeRenewed( $patron, $item->checkout );
313 borrowernumber => $patron->borrowernumber,
314 itemnumber => $item->itemnumber,
318 push @newissueslist, $barcode;
325 $template->param( renewed => 0 );
327 } # foreach barcode in barcodes
331 my $borrowername = sprintf "%s %s", ($patron->firstname || ''), ($patron->surname || '');
333 my $pending_checkouts = $patron->pending_checkouts;
334 if ( C4::Context->preference('SCOLoadCheckoutsByDefault') || $load_checkouts ) {
335 while ( my $c = $pending_checkouts->next ) {
336 my $checkout = $c->unblessed_all_relateds;
337 my ( $can_be_renewed, $renew_error ) = CanBookBeRenewed( $patron, $c );
338 $checkout->{can_be_renewed} = $can_be_renewed; # In the future this will be $checkout->can_be_renewed
339 $checkout->{renew_error} = $renew_error;
340 $checkout->{overdue} = $c->is_overdue;
341 push @checkouts, $checkout;
346 for ( C4::Context->preference("OPACShowHoldQueueDetails") ) {
347 m/priority/ and $show_priority = 1;
350 my $account = $patron->account;
351 my $total = $account->balance;
352 my $accountlines = $account->lines;
354 my $holds = $patron->holds;
355 my $waiting_holds_count = 0;
357 while(my $hold = $holds->next) {
358 $waiting_holds_count++ if $hold->is_waiting;
363 borrowername => $borrowername,
364 issues_count => scalar(@checkouts) || $pending_checkouts->count(),
365 ISSUES => \@checkouts,
367 newissues => join(',',@newissueslist),
368 patronlogin => $patronlogin,
369 patronpw => $patronpw,
370 waiting_holds_count => $waiting_holds_count,
372 load_checkouts => $load_checkouts,
373 borrowernumber => $patron->borrowernumber,
374 SuspendHoldsOpac => C4::Context->preference('SuspendHoldsOpac'),
375 AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
376 howpriority => $show_priority,
377 ACCOUNT_LINES => $accountlines,
381 my $patron_messages = Koha::Patron::Messages->search(
383 borrowernumber => $patron->borrowernumber,
388 patron_messages => $patron_messages,
389 opacnote => $patron->opacnote,
396 if (C4::Context->preference('ShowPatronImageInWebBasedSelfCheck')) {
397 my $patron_image = $patron->image;
399 display_patron_image => 1,
407 my $cookie_mgr = Koha::CookieManager->new;
408 $cookie = $cookie_mgr->replace_in_list( $cookie, $query->cookie(
410 -value => $jwt // '',
411 -expires => $jwt ? '+1d' : '',
413 -secure => ( C4::Context->https_enabled() ? 1 : 0 ),
416 $template->param(patronid => $patronid);
418 output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 };