Bugzilla – Attachment 36262 Details for
Bug 13630
Angular-based circulation client
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 13630 - Basic Angular circulation client
Bug-13630---Basic-Angular-circulation-client.patch (text/plain), 71.69 KB, created by
Jesse Weaver
on 2015-03-02 06:22:56 UTC
(
hide
)
Description:
Bug 13630 - Basic Angular circulation client
Filename:
MIME Type:
Creator:
Jesse Weaver
Created:
2015-03-02 06:22:56 UTC
Size:
71.69 KB
patch
obsolete
>From 7ec3a49cb685fe91da852815c53345ee56cb92a5 Mon Sep 17 00:00:00 2001 >From: Taylor Schmidt <taschmid@mymail.mines.edu> >Date: Mon, 19 May 2014 12:27:22 -0600 >Subject: [PATCH] Bug 13630 - Basic Angular circulation client > >--- > Koha/Service.pm | 66 +- > Koha/Service/Authentication.pm | 8 +- > Koha/Service/Bib.pm | 24 +- > Koha/Service/BibProfile.pm | 12 +- > Koha/Service/Config/SystemPreferences.pm | 1 - > Koha/Service/Patrons.pm | 245 ++++++ > circ/checkout.pl | 98 +++ > circ/circulation.pl | 3 + > .../prog/en/includes/doc-head-open.inc | 14 +- > .../prog/en/includes/members-toolbar.inc | 15 + > koha-tmpl/intranet-tmpl/prog/en/js/ajax.js | 6 +- > .../intranet-tmpl/prog/en/modules/circ/checkout.tt | 945 +++++++++++++++++++++ > svc/patrons | 23 + > t/db_dependent/Members.t | 18 +- > t/db_dependent/Reserves.t | 16 +- > 15 files changed, 1448 insertions(+), 46 deletions(-) > create mode 100644 Koha/Service/Patrons.pm > create mode 100755 circ/checkout.pl > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/circ/checkout.tt > create mode 100755 svc/patrons > >diff --git a/Koha/Service.pm b/Koha/Service.pm >index f194298..a4d9799 100644 >--- a/Koha/Service.pm >+++ b/Koha/Service.pm >@@ -59,8 +59,10 @@ use Modern::Perl; > use base 'Class::Accessor'; > > use C4::Auth qw( check_api_auth ); >+use C4::Context; > use C4::Output qw( :ajax ); > use CGI; >+use DateTime; > use JSON; > > our $debug; >@@ -109,6 +111,35 @@ sub new { > }, $class; > } > >+=head2 test >+ >+ $service->test( $request_method, $path_info, \%params ); >+ >+Sets up a fake CGI context for unit tests. >+ >+=cut >+ >+sub test { >+ my ( $self, $request_method, $path_info, $params ) = @_; >+ >+ $ENV{REQUEST_METHOD} = $request_method; >+ $ENV{PATH_INFO} = $path_info; >+ $ENV{HTTP_CONTENT_LENGTH} = "0"; >+ $self->query(CGI->new); >+ >+ foreach my $key ( keys %$params ) { >+ $self->query->param( $key, $params->{ $key } ); >+ } >+ >+ my $user = $ENV{KOHA_USER} || C4::Context->config("user"); >+ my $password = $ENV{KOHA_PASS} || C4::Context->config("pass"); >+ >+ $self->query->param( 'userid', $user ); >+ $self->query->param( 'password', $password ); >+ >+ $self->authenticate; >+} >+ > =head2 authenticate > > my ( $query, $cookie ) = $self->authenticate(); >@@ -123,14 +154,31 @@ This must be called before the C<croak> or C<output> methods. > sub authenticate { > my ( $self ) = @_; > >- $self->query(CGI->new); >+ unless ( defined( $self->auth_status ) ) { >+ $self->query(CGI->new) unless ( $self->query ); > >- my ( $status, $cookie, $sessionID ) = check_api_auth( $self->query, $self->{needed_flags} ); >- $self->cookie($cookie); >- $self->auth_status($status); >- $self->croak( 'auth', $status ) if ( $status ne 'ok' && !$self->{authnotrequired} ); >+ my ( $status, $cookie, $sessionID ) = check_api_auth( $self->query, $self->{needed_flags} ); >+ $self->cookie($cookie); >+ $self->auth_status($status); >+ $self->handle_auth_failure() if ( $status ne 'ok' ); >+ } > >- return ( $self->query, $cookie ); >+ return ( $self->query, $self->cookie ); >+} >+ >+=head2 handle_auth_failure >+ >+ $self->handle_auth_failure(); >+ >+Called when C<authenticate> fails (C<$self->auth_status> not 'ok'). By default, if >+C<$self->{authnotrequired}> is not set, croaks and outputs an auth error. >+ >+=cut >+ >+sub handle_auth_failure { >+ my ( $self ) = @_; >+ >+ $self->croak( 'auth', $self->auth_status ) if ( !$self->{authnotrequired} ); > } > > =head2 output >@@ -157,6 +205,8 @@ is given, outputs JSONP. > > =cut > >+*DateTime::TO_JSON = sub { shift->_stringify; }; >+ > sub output { > my ( $self, $response, $options ) = @_; > >@@ -170,10 +220,10 @@ sub output { > }; > > if ( $options->{type} eq 'json' ) { >- $response = encode_json($response); >+ $response = JSON->new->convert_blessed->encode($response); > > if ( $self->query->param( 'callback' ) ) { >- $response = $self->query->param( 'callback' ) . '(' . encode_json($response) . ');'; >+ $response = $self->query->param( 'callback' ) . '(' . $response . ');'; > $options->{status} = '200 OK'; > $options->{type} = 'js'; > } >diff --git a/Koha/Service/Authentication.pm b/Koha/Service/Authentication.pm >index a337eba..352b391 100644 >--- a/Koha/Service/Authentication.pm >+++ b/Koha/Service/Authentication.pm >@@ -20,6 +20,7 @@ package Koha::Service::Authentication; > > use Modern::Perl; > >+# Handles authentication and output manually, so no reason to inherit from Koha::Service::XML > use base 'Koha::Service'; > > use C4::Auth qw/check_api_auth/; >@@ -29,13 +30,15 @@ use XML::Simple; > sub new { > my ( $class ) = @_; > >- # Authentication is handled manually below > return $class->SUPER::new( { >- authnotrequired => 1, > needed_flags => { editcatalogue => 'edit_catalogue'}, > } ); > } > >+sub handle_auth_failure { >+ # Stub, to allow run() to output XML itself. >+} >+ > sub run { > my ( $self ) = @_; > # The authentication strategy for the biblios web >@@ -55,6 +58,7 @@ sub run { > > $self->authenticate; > >+ # Can't reuse Koha::Service::XML, as result node has different name. > $self->output( XMLout({ status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1), { type => 'xml' } ); > } > >diff --git a/Koha/Service/Bib.pm b/Koha/Service/Bib.pm >index 1d00f98..8f683f5 100644 >--- a/Koha/Service/Bib.pm >+++ b/Koha/Service/Bib.pm >@@ -21,7 +21,7 @@ package Koha::Service::Bib; > > use Modern::Perl; > >-use base 'Koha::Service'; >+use base 'Koha::Service::XML'; > > use C4::Biblio; > use C4::Items; >@@ -30,10 +30,8 @@ use XML::Simple; > sub new { > my ( $class ) = @_; > >- # Authentication is handled manually below > return $class->SUPER::new( { >- authnotrequired => 1, >- needed_flags => { editcatalogue => 'edit_catalogue'}, >+ needed_flags => { editcatalogue => 'edit_catalogue' }, > routes => [ > [ qr'GET /(\d+)', 'fetch_bib' ], > [ qr'POST /(\d+)', 'update_bib' ], >@@ -41,26 +39,13 @@ sub new { > } ); > } > >-sub run { >- my ( $self ) = @_; >- >- $self->authenticate; >- >- unless ( $self->auth_status eq "ok" ) { >- $self->output( XMLout( { auth_status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 ), { type => 'xml', status => '403 Forbidden' } ); >- exit; >- } >- >- $self->dispatch; >-} >- > sub fetch_bib { > my ( $self, $biblionumber ) = @_; > > my $record = GetMarcBiblio( $biblionumber, $self->query->url_param('items') ); > > if (defined $record) { >- $self->output( $record->as_xml_record(), { type => 'xml' } ); >+ return $record->as_xml_record(); > } else { > $self->output( '', { status => '404 Not Found', type => 'xml' } ); > } >@@ -77,7 +62,6 @@ sub update_bib { > > my $result = {}; > my $inxml = $self->query->param('POSTDATA'); >- use Data::Dumper; warn Dumper($self->query); > > my $record = eval {MARC::Record::new_from_xml( $inxml, "utf8", C4::Context->preference('marcflavour'))}; > my $do_not_escape = 0; >@@ -115,7 +99,7 @@ sub update_bib { > $do_not_escape = 1; > } > >- $self->output( XMLout($result, NoAttr => 1, RootName => 'response', XMLDecl => 1, NoEscape => $do_not_escape), { type => 'xml' } ); >+ return XMLout($result, NoAttr => 1, RootName => 'response', XMLDecl => 1, NoEscape => $do_not_escape); > } > > 1; >diff --git a/Koha/Service/BibProfile.pm b/Koha/Service/BibProfile.pm >index db74635..1fefbbf 100644 >--- a/Koha/Service/BibProfile.pm >+++ b/Koha/Service/BibProfile.pm >@@ -22,7 +22,7 @@ package Koha::Service::BibProfile; > > use Modern::Perl; > >-use base 'Koha::Service'; >+use base 'Koha::Service::XML'; > > use C4::Context; > use C4::Koha; >@@ -33,8 +33,7 @@ sub new { > > # Authentication is handled manually below > return $class->SUPER::new( { >- authnotrequired => 1, >- needed_flags => { editcatalogue => 'edit_catalogue'}, >+ needed_flags => { editcatalogue => 'edit_catalogue' }, > routes => [ > [ qr'GET /(\d+)', 'fetch_bib' ], > [ qr'POST /(\d+)', 'update_bib' ], >@@ -47,11 +46,6 @@ sub run { > > $self->authenticate; > >- unless ( $self->auth_status eq "ok" ) { >- $self->output( XMLout( { auth_status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 ), { type => 'xml', status => '403 Forbidden' } ); >- exit; >- } >- > # get list of required tags > my $result = {}; > $result->{'auth_status'} = $self->auth_status; >@@ -69,7 +63,7 @@ sub run { > XMLDecl => 1, > GroupTags => {mandatory_tags => 'tag', mandatory_subfields => 'subfield', reserved_tags => 'tag', valid_values => 'value'} > ), >- { type => 'xml', status => '403 Forbidden' } >+ { type => 'xml' } > ); > } > >diff --git a/Koha/Service/Config/SystemPreferences.pm b/Koha/Service/Config/SystemPreferences.pm >index e3494da..ecf26fa 100644 >--- a/Koha/Service/Config/SystemPreferences.pm >+++ b/Koha/Service/Config/SystemPreferences.pm >@@ -47,7 +47,6 @@ use C4::Log; > sub new { > my ( $class ) = @_; > >- # Authentication is handled manually below > return $class->SUPER::new( { > needed_flags => { parameters => 1 }, > routes => [ >diff --git a/Koha/Service/Patrons.pm b/Koha/Service/Patrons.pm >new file mode 100644 >index 0000000..ec315fb >--- /dev/null >+++ b/Koha/Service/Patrons.pm >@@ -0,0 +1,245 @@ >+#!/usr/bin/perl >+package Koha::Service::Patrons; >+ >+# This file is part of Koha. >+# >+# Copyright (C) 2014 ByWater Solutions >+# >+# 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>. >+ >+=head1 NAME >+ >+svc/patrons - Web service for getting patron information >+ >+=head1 SYNOPSIS >+ >+ GET /svc/patrons/BORROWERNUMBER >+ >+=head1 DESCRIPTION >+ >+This service is used to query and change patron information. >+ >+=head1 METHODS >+ >+=cut >+ >+use Modern::Perl; >+ >+use base 'Koha::Service'; >+ >+use C4::Biblio; >+use C4::Circulation; >+use C4::Context; >+use C4::Dates; >+use C4::Items; >+use C4::Members; >+use C4::Reserves; >+use C4::Search qw( SimpleSearch ); >+use Koha::DateUtils; >+ >+sub new { >+ my ( $class ) = @_; >+ >+ # Authentication is handled manually below >+ return $class->SUPER::new( { >+ needed_flags => { circulate => 'circulate_remaining_permissions' }, >+ routes => [ >+ [ qr'POST /(\d+)/checkouts', 'add_checkout'], >+ [ qr'GET /(\d+)/checkouts', 'get_checkouts' ], >+ [ qr'GET /(\d+)/holds', 'get_holds' ], >+ [ qr'GET /(\d+)/patronInfo', 'get_patron_info' ], >+ [ qr'POST /(\d+)/checkouts/(\d+(?:,\d+)*)', 'renew_checkouts', [ 'renewed' ] ], >+ ] >+ } ); >+} >+ >+=head2 add_checkout >+ >+=over 4 >+ >+POST /svc/patrons/BORROWERNUMBER/checkouts >+ >+=back >+ >+Checks out an item >+ >+=cut >+ >+sub add_checkout { >+ my ( $self, $borrowernumber ) = @_; >+ >+ my $datedue; >+ my $duedatespec = $self->query->param('duedate'); >+ >+ if ( C4::Context->preference('SpecifyDueDate') && $duedatespec ){ >+ if ($duedatespec =~ C4::Dates->regexp('syspref')) { >+ $datedue = dt_from_string($duedatespec); >+ } else { >+ return {errors => {INVALID_DATE=>$duedatespec}}; >+ } >+ } >+ >+ my ($barcode) = $self->require_params('barcode'); >+ >+ my $borrower = GetMember( borrowernumber => $borrowernumber ); >+ >+ my ( $errors, $questions, $alerts ) = >+ CanBookBeIssued( $borrower, $barcode, $datedue ); >+ >+ if ( $errors->{'UNKNOWN_BARCODE'} && C4::Context->preference("itemBarcodeFallbackSearch") ) { >+ my $query = "kw=" . $barcode; >+ my ( $searcherror, $results, $total_hits ) = SimpleSearch($query); >+ >+ # if multiple hits, offer options to librarian >+ if ( $total_hits > 0 ) { >+ my @options = (); >+ foreach my $hit ( @{$results} ) { >+ my $biblionumber = C4::Biblio::get_koha_field_from_marc( >+ 'biblio', >+ 'biblionumber', >+ C4::Search::new_record_from_zebra('biblioserver',$hit) >+ ); >+ >+ next unless ( $biblionumber ); >+ >+ # offer all items with barcodes individually >+ foreach my $item ( GetItemsInfo( $biblionumber ) ) { >+ $item->{available} = !( $item->{itemnotforloan} || $item->{onloan} || $item->{itemlost} || $item->{withdrawn} || $item->{damaged} || $item->{transfertwhen} || $item->{reservedate} ); >+ >+ push @options, $item if ( $item->{barcode} ); >+ } >+ } >+ >+ $errors->{fallback_choices} = \@options; >+ } >+ } >+ >+ if ( %$errors || ( %$questions && !$self->query->param('confirmed') ) ) { >+ return { >+ item => GetBiblioFromItemNumber( undef, $barcode ), >+ errors => $errors, >+ questions => $questions, >+ alerts => $alerts >+ }; >+ } >+ >+ AddIssue( $borrower, $barcode, $datedue ); >+ >+ return {}; >+} >+ >+=head2 get_holds >+ >+=over 4 >+ >+GET /svc/patrons/BORROWERNUMBER/holds >+ >+=back >+ >+Retrieves information on the holds for a patron. >+ >+=cut >+ >+sub get_holds { >+ my ( $self, $borrowernumber ) = @_; >+ >+ my @holds = GetReservesFromBorrowernumber($borrowernumber); >+ foreach my $hold (@holds) { >+ my $getiteminfo = GetBiblioFromItemNumber( $hold->{'itemnumber'} ); >+ $hold->{title} = $getiteminfo->{title}; >+ $hold->{author} = $getiteminfo->{author}; >+ $hold->{barcode} = $getiteminfo->{barcode}; >+ } >+ >+ return { holds => \@holds }; >+} >+ >+=head2 get_checkouts >+ >+=over 4 >+ >+GET /svc/patrons/BORROWERNUMBER/checkouts >+ >+=back >+ >+Retrieves information on the checkouts for a patron. >+ >+=cut >+ >+sub get_checkouts { >+ my ( $self, $borrowernumber ) = @_; >+ >+ return { checkouts => GetPendingIssues( $borrowernumber ) }; >+} >+ >+=head2 get_patron_info >+ >+=over 4 >+ >+GET /svc/patrons/BORROWERNUMBER/patronInfo >+ >+=back >+ >+Retrieves information on a patron. >+ >+=cut >+ >+sub get_patron_info { >+ my ( $self, $borrowernumber ) = @_; >+ >+ return { patronInfo => GetMemberDetails( $borrowernumber, 0 ) }; >+} >+ >+=head2 renew_checkouts >+ >+=over 4 >+ >+POST /svc/patrons/BORROWERNUMBER/checkouts/ITEMNUMBER,ITEMNUMBER,.../?renewed=1 >+ >+=back >+ >+Renews several checkouts. >+ >+=cut >+ >+sub renew_checkouts { >+ my ( $self, $borrowernumber, $itemnumbers ) = @_; >+ >+ my @items = split /,/, $itemnumbers; >+ >+ my $branch = C4::Context->userenv ? C4::Context->userenv->{'branch'} : ''; >+ my $datedue; >+ if ( $self->query->param('newduedate') ) { >+ $datedue = dt_from_string( $self->query->param('newduedate') ); >+ $datedue->set_hour(23); >+ $datedue->set_minute(59); >+ } >+ >+ my $override_limit = $self->query->param("override_limit") || 0; >+ my @responses; >+ foreach my $itemno (@items) { >+ # check status before renewing issue >+ my ( $renewokay, $error ) = >+ CanBookBeRenewed( $borrowernumber, $itemno, $override_limit ); >+ if ($renewokay) { >+ push @responses, { itemnumber => $itemno, datedue => AddRenewal( $borrowernumber, $itemno, $branch, $datedue ) }; >+ } else { >+ push @responses, { itemnumber => $itemno, error => $error }; >+ } >+ } >+ >+ return { responses => \@responses }; >+} >+ >+1; >diff --git a/circ/checkout.pl b/circ/checkout.pl >new file mode 100755 >index 0000000..1260c69 >--- /dev/null >+++ b/circ/checkout.pl >@@ -0,0 +1,98 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha. >+# >+# Copyright (C) 2014 ByWater Solutions >+# >+# 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 C4::Auth; >+use C4::Branch; >+use C4::ClassSource; >+use C4::Context; >+use C4::Output; >+use C4::Members; >+use CGI; >+use Koha::Database; >+ >+my $query = CGI->new; >+ >+my ( $template, $loggedinuser, $cookie ) = get_template_and_user ( >+ { >+ template_name => 'circ/checkout.tt', >+ query => $query, >+ type => "intranet", >+ authnotrequired => 0, >+ flagsrequired => { circulate => 'circulate_remaining_permissions' }, >+ } >+); >+ >+my $borrowernumber = $query->param( 'borrowernumber' ); >+$template->{ VARS }->{ borrowernumber }=$borrowernumber; >+$template->{ VARS }->{ circview }=1; >+$template->param( %{ GetMemberDetails( $borrowernumber, 0 ) } ); >+ >+my $schema = Koha::Database->new->schema; >+my $authorised_values = {}; >+ >+$authorised_values->{branches} = []; >+my $onlymine=C4::Context->preference('IndependentBranches') && >+ C4::Context->userenv && >+ C4::Context->userenv->{flags} % 2 == 0 && >+ C4::Context->userenv->{branch}; >+my $branches = GetBranches($onlymine); >+foreach my $thisbranch ( sort keys %$branches ) { >+ push @{ $authorised_values->{branches} }, { value => $thisbranch, lib => $branches->{$thisbranch}->{'branchname'} }; >+} >+ >+$authorised_values->{itemtypes} = [ $schema->resultset( "Itemtype" )->search( undef, { >+ columns => [ { value => 'itemtype' }, { lib => "description" } ], >+ order_by => "description", >+ result_class => 'DBIx::Class::ResultClass::HashRefInflator' >+} ) ]; >+ >+my $class_sources = GetClassSources(); >+ >+my $default_source = C4::Context->preference("DefaultClassificationSource"); >+ >+foreach my $class_source (sort keys %$class_sources) { >+ next unless $class_sources->{$class_source}->{'used'} or >+ ($class_source eq $default_source); >+ push @{ $authorised_values->{cn_source} }, { value => $class_source, lib => $class_sources->{$class_source}->{'description'} }; >+} >+ >+my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : ""; >+my $results; >+if( $branch_limit ) { >+ $results = $schema->resultset( "AuthorisedValue" )->search( >+ { "authorised_values_branches.branchcode" => { "=", [ $branch_limit, undef ] } }, >+ { join => "authorised_values_branches", order_by => "lib" } ); >+} else { >+ $results = $schema->resultset( "AuthorisedValue" )->search( >+ undef, >+ { order_by => "lib" } ); >+} >+ >+foreach my $result ( $results->all ) { >+ $authorised_values->{$result->category} ||= []; >+ push @{ $authorised_values->{$result->category} }, { value => $result->authorised_value, lib => $result->lib }; >+} >+ >+$template->{VARS}->{authorised_values} = $authorised_values; >+ >+$template->{VARS}->{authvalcode_notforloan} = C4::Koha::GetAuthValCode('items.notforloan', '' ); >+ >+output_html_with_http_headers $query, $cookie, $template->output; >diff --git a/circ/circulation.pl b/circ/circulation.pl >index 982669c..9573399 100755 >--- a/circ/circulation.pl >+++ b/circ/circulation.pl >@@ -230,6 +230,9 @@ if ($findborrower) { > # get the borrower information..... > my $borrower; > if ($borrowernumber) { >+ if ( $query->cookie( 'checkout_client' ) eq 'beta' ) { >+ print $query->redirect( '/cgi-bin/koha/circ/checkout.pl?borrowernumber=' . $borrowernumber ); >+ } > $borrower = GetMemberDetails( $borrowernumber, 0 ); > my ( $od, $issue, $fines ) = GetMemberIssuesAndFines( $borrowernumber ); > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-open.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-open.inc >index 23449a5..08a215f 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-open.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-open.inc >@@ -1,4 +1,16 @@ > <!DOCTYPE html> > <!-- TEMPLATE FILE: [% template.name.split('/').last %] --> >-[% IF ( bidi ) %]<html lang="[% lang %]" dir="[% bidi %]">[% ELSE %]<html lang="[% lang %]">[% END %] >+[% IF ( bidi ) %] >+[% IF ( angular_app ) %] >+<html lang="[% lang %]" dir="[% bidi %]" ng-app="[% angular_app %]> >+[% ELSE %] >+<html lang="[% lang %]" dir="[% bidi %]"> >+[% END %] >+[% ELSE %] >+[% IF ( angular_app ) %] >+<html lang="[% lang %]" ng-app="[% angular_app %]"> >+[% ELSE %] >+<html lang="[% lang %]"> >+[% END %] >+[% END %] > <head> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc >index 828c564..050a7b0 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc >@@ -62,7 +62,21 @@ $(document).ready(function(){ > searchToHold(); > return false; > }) >+ $("#switchclient").click( function() { >+ var borrowernumber = [% borrowernumber || "null" %]; >+ >+ if ( $.cookie('checkout_client' ) == 'beta' ) { >+ $.cookie( 'checkout_client', 'standard', { expires: 365, path: '/' } ); >+ window.location = '/cgi-bin/koha/circ/circulation.pl?borrowernumber=' + borrowernumber; >+ } >+ else { >+ $.cookie( 'checkout_client', 'beta', { expires: 365, path: '/' } ); >+ window.location = '/cgi-bin/koha/circ/checkout.pl?borrowernumber=' + borrowernumber; >+ } >+ return false; >+ } ); > }); >+ > function confirm_deletion() { > var is_confirmed = window.confirm(_("Are you sure you want to delete this patron? This cannot be undone.")); > if (is_confirmed) { >@@ -195,6 +209,7 @@ function searchToHold(){ > <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="Patron is an adult" id="updatechild" href="#">Update child to adult patron</a></li></li> > [% END %] > <li><a id="exportcheckins" href="#">Export today's checked in barcodes</a></li> >+ <li><a id="switchclient" href="#">Switch checkout client</a></li> > </ul> > </div> > </div> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/ajax.js b/koha-tmpl/intranet-tmpl/prog/en/js/ajax.js >index a8a9241..3691d3b 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/js/ajax.js >+++ b/koha-tmpl/intranet-tmpl/prog/en/js/ajax.js >@@ -19,14 +19,14 @@ KOHA.AJAX = { > return; > } > >- var error = eval( '(' + xhr.responseText + ')' ); >+ var data = eval( '(' + xhr.responseText + ')' ); > >- if ( error.type == 'auth' ) { >+ if ( data.error == 'auth' ) { > humanMsg.displayMsg( MSG_SESSION_TIMED_OUT ); > } > > if ( callback ) { >- callback( error ); >+ callback( data ); > } else { > humanMsg.displayAlert( MSG_DATA_NOT_SAVED ); > } >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/checkout.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/checkout.tt >new file mode 100644 >index 0000000..2c7e160 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/checkout.tt >@@ -0,0 +1,945 @@ >+[% USE Koha %] >+[% USE Branches %] >+[% USE KohaDates %] >+[% IF ( export_remove_fields OR export_with_csv_profile ) %] >+ [% SET exports_enabled = 1 %] >+[% END %] >+[% USE AuthorisedValues %] >+[% INCLUDE 'doc-head-open.inc' angular_app = "checkoutApp" %] >+[% SET destination = "circ" %] >+<title>Koha › Circulation >+[% IF borrowernumber %] >+ › Checking out to [% INCLUDE 'patron-title.inc' invert_name = 1 %] >+[% END %] >+</title> >+[% INCLUDE 'doc-head-close.inc' %] >+[% INCLUDE 'calendar.inc' %] >+[% IF ( UseTablesortForCirc ) %]<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" /> >+[% INCLUDE 'datatables.inc' %][% END %] >+<script src="[% interface %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script> >+<script src="[% interface %]/lib/jquery/plugins/jquery-ui-timepicker-addon.min.js"></script> >+[% INCLUDE 'timepicker.inc' %] >+[% INCLUDE 'doc-head-angular.inc' %] >+<script> >+var authorised_values = { >+ [% FOREACH category IN authorised_values -%] >+ "[% category.key %]":{ >+ [% FOREACH value IN category.value -%] >+ "[% value.value %]":"[% value.lib %]", >+ [% END -%] >+ }, >+ [% END -%] >+} >+ >+var checkoutApp = angular.module( 'checkoutApp', [] ) >+ .filter( 'isNotEmpty', function() { >+ return function(obj) { >+ return !$.isEmptyObject(obj); >+ }; >+ } ); >+ >+checkoutApp.controller( 'CheckoutController', function ( $scope, $http ) { >+ $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; >+ $scope.borrowernumber = [% borrowernumber %]; >+ $scope.authorised_values = authorised_values; >+ >+ $http.get( '/cgi-bin/koha/svc/patrons/' + $scope.borrowernumber + '/patronInfo' ).success( function( data ) { >+ $scope.borrower = data.patronInfo; >+ $scope.not_loading = true; >+ $('#yui-main > .yui-b').show(); >+ }); >+ >+ function get_checkouts() { >+ $http.get( '/cgi-bin/koha/svc/patrons/' + $scope.borrowernumber + '/checkouts' ).success( function( data ) { >+ $scope.checkouts = data.checkouts; >+ }); >+ } >+ >+ get_checkouts(); >+ >+ $scope.checkout = function(barcode, confirmed) { >+ var barcode = barcode || $scope.barcode; >+ var params = { barcode: barcode, duedate: $scope.duedatespec || '' }; >+ if ( confirmed ) { >+ params.confirmed = 1; >+ params.cancelreserve = $scope.cancelreserve; >+ } >+ $scope.not_loading = false; >+ $http.post( '/cgi-bin/koha/svc/patrons/' + $scope.borrowernumber + '/checkouts', $.param( params ) ).success(function( data ) { >+ $scope.not_loading = true; >+ $scope.last_barcode = barcode; >+ $scope.barcode = ''; >+ >+ if ( data.errors || data.questions ) { >+ $scope.last_item = data.item; >+ $scope.errors = data.errors; >+ $scope.questions = data.questions; >+ $scope.cancelreserve = data.questions.RESERVE_WAITING ? "revert" : null; >+ }else { >+ $scope.last_item = null; >+ $scope.errors = null; >+ $scope.questions = null; >+ if ( !$scope.stickyduedate ) $scope.duedatespec = ""; >+ $('#barcode')[0].focus(); >+ get_checkouts(); >+ } >+ }); >+ }; >+ >+ $scope.cancelCheckout = function() { >+ $scope.questions = null; >+ $scope.errors = null; >+ $('#barcode')[0].focus(); >+ }; >+ >+ >+} ); >+ >+$(document).ready( function() { >+ $('#patronlists').tabs( [% IF ( UseTablesortForCirc ) %]{ >+ // Correct table sizing for tables hidden in tabs >+ // http://www.datatables.net/examples/api/tabs_and_scrolling.html >+ "show": function( event, ui ) { >+ var oTable = $( 'div.dataTables_wrapper>table', ui.panel ).dataTable(); >+ if ( oTable.length > 0 ) { >+ oTable.fnAdjustColumnSizing(); >+ } >+ } >+ }[% END %] ); >+ $("#duedatespec").datetimepicker({ >+ onClose: function(dateText, inst) { $("#barcode").focus(); }, >+ hour: 23, >+ minute: 59 >+ }); >+ $("#confduedatespec").datetimepicker({ >+ hour: 23, >+ minute: 59 >+ }); >+} ); >+</script> >+</head> >+<body id="circ_circulation" class="circ" ng-controller="CheckoutController"> >+ >+ <div class="loading-overlay" ng-hide="not_loading"> >+ <div>Loading, please wait...</div> >+ </div> >+ >+[% INCLUDE 'header.inc' %] >+[% INCLUDE 'circ-search.inc' %] >+ >+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a> › <a href="/cgi-bin/koha/circ/circulation.pl">Checkouts</a> >+<span ng-show="borrower"> › Checking out to {{ borrower.surname }}, {{ borrower.firstname }}</span> >+</div> >+<div id="doc3" class="yui-t2"> >+ >+ <div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b" style="display: none"> >+ >+[% INCLUDE 'members-toolbar.inc' %] >+ >+<!-- INITIAL BLOC : PARAMETERS & BORROWER INFO --> >+<div style="display: none;" id="add_message_form"> >+<form method="post" action="/cgi-bin/koha/circ/add_message.pl" id="message_form" name="message_f"> >+<fieldset id="borrower_messages" class="brief"> >+<legend>Leave a message</legend> >+ <ol> >+ <li> >+ <label for="message_type">Add a message for:</label> >+ <select name="message_type" id="message_type"> >+ <option value="L">Other librarians</option> >+ <option value="B">[% firstname %]</option> >+ </select> >+ </li> >+ [% IF ( canned_bor_notes_loop ) %] >+ <li> >+ <label for="type">Predefined notes: </label> >+ <select name="type" id="type" onchange="this.form.borrower_message.value=this.options[this.selectedIndex].value;"> >+ <option value="">Select note</option> >+ [% FOREACH canned_bor_notes_loo IN canned_bor_notes_loop %] >+ <option value="[% canned_bor_notes_loo.lib %]">[% canned_bor_notes_loo.lib %]</option> >+ [% END %] >+ </select> >+ </li> >+ [% END %] >+ <li> >+ <textarea rows="3" cols="60" name="borrower_message" id="borrower_message" ></textarea> >+ </li> >+ </ol> >+ <fieldset class="action"> >+ <input type="submit" value="Save" /> <a href="#" class="cancel">Cancel</a> >+ </fieldset> >+ >+ <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" /> >+ <input type="hidden" name="branchcode" value="[% branch %]" /> >+</fieldset> >+</form> >+</div> >+ >+<div class="yui-g" ng-show="errors | isNotEmpty"> >+<div id="circ_impossible" class="dialog alert"> >+<!-- RESULT OF ISSUING REQUEST --> >+ <ul> >+ >+ <li ng-show="errors.STATS">Local use recorded</li> >+ >+ <li ng-show="errors.INVALID_DATE">The due date "{{errors.INVALID_DATE}}" is invalid</li> >+ >+ <li ng-show="errors.UNKNOWN_BARCODE"> >+ The barcode "{{last_barcode}}" was not found >+ >+ <div ng-show="errors.fallback_choices"> >+ The following items were found by searching: >+ <table> >+ <thead> >+ <th scope="col">Title</th> >+ <th scope="col">Author</th> >+ <th scope="col">Barcode</th> >+ <th scope="col">Status</th> >+ <th scope="col"> </th> >+ </thead> >+ <tbody> >+ <tr ng-repeat="choice in errors.fallback_choices"> >+ <td>{{ choice.title }}</td> >+ <td>{{ choice.author }}</td> >+ <td>{{ choice.barcode }}</td> >+ <td> >+ <span ng-hide="choice.available"> >+ Available >+ </span> >+ <span ng-show="choice.available"> >+ Not Available >+ </span> >+ </td> >+ <td><button ng-click="checkout(choice.barcode)">Check Out</button></td> >+ </tr> >+ </tbody> >+ </table> >+ </div> >+ </li> >+ >+ <li ng-show="errors.NOT_FOR_LOAN"> >+ <span ng-show="errors.itemtype_notforloan">Item type not for loan.</span> >+ >+ <span ng-show="errors.item_notforloan"> >+ Item not for loan >+ >+ <span ng-show="authorised_values['[% authvalcode_notforloan %]'][errors.item_notforloan]"> ({{authorised_values['[% authvalcode_notforloan %]'][errors.item_notforloan]}})</span> >+ </span> >+ </li> >+ >+ <li ng-show="errors.WTHDRAWN">Item has been withdrawn</li> >+ >+ <li ng-show="errors.RESTRICTED">Item is restricted</li> >+ >+ <li ng-show="errors.GNA">Patron's address is in doubt</li> >+ >+ <li ng-show="errors.CARD_LOST">Patron's card is lost</li> >+ >+ <li ng-show="errors.DERBARRED">Patron is restricted</li> >+ >+ <li ng-show="errors.NO_MORE_RENEWALS">No more renewals possible</li> >+ >+ <li ng-show="errors.AGE_RESTRICTION">Age restriction {{errors.AGE_RESTRICTION}}.</li> >+ >+ <li ng-show="errors.EXPIRED">Patron's card is expired</li> >+ >+ <li ng-show="errors.TOO_MANY">Too many checked out. {{errors.current_loan_count}} checked out, only {{errors.max_loans_allowed}} are allowed.</li> >+ >+ <li ng-show="errors.ITEMNOTSAMEBRANCH">This item belongs to {{authorised_values.branches[errors.itemhomebranch]}} and cannot be checked out from this location.</li> >+ >+ <li ng-show="errors.USERBLOCKEDREMAINING">Patron has had overdue items and is blocked for {{errors.USERBLOCKEDREMAINING}} day(s).</li> >+ >+ <li ng-show="errors.USERBLOCKEDOVERDUE">Checkouts are BLOCKED because patron has overdue items</li> >+ </ul> >+ >+</div></div> >+<!-- /impossible --> >+ >+<div class="yui-g" ng-show="questions | isNotEmpty"> >+ >+<div id="circ_needsconfirmation" class="dialog alert"> >+[% IF CAN_user_circulate_force_checkout %] >+ <h3>Please confirm checkout</h3> >+[% ELSE %] >+ <h3>Cannot check out</h3> >+[% END %] >+ >+<ul> >+ <li ng-show="questions.AGE_RESTRICTION"> >+ Age restriction {{ questions.AGE_RESTRICTION }}. >+ [% IF CAN_user_circulate_force_checkout %] >+ Check out anyway? >+ [% END %] >+ </li> >+ >+ <li ng-show="questions.DEBT">The patron has a debt of {{ questions.DEBT }}.</li> >+ >+ <li ng-show="questions.RENEW_ISSUE">Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) is currently checked out to this patron. Renew?</li> >+ >+ <li ng-show="questions.RESERVE_WAITING"> >+ Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) has been waiting for >+ <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber={{ questions.resborrowernumber }}">{{ questions.resfirstname }} {{ questions.ressurname }}</a> >+ ({{ questions.rescardnumber }}) at {{ questions.resbranchname }} since {{ questions.reswaitingdate }}. >+ </li> >+ >+ <li ng-show="questions.RESERVED"> >+ Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) has been on hold for >+ <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber={{ questions.resborrowernumber }}">{{ questions.resfirstname }} {{ questions.ressurname }}</a> >+ ({{ questions.rescardnumber }}) at {{ questions.resbranchname }} since {{ questions.resreservedate }}. >+ </li> >+ >+ <li ng-show="questions.ISSUED_TO_ANOTHER">Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) is checked out to <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber={{ questions.issued_borrowernumber }}">{{ questions.issued_firstname }} {{ questions.issued_surname }}</a> ({{ questions.issued_cardnumber }}). >+ [% IF CAN_user_circulate_force_checkout %] >+ Check in and check out? >+ [% END %] >+ </li> >+ >+ <li ng-show="questions.TOO_MANY">Too many checked out. {{questions.current_loan_count}} checked out, only {{questions.max_loans_allowed}} are allowed.</li> >+ >+ <li ng-show="questions.BORRNOTSAMEBRANCH">This patron is from a different library ({{ questions.BORRNOTSAMEBRANCH }}).</li> >+ >+ <li ng-show="questions.PATRON_CANT">This patron can't check out this item per library circulation policy.</li> >+ >+ <li ng-show="questions.NOT_FOR_LOAN_FORCING"> >+ <span ng-show="questions.itemtype_notforloan">Item type is normally not for loan.</span> >+ >+ <span ng-show="questions.item_notforloan"> >+ Item is normally not for loan >+ >+ <span ng-show="authorised_values['[% authvalcode_notforloan %]'][questions.item_notforloan]"> ({{authorised_values['[% authvalcode_notforloan %]'][questions.item_notforloan]}})</span>. >+ </span> >+ >+ [% IF CAN_user_circulate_force_checkout %] >+ Check out anyway? >+ [% END %] >+ </li> >+ >+ <li ng-show="questions.USERBLOCKEDOVERDUE"> >+ Patron has {{ questions.USERBLOCKEDOVERDUE }} overdue item(s). >+ [% IF CAN_user_circulate_force_checkout %] >+ Check out anyway? >+ [% END %] >+ </li> >+ >+ <li ng-show="questions.ITEM_LOST"> >+ This item has been lost with a status of "{{ questions.ITEM_LOST }}". >+ [% IF CAN_user_circulate_force_checkout %] >+ Check out anyway? >+ [% END %] >+ </li> >+ >+ <li ng-show="questions.HIGHHOLDS">High demand item. Loan period shortened to {{ questions.HIGHHOLDS.duration }} days (due {{ questions.HIGHHOLDS.returndate }}). Check out anyway?</li> >+ >+ <li ng-show="questions.BIBLIO_ALREADY_ISSUED"> >+ Patron has already checked out another item from this record. >+ [% IF CAN_user_circulate_force_checkout %] >+ Check out anyway? >+ [% END %] >+ </li> >+</ul> >+ >+[% IF CAN_user_circulate_force_checkout %] >+<form autocomplete="off"> >+[% ELSE %] >+<form autocomplete="off" ng-show="questions.HIGHHOLDS"> >+[% END %] >+ >+ <p ng-show="questions.RESERVED"> >+ <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" ng-model="cancelreserve"/> >+ <label for="cancelreserve">Cancel hold</label> >+ </p> >+ >+ <p ng-show="questions.RESERVE_WAITING"> >+ <label for="cancelreserve">Cancel hold</label> >+ <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" ng-model="cancelreserve"/><br /> >+ <label for="revertreserve">Revert waiting status</label> >+ <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked" ng-model="cancelreserve"/> >+ </p> >+ >+ <p ng-show="questions.INVALID_DATE"> >+ <input type="text" size="13" id="confduedatespec" name="duedatespec" readonly="readonly" ng-model="duedatespec" /> >+ <label for="confduedatespec">Due date</label> >+ </p> >+ >+ <input type="submit" class="approve" value="Yes, Renew (Y)" accesskey="y" ng-show="questions.RENEW_ISSUE" ng-click="checkout( last_barcode, true )" /> >+ >+ <input type="submit" class="approve" value="Yes, Check Out (Y)" accesskey="y" ng-hide="questions.RENEW_ISSUE" ng-click="checkout( last_barcode, true )" /> >+ >+ <input type="submit" class="deny" value="No, Don't Renew (N)" accesskey="n" ng-show="questions.RENEW_ISSUE" ng-click="cancelCheckout()" /> >+ >+ <input type="submit" class="deny" value="No, Don't Check Out (N)" accesskey="n" ng-hide="questions.RENEW_ISSUE" ng-click="cancelCheckout()" /> >+ >+</form> >+ >+[% UNLESS CAN_user_circulate_force_checkout %] >+<form ng-hide="questions.HIGHHOLDS"> >+ <input type="submit" class="deny" value="Continue" ng-click="cancelCheckout()" /> >+</form> >+[% END %] >+ >+</div></div> >+ >+[% IF ( was_renewed ) %]<div class="dialog message">Patron's account has been renewed until [% expiry %]</div>[% END %] >+ >+[% IF additional_materials %] >+ <div id="materials" class="dialog message">Note about the accompanying materials: <br /> >+ [% additional_materials %] >+ </div> >+[% END %] >+ >+[% IF ( alert.ITEM_LOST ) %] >+ <div class="dialog message">This item has been lost with a status of "[% alert.ITEM_LOST %]".</div> >+[% END %] >+ >+[% IF ( alert.OTHER_CHARGES ) %] >+ <div class="dialog message">The patron has unpaid charges for reserves, rentals etc of [% alert.OTHER_CHARGES %]</div> >+[% END %] >+ >+[% IF ( issued ) %] >+<p>Item checked out</p> >+[% END %] >+ >+<!-- BARCODE ENTRY --> >+ >+<div class="yui-g" ng-show="borrower"> >+[% UNLESS ( noissues ) %] >+[% IF ( flagged ) %] >+<div class="yui-u first"> >+[% ELSE %] >+<div> >+ >+[% END %] >+ >+<form method="post" id="mainform" name="mainform" autocomplete="off" ng-submit="checkout()"> >+<fieldset id="circ_circulation_issue"> >+ [% IF ( DisplayClearScreenButton ) %] >+ <span id="clearscreen"><a href="/cgi-bin/koha/circ/circulation.pl" title="Clear screen">x</a></span> >+ [% END %] >+ >+ <label for="barcode">Checking out to {{ borrower.surname }}, {{ borrower.firstname }} ({{ borrower.cardnumber }})</label> >+ >+ <div class="hint">Enter item barcode:</div> >+ >+ <input type="text" name="barcode" id="barcode" class="barcode focus" size="14" ng-attr-disabled="{{questions? 'disabled':''}}" ng-model="barcode"/> >+ >+ <input type="submit" value="Check Out" /> >+ >+ [% IF ( Koha.Preference( 'SpecifyDueDate' ) ) %]<div class="date-select"> >+ <div class="hint">Specify due date [% INCLUDE 'date-format.inc' %]: </div> >+ <input type="text" size="13" id="duedatespec" name="duedatespec" readonly="readonly" ng-model="duedatespec" /> >+ <label for="stickyduedate"> Remember for session:</label> >+ <input type="checkbox" id="stickyduedate" onclick="this.form.barcode.focus();" name="stickyduedate" ng-model="stickyduedate"/> >+ <input type="button" class="action" id="cleardate" value="Clear" name="cleardate" onclick="this.checked = false; this.form.duedatespec.value = ''; this.form.stickyduedate.checked = false; this.form.barcode.focus(); return false;" /> >+</div>[% END %] >+</fieldset> >+</form></div>[% END %]<!-- /unless noissues --> >+ >+[% IF ( noissues ) %]<div>[% ELSE %]<div class="yui-u">[% END %] >+ >+ [% IF ( flagged ) %] >+ [% IF ( noissues ) %] >+ <h4>Checking out to [% INCLUDE 'patron-title.inc' %]</h4> >+ <div id="circmessages" class="circmessage warning"> >+ [% ELSE %] >+ <div id="circmessages" class="circmessage attention"> >+ [% END %] >+ >+ <h3>[% IF ( noissues ) %] >+ Cannot check out! >+ [% ELSE %]Attention:[% END %]</h3> >+ <ul> >+ >+ [% IF ( warndeparture ) %] >+ <li><span class="circ-hlt">Expiration:</span> Patron's card will expire soon. >+ Patron's card expires on [% expiry %] <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% borrowernumber %]&cardnumber=[% cardnumber %]&destination=circ&reregistration=y">Renew</a> or <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% borrowernumber %]&categorycode=[% categorycode %]">Edit Details</a> >+ >+ </li> >+ [% END %] >+ >+ [% IF ( returnbeforeexpiry ) %] >+ <li><span class="circ-hlt">Set due date to expiry:</span> You have the ReturnBeforeExpiry system preference enabled this means if the >+ expiry date is before the date due, the date due will be set to the expiry date >+ </li> >+ [% END %] >+ >+ [% IF ( expired ) %] >+ <li><span class="circ-hlt">Expiration:</span> Patron's card has expired. >+ [% IF ( expiry ) %]Patron's card expired on [% expiry %][% END %] <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% borrowernumber %]&cardnumber=[% cardnumber %]&destination=circ&reregistration=y">Renew</a> or <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% borrowernumber %]&categorycode=[% categorycode %]">Edit Details</a> >+ >+ </li> >+ [% END %] >+ >+ [% IF ( gna ) %] >+ <li class="blocker"><span class="circ-hlt">Address:</span> Patron's address in doubt</li> >+ [% END %] >+ >+ [% IF ( lost ) %] >+ <li class="blocker"><span class="circ-hlt">Lost: </span>Patron's card is lost</li> >+ [% END %] >+ >+ [% IF ( userdebarred ) %] >+ <li class="blocker"> >+ <span class="circ-hlt"> Restricted:</span> Patron's account is restricted >+ >+ [% IF ( userdebarreddate ) %] >+ until [% userdebarreddate %] >+ [% END %] >+ >+ [% IF ( debarredcomment ) %] >+ with the explanation: <br/><i>[% debarredcomment | html_line_break %]</i> >+ [% END %] >+ >+ <br/> >+ <a class="btn btn-small" href="#reldebarments" onclick="$('#debarments-tab-link').click()"><i class="icon-ban-circle"></i> View restrictions</a> >+ </li> >+ [% END %] >+ >+ [% IF ( odues ) %]<li>[% IF ( nonreturns ) %]<span class="circ-hlt">Overdues:</span> Patron has <span class="circ-hlt">ITEMS OVERDUE</span>. See highlighted items <a href="#checkouts">below</a>[% END %]</li> >+ [% END %] >+ >+ [% IF ( charges ) %] >+ <li> >+ <span class="circ-hlt">Fees & Charges:</span> Patron has <a href="/cgi-bin/koha/members/boraccount.pl?borrowernumber=[% borrowernumber %]">Outstanding fees & charges[% IF ( chargesamount ) %] of [% chargesamount %][% END %]</a>. >+ [% IF ( charges_is_blocker ) %] >+ Checkouts are <span class="circ-hlt">BLOCKED</span> because fine balance is <span class="circ-hlt">OVER THE LIMIT</span>. >+ [% END %] >+ <a href="/cgi-bin/koha/members/pay.pl?borrowernumber=[% borrowernumber %]">Make payment</a></li> >+ [% END %] >+ >+ [% IF ( credits ) %] >+ <li> >+ <span class="circ-hlt">Credits:</span> Patron has a credit[% IF ( creditsamount ) %] of [% creditsamount %][% END %] >+ </li> >+ [% END %] >+ >+ >+ >+ </ul> >+ </div> >+ >+ [% IF ( WaitingReserveLoop ) %] >+ <div id="holdswaiting" class="circmessage"> >+ <h4>Holds waiting:</h4> >+ [% FOREACH WaitingReserveLoo IN WaitingReserveLoop %] >+ <ul> >+ <li> <a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% WaitingReserveLoo.biblionumber %]">[% WaitingReserveLoo.title |html %]</a> ([% WaitingReserveLoo.itemtype %]), [% IF ( WaitingReserveLoo.author ) %]by [% WaitingReserveLoo.author %][% END %] [% IF ( WaitingReserveLoo.itemcallnumber ) %][[% WaitingReserveLoo.itemcallnumber %]] [% END %]Hold placed on [% WaitingReserveLoo.reservedate %]. >+ [% IF ( WaitingReserveLoo.waitingat ) %] >+ <br />[% IF ( WaitingReserveLoo.waitinghere ) %]<strong class="waitinghere">[% ELSE %]<strong>[% END %]Waiting at [% WaitingReserveLoo.waitingat %]</strong> >+ [% END %] >+ </li> >+ </ul> >+ [% END %] >+ </div> >+ <!-- /If WaitingReserveLoop -->[% END %] >+ [% IF ( notes ) %] >+ <div id="circnotes" class="circmessage"> >+ <h4>Notes:</h4> >+ <p><span class="circ-hlt">[% notesmsg %]</span></p> >+ </div> >+ >+ >+ <!-- /If notes -->[% END %] >+ >+ <div id="messages" class="circmessage"> >+ <h4>Messages:</h4> >+ <ul> >+ [% FOREACH lib_messages_loo IN lib_messages_loop %] >+ <li> >+ <span class="circ-hlt"> >+ [% lib_messages_loo.message_date_formatted %] >+ [% lib_messages_loo.branchcode %] >+ <i>"[% lib_messages_loo.message %]"</i> >+ </span> >+ [% IF ( lib_messages_loo.can_delete ) %] >+ <a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% lib_messages_loo.message_id %]&borrowernumber=[% lib_messages_loo.borrowernumber %]">[Delete]</a> >+ [% ELSE %] >+ [% IF ( all_messages_del ) %] >+ <a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% lib_messages_loo.message_id %]&borrowernumber=[% lib_messages_loo.borrowernumber %]">[Delete]</a> >+ [% END %] >+ [% END %] >+ </li> >+ [% END %] >+ [% FOREACH bor_messages_loo IN bor_messages_loop %] >+ <li><span class="">[% bor_messages_loo.message_date_formatted %] [% bor_messages_loo.branchcode %] <i>"[% bor_messages_loo.message %]"</i></span> [% IF ( bor_messages_loo.can_delete ) %]<a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% bor_messages_loo.message_id %]&borrowernumber=[% bor_messages_loo.borrowernumber %]">[Delete]</a> >+ [% ELSIF ( all_messages_del ) %] >+ <a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% bor_messages_loo.message_id %]&borrowernumber=[% bor_messages_loo.borrowernumber %]">[Delete]</a> >+ [% END %]</li> >+ [% END %] >+ >+ </ul> >+ </div> >+ >+ <!-- /If flagged -->[% END %] >+ >+ >+ >+</div> >+</div> >+ >+<div class="yui-g" ng-show="borrower"><div id="patronlists" class="toptabs"> >+ >+<ul> >+<li> <a href="#checkouts">{{ checkouts.length }} Checkout(s)</a></li> >+[% IF ( displayrelissues ) %] >+<li><a href="#relissues">Relatives' checkouts</a></li> >+[% END %] >+<li>[% IF ( countreserv ) %] >+ <a href="#reserves">[% countreserv %] Hold(s)</a> >+ [% ELSE %] >+ <a href="#reserves">0 Holds</a> >+ [% END %]</li> >+ <li><a id="debarments-tab-link" href="#reldebarments">[% debarments.size %] Restrictions</a></li> >+ >+</ul> >+ >+<!-- SUMMARY : TODAY & PREVIOUS ISSUES --> >+<div id="checkouts"> >+ >+<form name="issues" action="/cgi-bin/koha/reserve/renewscript.pl" method="post" class="checkboxed" ng-show="checkouts.length"> >+ <input type="hidden" value="circ" name="destination" /> >+ <input type="hidden" name="cardnumber" value="[% cardnumber %]" /> >+ <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" /> >+ <input type="hidden" name="branch" value="[% branch %]" /> >+ <table id="issuest"> >+ <thead><tr> >+ <th scope="col" class="title-string">Due date</th> >+ <th scope="col" class="anti-the">Title</th> >+ <th scope="col">Item type</th> >+ <th scope="col" class="title-string">Checked out on</th> >+ <th scope="col">Checked out from</th> >+ <th scope="col">Call no</th> >+ <th scope="col">Charge</th> >+ <th scope="col">Price</th> >+ <th scope="col">Renew <p class="column-tool"><a href="#" id="CheckAllitems">select all</a> | <a href="#" id="CheckNoitems">none</a></p></th> >+ <th scope="col">Check in <p class="column-tool"><a href="#" id="CheckAllreturns">select all</a> | <a href="#" id="CheckNoreturns">none</a></p></th> >+ [% IF ( exports_enabled ) %] >+ <th scope="col">Export <p class="column-tool"><a href="#" id="CheckAllexports">select all</a> | <a href="#" id="CheckNoexports">none</a></p></th> >+ [% END %] >+ </tr></thead> >+[% INCLUDE 'checkouts-table-footer.inc' %] >+ <tbody> >+ >+ <tr ng-repeat="checkout in checkouts" class="{{ $odd ? 'highlight' : '' }}"> >+ [% IF ( todayissue.od ) %]<td class="od">[% ELSE %]<td>[% END %] >+ <span title="{{ checkout.date_due }}">{{ checkout.date_due }}</span> >+ </td> >+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber={{ checkout.biblionumber }}&type=intra"><strong>{{ checkout.title }}</strong></a><span ng-show="checkout.author">, by {{ checkout.author }}</span><span class="circ-hlt" ng-show="checkout.itemnotes"> - {{ checkout.itemnotes }}</span> <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber={{ checkout.biblionumber }}&itemnumber={{ checkout.itemnumber }}#item{{ checkout.itemnumber }}">{{ checkout.barcode }}</a></td> >+ <td>[% UNLESS ( noItemTypeImages ) %] [% IF ( todayissue.itemtype_image ) %]<img src="{{ checkout.itemtype_image }}" alt="" />[% END %][% END %]{{ checkout.itemtype }}</td> >+ <td><span title="{{ checkout.displaydate_sort }}">{{ checkout.issuedate }}</span></td> >+ <td>{{ checkout.issuingbranchname }}</td> >+ <td>{{ checkout.itemcallnumber }}</td> >+ <td>{{ checkout.charge }}</td> >+ <td>{{ checkout.replacementprice }}</td> >+ <td><span style="padding: 0 1em;">{{ checkout.renewals || 0 }}</span> >+ <td><input type="checkbox" class="radio" name="barcodes[]" value="{{ checkout.barcode }}" /> >+ <input type="checkbox" name="all_barcodes[]" value="{{ checkout.barcode }}" checked="checked" style="display: none;" /> >+ </td> >+ [% IF ( exports_enabled ) %] >+ <td style="text-align:center;"> >+ <input type="checkbox" id="export_{{ checkout.biblionumber }}" name="biblionumbers" value="{{ checkout.biblionumber }}" /> >+ <input type="checkbox" name="itemnumbers" value="{{ checkout.itemnumber }}" style="visibility:hidden;" /> >+ </td> >+ [% END %] >+ </tr> >+ >+[% IF ( previssues ) %] >+ [% UNLESS ( todayissues ) %] >+ [% INCLUDE 'checkouts-table-footer.inc' %] >+ <tbody> >+ [% END %] >+ [% IF ( UseTablesortForCirc ) %]<tr id="previous"><th><span title="">Previous checkouts</span></th><th></th><th></th><th><span title=""></span></th><th></th><th></th><th></th><th></th><th></th><th></th>[% IF ( exports_enabled ) %]<th></th>[% END %]</tr>[% ELSE %]<tr id="previous">[% IF ( exports_enabled ) %]<th colspan="11">[% ELSE %]<th colspan="10">[% END %]Previous checkouts</th></tr>[% END %] >+ [% FOREACH previssue IN previssues %] >+ [% IF ( loop.odd ) %] >+ <tr> >+ [% ELSE %] >+ <tr class="highlight"> >+ [% END %] >+ [% IF ( previssue.od ) %]<td class="od">[% ELSE %]<td>[% END %] >+ <span title="[% previssue.dd_sort %]">[% previssue.dd %]</span> >+ >+ [% IF ( previssue.itemlost ) %] >+ <span class="lost">[% AuthorisedValues.GetByCode( 'LOST', previssue.itemlost ) %]</span> >+ [% END %] >+ [% IF ( previssue.damaged ) %] >+ <span class="dmg">[% AuthorisedValues.GetByCode( 'DAMAGED', previssue.damaged ) %]</span> >+ [% END %] >+ </td> >+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% previssue.biblionumber %]&type=intra"><strong>[% previssue.title |html %][% FOREACH subtitl IN previssue.subtitle %] [% subtitl.subfield %][% END %]</strong></a>[% IF ( previssue.author ) %], by [% previssue.author %][% END %] [% IF ( previssue.itemnotes ) %]- [% previssue.itemnotes %][% END %] <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% previssue.biblionumber %]&itemnumber=[% previssue.itemnumber %]#item[% previssue.itemnumber %]">[% previssue.barcode %]</a></td> >+ <td> >+ [% previssue.itemtype %] >+ </td> >+ <td><span title="[% previssue.displaydate_sort %]">[% previssue.displaydate %]</span></td> >+ [% IF ( previssue.multiple_borrowers ) %]<td>[% previssue.firstname %] [% previssue.surname %]</td>[% END %] >+ <td>[% previssue.issuingbranchname %]</td> >+ <td>[% previssue.itemcallnumber %]</td> >+ <td>[% previssue.charge %]</td> >+ <td>[% previssue.replacementprice %]</td> >+ [% IF ( previssue.renew_failed ) %] >+ <td class="problem">Renewal failed</td> >+ [% ELSE %] >+ <td><span style="padding: 0 1em;">[% IF ( previssue.renewals ) %][% previssue.renewals %][% ELSE %]0[% END %]</span> >+ [% IF ( previssue.can_renew ) %] >+ <input type="checkbox" name="all_items[]" value="[% previssue.itemnumber %]" checked="checked" style="display: none;" /> >+ [% IF ( previssue.od ) %] >+ <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" checked="checked" /> >+ [% ELSE %] >+ <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" /> >+ [% END %] >+ [% IF previssue.renewsallowed && previssue.renewsleft %] >+ <span class="renewals">([% previssue.renewsleft %] of [% previssue.renewsallowed %] renewals remaining)</span> >+ [% END %] >+ [% ELSE %] >+ [% IF ( previssue.can_confirm ) %]<span class="renewals-allowed" style="display: none"> >+ <input type="checkbox" name="all_items[]" value="[% previssue.itemnumber %]" checked="checked" style="display: none;" /> >+ [% IF ( previssue.od ) %] >+ <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" checked="checked" /> >+ [% ELSE %] >+ <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" /> >+ [% END %] >+ </span> >+ [% IF previssue.renewsallowed && previssue.renewsleft && !previssue.renew_error_too_soon %] >+ <span class="renewals">([% previssue.renewsleft %] of [% previssue.renewsallowed %] renewals remaining)</span> >+ [% END %] >+ <span class="renewals-disabled"> >+ [% END %] >+ [% IF ( previssue.renew_error_on_reserve ) %] >+ <a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% previssue.biblionumber %]">On Hold</a> >+ [% ELSIF ( previssue.renew_error_too_many ) %] >+ Not renewable >+ [% ELSIF ( previssue.renew_error_too_soon ) %] >+ No renewal before [% previssue.soonestrenewdate %] >+ <span class="renewals">([% previssue.renewsleft %] of [% previssue.renewsallowed %] renewals remaining)</span> >+ [% END %] >+ [% IF ( previssue.can_confirm ) %] >+ </span> >+ [% END %] >+ [% END %] >+ </td> >+ [% END %] >+ [% IF ( previssue.return_failed ) %] >+ <td class="problem">Check-in failed</td> >+ [% ELSE %] >+ [% IF ( previssue.renew_error_on_reserve ) %] >+ <td><a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% previssue.biblionumber %]">On hold</a> >+ <input type="checkbox" name="all_barcodes[]" value="[% previssue.barcode %]" checked="checked" style="display: none;" /> >+ </td> >+ [% ELSE %] >+ <td><input type="checkbox" class="radio" name="barcodes[]" value="[% previssue.barcode %]" /> >+ <input type="checkbox" name="all_barcodes[]" value="[% previssue.barcode %]" checked="checked" style="display: none;" /> >+ </td> >+ [% END %] >+ [% END %] >+ [% IF ( exports_enabled ) %] >+ <td style="text-align:center;"> >+ <input type="checkbox" id="export_[% previssue.biblionumber %]" name="biblionumbers" value="[% previssue.biblionumber %]" /> >+ <input type="checkbox" name="itemnumbers" value="[% previssue.itemnumber %]" style="visibility:hidden;" /> >+ </td> >+ [% END %] >+ </tr> >+ <!-- /loop previssues -->[% END %] >+<!--/if previssues -->[% END %] >+ </tbody> >+ </table> >+ [% IF ( issuecount ) %] >+ <fieldset class="action"> >+ [% IF ( CAN_user_circulate_override_renewals ) %] >+ [% IF ( AllowRenewalLimitOverride ) %] >+ <label for="override_limit">Override renewal limit:</label> >+ <input type="checkbox" name="override_limit" id="override_limit" value="1" /> >+ [% END %] >+ [% END %] >+ <input type="submit" name="renew_checked" value="Renew or Return checked items" /> >+ <input type="submit" id="renew_all" name="renew_all" value="Renew all" /> >+ </fieldset> >+ [% IF ( exports_enabled ) %] >+ <fieldset> >+ <label for="export_formats"><b>Export checkouts using format:</b></label> >+ <select name="export_formats" id="export_formats"> >+ <option value="iso2709_995">ISO2709 with items</option> >+ <option value="iso2709">ISO2709 without items</option> >+ [% IF ( export_with_csv_profile ) %] >+ <option value="csv">CSV</option> >+ [% END %] >+ >+ </select> >+ <label for="export_remove_fields">Don't export fields:</label> <input type="text" id="export_remove_fields" name="export_remove_fields" value="[% export_remove_fields %]" title="Use for iso2709 exports" /> >+ <input type="hidden" name="op" value="export" /> >+ <input type="hidden" id="export_format" name="format" value="iso2709" /> >+ <input type="hidden" id="dont_export_item" name="dont_export_item" value="0" /> >+ <input type="hidden" id="record_type" name="record_type" value="bibs" /> >+ <input type="button" id="export_submit" value="Export" /> >+ </fieldset> >+ [% END %] >+ [% END %] >+</form> >+<p ng-hide="checkouts.length">Patron has nothing checked out.</p> >+ >+</div> >+ >+ >+[% IF ( displayrelissues ) %] >+<div id="relissues"> >+ <table id="relissuest"> >+ <thead> >+ <tr> >+ <th scope="col" class="title-string">Due date</th> >+ <th scope="col" class="anti-the">Title</th> >+ <th scope="col">Item type</th> >+ <th scope="col" class="title-string">Checked out on</th> >+ <th scope="col">Checked out from</th> >+ <th scope="col">Call no</th> >+ <th scope="col">Charge</th> >+ <th scope="col">Price</th> >+ <th scope="col" class="html-content">Patron</th> >+ </tr> >+ </thead> >+[% IF ( relissues ) %] <tbody> >+ >+ [% FOREACH relissue IN relissues %] >+ [% IF ( loop.odd ) %] >+ <tr> >+ [% ELSE %] >+ <tr class="highlight"> >+ [% END %] >+ [% IF ( relissue.overdue ) %]<td class="od">[% ELSE %]<td>[% END %] >+ <span title="[% relissue.dd_sort %]">[% relissue.dd %]</span></td> >+ >+ [% IF ( relissue.itemlost ) %] >+ <span class="lost">[% AuthorisedValues.GetByCode( 'LOST', relissue.itemlost ) %]</span> >+ [% END %] >+ [% IF ( relissue.damaged ) %] >+ <span class="dmg">[% AuthorisedValues.GetByCode( 'DAMAGED', relissue.damaged ) %]</span> >+ [% END %] >+ </td> >+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% relissue.biblionumber %]&type=intra"><strong>[% relissue.title |html %][% FOREACH subtitl IN relissue.subtitle %] [% subtitl.subfield %][% END %]</strong></a>[% IF ( relissue.author ) %], by [% relissue.author %][% END %][% IF ( relissue.itemnotes ) %]- <span class="circ-hlt">[% relissue.itemnotes %]</span>[% END %] <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% relissue.biblionumber %]&itemnumber=[% relissue.itemnumber %]#item[% relissue.itemnumber %]">[% relissue.barcode %]</a></td> >+ <td>[% UNLESS ( noItemTypeImages ) %] [% IF ( relissue.itemtype_image ) %]<img src="[% relissue.itemtype_image %]" alt="" />[% END %][% END %][% relissue.itemtype %]</td> >+ <td><span title="[% relissue.displaydate_sort %]">[% relissue.displaydate %]</span></td> >+ <td>[% relissue.issuingbranchname %]</td> >+ <td>[% relissue.itemcallnumber %]</td> >+ <td>[% relissue.charge %]</td> >+ <td>[% relissue.replacementprice %]</td><td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% relissue.borrowernumber %]">[% relissue.firstname %] [% relissue.surname %] ([% relissue.cardnumber %])</a></td> >+ </tr> >+ [% END %] <!-- /loop relissues --> >+ <!-- /if relissues -->[% END %] >+[% IF ( relprevissues ) %] >+ [% IF ( UseTablesortForCirc ) %]<tr id="relprevious"><th><span title="">Previous checkouts</span></th><th></th><th></th><th><span title=""></span></th><th></th><th></th><th></th><th></th><th></th></tr>[% ELSE %]<tr id="relprevious"><th colspan="9">Previous checkouts</th></tr>[% END %] >+ [% FOREACH relprevissue IN relprevissues %] >+ [% IF ( loop.odd ) %] >+ <tr> >+ [% ELSE %] >+ <tr class="highlight"> >+ [% END %] >+ [% IF ( relprevissue.overdue ) %]<td class="od">[% ELSE %]<td>[% END %] >+ <span title="[% relprevissue.dd_sort %]">[% relprevissue.dd %]</span> >+ </td> >+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% relprevissue.biblionumber %]&type=intra"><strong>[% relprevissue.title |html %][% FOREACH subtitl IN relprevissue.subtitle %] [% subtitl.subfield %][% END %]</strong></a>[% IF ( relprevissue.author ) %], by [% relprevissue.author %][% END %] [% IF ( relprevissue.itemnotes ) %]- [% relprevissue.itemnotes %][% END %] <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% relprevissue.biblionumber %]&itemnumber=[% relprevissue.itemnumber %]#item[% relprevissue.itemnumber %]">[% relprevissue.barcode %]</a></td> >+ <td>[% UNLESS noItemTypeImages %][% IF relprevissue.itemtype_image %]<img src="[% relprevissue.itemtype_image %]" alt="" />[% END %][% END %][% relprevissue.itemtype %]</td> >+ <td><span title="[% relprevissue.displaydate_sort %]">[% relprevissue.displaydate %]</span></td> >+ <td>[% relprevissue.issuingbranchname %]</td> >+ <td>[% relprevissue.itemcallnumber %]</td> >+ [% IF ( relprevissue.multiple_borrowers ) %]<td>[% relprevissue.firstname %] [% relprevissue.surname %]</td>[% END %] >+ <td>[% relprevissue.charge %]</td> >+ <td>[% relprevissue.replacementprice %]</td> >+ <td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% relprevissue.borrowernumber %]">[% relprevissue.firstname %] [% relprevissue.surname %] ([% relprevissue.cardnumber %])</a></td> >+ >+ </tr> >+ <!-- /loop relprevissue -->[% END %] >+<!--/if relprevissues -->[% END %] >+ </tbody> >+ </table> >+ >+</div> >+[% END %]<!-- end displayrelissues --> >+ >+[% INCLUDE borrower_debarments.inc %] >+ >+<div id="reserves"> >+[% IF ( reservloop ) %] >+ <form action="/cgi-bin/koha/reserve/modrequest.pl" method="post"> >+ <input type="hidden" name="from" value="circ" /> >+ <table id="holdst"> >+ <thead><tr> >+ <th>Hold date</th> >+ <th>Title</th> >+ <th>Call number</th> >+ <th>Barcode</th> >+ <th>Expiration</th> >+ <th>Priority</th> >+ <th>Delete?</th> >+ <th> </th> >+ </tr></thead> >+ <tbody> >+ [% FOREACH reservloo IN reservloop %] >+ <tr class="[% reservloo.color %]"> >+ <td>[% reservloo.reservedate %]</td> >+ <td><a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% reservloo.biblionumber %]"><strong>[% reservloo.title |html %][% FOREACH subtitl IN reservloo.subtitle %] [% subtitl.subfield %][% END %]</strong></a>[% IF ( reservloo.author ) %], by [% reservloo.author %][% END %]</td> >+ <td>[% reservloo.itemcallnumber %]</td> >+ <td><em>[% IF ( reservloo.barcodereserv ) %]Item [% reservloo.barcodereserv %] >+ [% END %][% IF ( reservloo.waiting ) %] <strong>waiting at [% reservloo.waitingat %]</strong> >+ [% END %] >+ [% IF ( reservloo.transfered ) %] <strong>in transit</strong> from >+ [% reservloo.frombranch %] since [% reservloo.datesent %] >+ [% END %] >+ [% IF ( reservloo.nottransfered ) %] hasn't been transferred yet from [% reservloo.nottransferedby %]</i> >+ [% END %]</em></td> >+ <td>[% reservloo.expirationdate | $KohaDates %]</td> >+ <td> >+ [% IF ( reservloo.waitingposition ) %]<b> [% reservloo.waitingposition %] </b>[% END %] >+ </td> >+ <td><select name="rank-request"> >+ <option value="n">No</option> >+ <option value="del">Yes</option> >+ </select> >+ <input type="hidden" name="biblionumber" value="[% reservloo.biblionumber %]" /> >+ <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" /> >+ <input type="hidden" name="reserve_id" value="[% reservloo.reserve_id %]" /> >+ </td> >+ <td>[% IF ( reservloo.suspend ) %]Suspended [% IF ( reservloo.suspend_until ) %] until [% reservloo.suspend_until | $KohaDates %][% END %][% END %]</td> >+ </tr> >+ [% END %]</tbody> >+ </table> >+ <fieldset class="action"><input type="submit" class="cancel" name="submit" value="Cancel marked holds" /></fieldset> >+ </form> >+ >+ [% IF SuspendHoldsIntranet %] >+ <fieldset class="action"> >+ <form action="/cgi-bin/koha/reserve/modrequest_suspendall.pl" method="post"> >+ <input type="hidden" name="from" value="circ" /> >+ <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" /> >+ <input type="submit" value="Suspend all holds" /> >+ >+ [% IF AutoResumeSuspendedHolds %] >+ <label for="suspend_until">until</label> >+ <input type="text" size="10" id="suspend_until" name="suspend_until" class="datepicker" /> >+ <span class="hint">Specify date on which to resume [% INCLUDE 'date-format.inc' %]: </span> >+ [% END %] >+ </form> >+ </fieldset> >+ >+ <fieldset class="action"> >+ <form action="/cgi-bin/koha/reserve/modrequest_suspendall.pl" method="post"> >+ <input type="hidden" name="from" value="circ" /> >+ <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" /> >+ <input type="hidden" name="suspend" value="0" /> >+ <input type="submit" value="Resume all suspended holds" /> >+ </form> >+ </fieldset> >+ [% END # IF SuspendHoldsIntranet %] >+ >+[% ELSE %] >+ <p>Patron has nothing on hold.</p> >+[% END %] >+</div> <!-- reservesloop --> >+ >+</div></div> >+ >+ >+ >+</div> >+</div> >+<div class="yui-b"> >+[% INCLUDE 'circ-menu.inc' %] >+</div> >+</div> >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/svc/patrons b/svc/patrons >new file mode 100755 >index 0000000..2777989 >--- /dev/null >+++ b/svc/patrons >@@ -0,0 +1,23 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha. >+# >+# Copyright (C) 2014 ByWater Solutions >+# >+# 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 Koha::Service::Patrons; >+Koha::Service::Patrons->new->run; >diff --git a/t/db_dependent/Members.t b/t/db_dependent/Members.t >index 61ab9a2..822b423 100755 >--- a/t/db_dependent/Members.t >+++ b/t/db_dependent/Members.t >@@ -17,8 +17,9 @@ > > use Modern::Perl; > >-use Test::More tests => 72; >+use Test::More tests => 73; > use Test::MockModule; >+use Koha::Service::Patrons; > use Data::Dumper; > use C4::Context; > >@@ -113,6 +114,21 @@ ok ( $member->{firstname} eq $FIRSTNAME && > > is($member->{dateofbirth}, undef, "Empty dates handled correctly"); > >+my $service = Koha::Service::Patrons->new; >+my $borrowernumber = $member->{borrowernumber}; >+$service->test( "GET", "/$borrowernumber/patronInfo" ); >+my $jsonresponse = $service->dispatch->{patronInfo}; >+ >+ok ( $jsonresponse->{firstname} eq $FIRSTNAME && >+ $jsonresponse->{surname} eq $SURNAME && >+ $jsonresponse->{categorycode} eq $CATEGORYCODE && >+ $jsonresponse->{branchcode} eq $BRANCHCODE >+ , "Got member") >+ or diag("Mismatching member details: ".Dumper(\%data, $jsonresponse)); >+ >+C4::Context->_unset_userenv; >+C4::Context->set_userenv ( @USERENV ); >+ > $member->{firstname} = $CHANGED_FIRSTNAME; > $member->{email} = $EMAIL; > $member->{ethnicity} = $ETHNICITY; >diff --git a/t/db_dependent/Reserves.t b/t/db_dependent/Reserves.t >index 4676a60..39e36f6 100755 >--- a/t/db_dependent/Reserves.t >+++ b/t/db_dependent/Reserves.t >@@ -17,7 +17,7 @@ > > use Modern::Perl; > >-use Test::More tests => 53; >+use Test::More tests => 55; > > use MARC::Record; > use DateTime::Duration; >@@ -27,15 +27,20 @@ use C4::Biblio; > use C4::Items; > use C4::Members; > use C4::Circulation; >+use JSON; > use t::lib::Mocks; >+use Test::WWW::Mechanize; > > use Koha::DateUtils; > > use Data::Dumper; > BEGIN { > use_ok('C4::Reserves'); >+ use_ok('Koha::Service::Patrons'); > } > >+ >+ > # a very minimal mack of userenv for use by the test of DelItemCheck > *C4::Context::userenv = sub { > return {}; >@@ -127,6 +132,15 @@ is($status, "Reserved", "CheckReserves Test 2"); > ($status, $reserve, $all_reserves) = CheckReserves(undef, $barcode); > is($status, "Reserved", "CheckReserves Test 3"); > >+my $service = Koha::Service::Patrons->new; >+$service->test( "GET", "/$borrowernumber/holds" ); >+my $jsonresponse = $service->dispatch; >+ >+ok( >+ ref($jsonresponse->{holds}) eq "ARRAY" && scalar @{ $jsonresponse->{holds} } == 1, >+ "checks to make sure that API returns one hold" >+); >+ > my $ReservesControlBranch = C4::Context->preference('ReservesControlBranch'); > C4::Context->set_preference( 'ReservesControlBranch', 'ItemHomeLibrary' ); > ok( >-- >2.1.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 13630
: 36262