View | Details | Raw Unified | Return to bug 13630
Collapse All | Expand All

(-)a/Koha/Service.pm (-8 / +58 lines)
Lines 59-66 use Modern::Perl; Link Here
59
use base 'Class::Accessor';
59
use base 'Class::Accessor';
60
60
61
use C4::Auth qw( check_api_auth );
61
use C4::Auth qw( check_api_auth );
62
use C4::Context;
62
use C4::Output qw( :ajax );
63
use C4::Output qw( :ajax );
63
use CGI;
64
use CGI;
65
use DateTime;
64
use JSON;
66
use JSON;
65
67
66
our $debug;
68
our $debug;
Lines 109-114 sub new { Link Here
109
    }, $class;
111
    }, $class;
110
}
112
}
111
113
114
=head2 test
115
116
    $service->test( $request_method, $path_info, \%params );
117
118
Sets up a fake CGI context for unit tests.
119
120
=cut
121
122
sub test {
123
    my ( $self, $request_method, $path_info, $params ) = @_;
124
125
    $ENV{REQUEST_METHOD} = $request_method;
126
    $ENV{PATH_INFO} = $path_info;
127
    $ENV{HTTP_CONTENT_LENGTH} = "0";
128
    $self->query(CGI->new);
129
130
    foreach my $key ( keys %$params ) {
131
        $self->query->param( $key, $params->{ $key } );
132
    }
133
134
    my $user     = $ENV{KOHA_USER} || C4::Context->config("user");
135
    my $password = $ENV{KOHA_PASS} || C4::Context->config("pass");
136
137
    $self->query->param( 'userid', $user );
138
    $self->query->param( 'password', $password );
139
140
    $self->authenticate;
141
}
142
112
=head2 authenticate
143
=head2 authenticate
113
144
114
    my ( $query, $cookie ) = $self->authenticate();
145
    my ( $query, $cookie ) = $self->authenticate();
Lines 123-136 This must be called before the C<croak> or C<output> methods. Link Here
123
sub authenticate {
154
sub authenticate {
124
    my ( $self ) = @_;
155
    my ( $self ) = @_;
125
156
126
    $self->query(CGI->new);
157
    unless ( defined( $self->auth_status ) ) {
158
        $self->query(CGI->new) unless ( $self->query );
127
159
128
    my ( $status, $cookie, $sessionID ) = check_api_auth( $self->query, $self->{needed_flags} );
160
        my ( $status, $cookie, $sessionID ) = check_api_auth( $self->query, $self->{needed_flags} );
129
    $self->cookie($cookie);
161
        $self->cookie($cookie);
130
    $self->auth_status($status);
162
        $self->auth_status($status);
131
    $self->croak( 'auth', $status ) if ( $status ne 'ok' && !$self->{authnotrequired} );
163
        $self->handle_auth_failure() if ( $status ne 'ok' );
164
    }
132
165
133
    return ( $self->query, $cookie );
166
    return ( $self->query, $self->cookie );
167
}
168
169
=head2 handle_auth_failure
170
171
    $self->handle_auth_failure();
172
173
Called when C<authenticate> fails (C<$self->auth_status> not 'ok'). By default, if
174
C<$self->{authnotrequired}> is not set, croaks and outputs an auth error.
175
176
=cut
177
178
sub handle_auth_failure {
179
    my ( $self ) = @_;
180
181
    $self->croak( 'auth', $self->auth_status ) if ( !$self->{authnotrequired} );
134
}
182
}
135
183
136
=head2 output
184
=head2 output
Lines 157-162 is given, outputs JSONP. Link Here
157
205
158
=cut
206
=cut
159
207
208
*DateTime::TO_JSON = sub { shift->_stringify; };
209
160
sub output {
210
sub output {
161
    my ( $self, $response, $options ) = @_;
211
    my ( $self, $response, $options ) = @_;
162
212
Lines 170-179 sub output { Link Here
170
    };
220
    };
171
221
172
    if ( $options->{type} eq 'json' ) {
222
    if ( $options->{type} eq 'json' ) {
173
        $response = encode_json($response);
223
        $response = JSON->new->convert_blessed->encode($response);
174
224
175
        if ( $self->query->param( 'callback' ) ) {
225
        if ( $self->query->param( 'callback' ) ) {
176
            $response = $self->query->param( 'callback' ) . '(' . encode_json($response) . ');';
226
            $response = $self->query->param( 'callback' ) . '(' . $response . ');';
177
            $options->{status} = '200 OK';
227
            $options->{status} = '200 OK';
178
            $options->{type} = 'js';
228
            $options->{type} = 'js';
179
        }
229
        }
(-)a/Koha/Service/Authentication.pm (-2 / +6 lines)
Lines 20-25 package Koha::Service::Authentication; Link Here
20
20
21
use Modern::Perl;
21
use Modern::Perl;
22
22
23
# Handles authentication and output manually, so no reason to inherit from Koha::Service::XML
23
use base 'Koha::Service';
24
use base 'Koha::Service';
24
25
25
use C4::Auth qw/check_api_auth/;
26
use C4::Auth qw/check_api_auth/;
Lines 29-41 use XML::Simple; Link Here
29
sub new {
30
sub new {
30
    my ( $class ) = @_;
31
    my ( $class ) = @_;
31
32
32
    # Authentication is handled manually below
33
    return $class->SUPER::new( {
33
    return $class->SUPER::new( {
34
        authnotrequired => 1,
35
        needed_flags => { editcatalogue => 'edit_catalogue'},
34
        needed_flags => { editcatalogue => 'edit_catalogue'},
36
    } );
35
    } );
37
}
36
}
38
37
38
sub handle_auth_failure {
39
    # Stub, to allow run() to output XML itself.
40
}
41
39
sub run {
42
sub run {
40
    my ( $self ) = @_;
43
    my ( $self ) = @_;
41
    # The authentication strategy for the biblios web
44
    # The authentication strategy for the biblios web
Lines 55-60 sub run { Link Here
55
58
56
    $self->authenticate;
59
    $self->authenticate;
57
60
61
    # Can't reuse Koha::Service::XML, as result node has different name.
58
    $self->output( XMLout({ status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1), { type => 'xml' } );
62
    $self->output( XMLout({ status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1), { type => 'xml' } );
59
}
63
}
60
64
(-)a/Koha/Service/Bib.pm (-20 / +4 lines)
Lines 21-27 package Koha::Service::Bib; Link Here
21
21
22
use Modern::Perl;
22
use Modern::Perl;
23
23
24
use base 'Koha::Service';
24
use base 'Koha::Service::XML';
25
25
26
use C4::Biblio;
26
use C4::Biblio;
27
use C4::Items;
27
use C4::Items;
Lines 30-39 use XML::Simple; Link Here
30
sub new {
30
sub new {
31
    my ( $class ) = @_;
31
    my ( $class ) = @_;
32
32
33
    # Authentication is handled manually below
34
    return $class->SUPER::new( {
33
    return $class->SUPER::new( {
35
        authnotrequired => 1,
34
        needed_flags => { editcatalogue => 'edit_catalogue' },
36
        needed_flags => { editcatalogue => 'edit_catalogue'},
37
        routes => [
35
        routes => [
38
            [ qr'GET /(\d+)', 'fetch_bib' ],
36
            [ qr'GET /(\d+)', 'fetch_bib' ],
39
            [ qr'POST /(\d+)', 'update_bib' ],
37
            [ qr'POST /(\d+)', 'update_bib' ],
Lines 41-66 sub new { Link Here
41
    } );
39
    } );
42
}
40
}
43
41
44
sub run {
45
    my ( $self ) = @_;
46
47
    $self->authenticate;
48
49
    unless ( $self->auth_status eq "ok" ) {
50
        $self->output( XMLout( { auth_status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 ), { type => 'xml', status => '403 Forbidden' } );
51
        exit;
52
    }
53
54
    $self->dispatch;
55
}
56
57
sub fetch_bib {
42
sub fetch_bib {
58
    my ( $self, $biblionumber ) = @_;
43
    my ( $self, $biblionumber ) = @_;
59
44
60
    my $record = GetMarcBiblio( $biblionumber, $self->query->url_param('items') );
45
    my $record = GetMarcBiblio( $biblionumber, $self->query->url_param('items') );
61
46
62
    if (defined $record) {
47
    if (defined $record) {
63
        $self->output( $record->as_xml_record(), { type => 'xml' } );
48
        return $record->as_xml_record();
64
    } else {
49
    } else {
65
        $self->output( '', { status => '404 Not Found', type => 'xml' } );
50
        $self->output( '', { status => '404 Not Found', type => 'xml' } );
66
    }
51
    }
Lines 77-83 sub update_bib { Link Here
77
62
78
    my $result = {};
63
    my $result = {};
79
    my $inxml = $self->query->param('POSTDATA');
64
    my $inxml = $self->query->param('POSTDATA');
80
    use Data::Dumper; warn Dumper($self->query);
81
65
82
    my $record = eval {MARC::Record::new_from_xml( $inxml, "utf8", C4::Context->preference('marcflavour'))};
66
    my $record = eval {MARC::Record::new_from_xml( $inxml, "utf8", C4::Context->preference('marcflavour'))};
83
    my $do_not_escape = 0;
67
    my $do_not_escape = 0;
Lines 115-121 sub update_bib { Link Here
115
        $do_not_escape = 1;
99
        $do_not_escape = 1;
116
    }
100
    }
117
101
118
    $self->output( XMLout($result, NoAttr => 1, RootName => 'response', XMLDecl => 1, NoEscape => $do_not_escape), { type => 'xml' } );
102
    return XMLout($result, NoAttr => 1, RootName => 'response', XMLDecl => 1, NoEscape => $do_not_escape);
119
}
103
}
120
104
121
1;
105
1;
(-)a/Koha/Service/BibProfile.pm (-9 / +3 lines)
Lines 22-28 package Koha::Service::BibProfile; Link Here
22
22
23
use Modern::Perl;
23
use Modern::Perl;
24
24
25
use base 'Koha::Service';
25
use base 'Koha::Service::XML';
26
26
27
use C4::Context;
27
use C4::Context;
28
use C4::Koha;
28
use C4::Koha;
Lines 33-40 sub new { Link Here
33
33
34
    # Authentication is handled manually below
34
    # Authentication is handled manually below
35
    return $class->SUPER::new( {
35
    return $class->SUPER::new( {
36
        authnotrequired => 1,
36
        needed_flags => { editcatalogue => 'edit_catalogue' },
37
        needed_flags => { editcatalogue => 'edit_catalogue'},
38
        routes => [
37
        routes => [
39
            [ qr'GET /(\d+)', 'fetch_bib' ],
38
            [ qr'GET /(\d+)', 'fetch_bib' ],
40
            [ qr'POST /(\d+)', 'update_bib' ],
39
            [ qr'POST /(\d+)', 'update_bib' ],
Lines 47-57 sub run { Link Here
47
46
48
    $self->authenticate;
47
    $self->authenticate;
49
48
50
    unless ( $self->auth_status eq "ok" ) {
51
        $self->output( XMLout( { auth_status => $self->auth_status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 ), { type => 'xml', status => '403 Forbidden' } );
52
        exit;
53
    }
54
55
    # get list of required tags
49
    # get list of required tags
56
    my $result = {};
50
    my $result = {};
57
    $result->{'auth_status'} = $self->auth_status;
51
    $result->{'auth_status'} = $self->auth_status;
Lines 69-75 sub run { Link Here
69
            XMLDecl => 1,
63
            XMLDecl => 1,
70
            GroupTags => {mandatory_tags => 'tag', mandatory_subfields => 'subfield', reserved_tags => 'tag', valid_values => 'value'}
64
            GroupTags => {mandatory_tags => 'tag', mandatory_subfields => 'subfield', reserved_tags => 'tag', valid_values => 'value'}
71
        ),
65
        ),
72
        { type => 'xml', status => '403 Forbidden' }
66
        { type => 'xml' }
73
    );
67
    );
74
}
68
}
75
69
(-)a/Koha/Service/Config/SystemPreferences.pm (-1 lines)
Lines 47-53 use C4::Log; Link Here
47
sub new {
47
sub new {
48
    my ( $class ) = @_;
48
    my ( $class ) = @_;
49
49
50
    # Authentication is handled manually below
51
    return $class->SUPER::new( {
50
    return $class->SUPER::new( {
52
        needed_flags => { parameters => 1 },
51
        needed_flags => { parameters => 1 },
53
        routes => [
52
        routes => [
(-)a/Koha/Service/Patrons.pm (+245 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
package Koha::Service::Patrons;
3
4
# This file is part of Koha.
5
#
6
# Copyright (C) 2014 ByWater Solutions
7
#
8
# Koha is free software; you can redistribute it and/or modify it
9
# under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 3 of the License, or
11
# (at your option) any later version.
12
#
13
# Koha is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21
=head1 NAME
22
23
svc/patrons - Web service for getting patron information
24
25
=head1 SYNOPSIS
26
27
  GET /svc/patrons/BORROWERNUMBER
28
29
=head1 DESCRIPTION
30
31
This service is used to query and change patron information.
32
33
=head1 METHODS
34
35
=cut
36
37
use Modern::Perl;
38
39
use base 'Koha::Service';
40
41
use C4::Biblio;
42
use C4::Circulation;
43
use C4::Context;
44
use C4::Dates;
45
use C4::Items;
46
use C4::Members;
47
use C4::Reserves;
48
use C4::Search qw( SimpleSearch );
49
use Koha::DateUtils;
50
51
sub new {
52
    my ( $class ) = @_;
53
54
    # Authentication is handled manually below
55
    return $class->SUPER::new( {
56
        needed_flags => { circulate => 'circulate_remaining_permissions' },
57
        routes => [
58
            [ qr'POST /(\d+)/checkouts', 'add_checkout'],
59
            [ qr'GET /(\d+)/checkouts', 'get_checkouts' ],
60
            [ qr'GET /(\d+)/holds', 'get_holds' ],
61
            [ qr'GET /(\d+)/patronInfo', 'get_patron_info' ],
62
            [ qr'POST /(\d+)/checkouts/(\d+(?:,\d+)*)', 'renew_checkouts', [ 'renewed' ] ],
63
        ]
64
    } );
65
}
66
67
=head2 add_checkout
68
69
=over 4
70
71
POST /svc/patrons/BORROWERNUMBER/checkouts
72
73
=back
74
75
Checks out an item
76
77
=cut
78
79
sub add_checkout {
80
    my ( $self, $borrowernumber ) = @_;
81
82
    my $datedue;
83
    my $duedatespec = $self->query->param('duedate');
84
85
    if ( C4::Context->preference('SpecifyDueDate') && $duedatespec ){
86
        if ($duedatespec =~ C4::Dates->regexp('syspref')) {
87
            $datedue = dt_from_string($duedatespec);
88
        } else {
89
            return {errors => {INVALID_DATE=>$duedatespec}};
90
        }
91
    }
92
93
    my ($barcode) = $self->require_params('barcode');
94
95
    my $borrower = GetMember( borrowernumber => $borrowernumber );
96
97
    my ( $errors, $questions, $alerts ) =
98
    CanBookBeIssued( $borrower, $barcode, $datedue );
99
100
    if ( $errors->{'UNKNOWN_BARCODE'} && C4::Context->preference("itemBarcodeFallbackSearch") ) {
101
        my $query = "kw=" . $barcode;
102
        my ( $searcherror, $results, $total_hits ) = SimpleSearch($query);
103
104
        # if multiple hits, offer options to librarian
105
        if ( $total_hits > 0 ) {
106
            my @options = ();
107
            foreach my $hit ( @{$results} ) {
108
                my $biblionumber = C4::Biblio::get_koha_field_from_marc(
109
                    'biblio',
110
                    'biblionumber',
111
                    C4::Search::new_record_from_zebra('biblioserver',$hit)
112
                );
113
114
                next unless ( $biblionumber );
115
116
                # offer all items with barcodes individually
117
                foreach my $item ( GetItemsInfo( $biblionumber ) ) {
118
                    $item->{available} = !( $item->{itemnotforloan} || $item->{onloan} || $item->{itemlost} || $item->{withdrawn} || $item->{damaged} || $item->{transfertwhen} || $item->{reservedate} );
119
120
                    push @options, $item if ( $item->{barcode} );
121
                }
122
            }
123
124
            $errors->{fallback_choices} = \@options;
125
        }
126
    }
127
128
    if ( %$errors || ( %$questions && !$self->query->param('confirmed') ) ) {
129
        return {
130
            item => GetBiblioFromItemNumber( undef, $barcode ),
131
            errors => $errors,
132
            questions => $questions,
133
            alerts => $alerts
134
        };
135
    }
136
137
    AddIssue( $borrower, $barcode, $datedue );
138
139
    return {};
140
}
141
142
=head2 get_holds
143
144
=over 4
145
146
GET /svc/patrons/BORROWERNUMBER/holds
147
148
=back
149
150
Retrieves information on the holds for a patron.
151
152
=cut
153
154
sub get_holds {
155
    my ( $self, $borrowernumber ) = @_;
156
157
    my @holds = GetReservesFromBorrowernumber($borrowernumber);
158
    foreach my $hold (@holds) {
159
        my $getiteminfo = GetBiblioFromItemNumber( $hold->{'itemnumber'} );
160
        $hold->{title} = $getiteminfo->{title};
161
        $hold->{author} = $getiteminfo->{author};
162
        $hold->{barcode} = $getiteminfo->{barcode};
163
    }
164
165
    return { holds => \@holds };
166
}
167
168
=head2 get_checkouts
169
170
=over 4
171
172
GET /svc/patrons/BORROWERNUMBER/checkouts
173
174
=back
175
176
Retrieves information on the checkouts for a patron.
177
178
=cut
179
180
sub get_checkouts {
181
    my ( $self, $borrowernumber ) = @_;
182
183
    return { checkouts => GetPendingIssues( $borrowernumber ) };
184
}
185
186
=head2 get_patron_info
187
188
=over 4
189
190
GET /svc/patrons/BORROWERNUMBER/patronInfo
191
192
=back
193
194
Retrieves information on a patron.
195
196
=cut
197
198
sub get_patron_info {
199
    my ( $self, $borrowernumber ) = @_;
200
201
    return { patronInfo => GetMemberDetails( $borrowernumber, 0 ) };
202
}
203
204
=head2 renew_checkouts
205
206
=over 4
207
208
POST /svc/patrons/BORROWERNUMBER/checkouts/ITEMNUMBER,ITEMNUMBER,.../?renewed=1
209
210
=back
211
212
Renews several checkouts.
213
214
=cut
215
216
sub renew_checkouts {
217
    my ( $self, $borrowernumber, $itemnumbers ) = @_;
218
219
    my @items = split /,/, $itemnumbers;
220
221
    my $branch = C4::Context->userenv ? C4::Context->userenv->{'branch'} : '';
222
    my $datedue;
223
    if ( $self->query->param('newduedate') ) {
224
        $datedue = dt_from_string( $self->query->param('newduedate') );
225
        $datedue->set_hour(23);
226
        $datedue->set_minute(59);
227
    }
228
229
    my $override_limit = $self->query->param("override_limit") || 0;
230
    my @responses;
231
    foreach my $itemno (@items) {
232
        # check status before renewing issue
233
        my ( $renewokay, $error ) =
234
          CanBookBeRenewed( $borrowernumber, $itemno, $override_limit );
235
        if ($renewokay) {
236
            push @responses, { itemnumber => $itemno, datedue => AddRenewal( $borrowernumber, $itemno, $branch, $datedue ) };
237
        } else {
238
            push @responses, { itemnumber => $itemno, error => $error };
239
        }
240
    }
241
242
    return { responses => \@responses };
243
}
244
245
1;
(-)a/circ/checkout.pl (+98 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright (C) 2014 ByWater Solutions
6
#
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.
11
#
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.
16
#
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>.
19
20
use Modern::Perl;
21
22
use C4::Auth;
23
use C4::Branch;
24
use C4::ClassSource;
25
use C4::Context;
26
use C4::Output;
27
use C4::Members;
28
use CGI;
29
use Koha::Database;
30
31
my $query = CGI->new;
32
33
my ( $template, $loggedinuser, $cookie ) = get_template_and_user (
34
    {
35
        template_name   => 'circ/checkout.tt',
36
        query           => $query,
37
        type            => "intranet",
38
        authnotrequired => 0,
39
        flagsrequired   => { circulate => 'circulate_remaining_permissions' },
40
    }
41
);
42
43
my $borrowernumber = $query->param( 'borrowernumber' );
44
$template->{ VARS }->{ borrowernumber }=$borrowernumber;
45
$template->{ VARS }->{ circview }=1;
46
$template->param( %{ GetMemberDetails( $borrowernumber, 0 ) } );
47
48
my $schema = Koha::Database->new->schema;
49
my $authorised_values = {};
50
51
$authorised_values->{branches} = [];
52
my $onlymine=C4::Context->preference('IndependentBranches') &&
53
        C4::Context->userenv &&
54
        C4::Context->userenv->{flags} % 2 == 0 &&
55
        C4::Context->userenv->{branch};
56
my $branches = GetBranches($onlymine);
57
foreach my $thisbranch ( sort keys %$branches ) {
58
    push @{ $authorised_values->{branches} }, { value => $thisbranch, lib => $branches->{$thisbranch}->{'branchname'} };
59
}
60
61
$authorised_values->{itemtypes} = [ $schema->resultset( "Itemtype" )->search( undef, {
62
    columns => [ { value => 'itemtype' }, { lib => "description" } ],
63
    order_by => "description",
64
    result_class => 'DBIx::Class::ResultClass::HashRefInflator'
65
} ) ];
66
67
my $class_sources = GetClassSources();
68
69
my $default_source = C4::Context->preference("DefaultClassificationSource");
70
71
foreach my $class_source (sort keys %$class_sources) {
72
    next unless $class_sources->{$class_source}->{'used'} or
73
                ($class_source eq $default_source);
74
    push @{ $authorised_values->{cn_source} }, { value => $class_source, lib => $class_sources->{$class_source}->{'description'} };
75
}
76
77
my $branch_limit = C4::Context->userenv ? C4::Context->userenv->{"branch"} : "";
78
my $results;
79
if( $branch_limit ) {
80
    $results = $schema->resultset( "AuthorisedValue" )->search(
81
    { "authorised_values_branches.branchcode" => { "=", [ $branch_limit, undef ] } },
82
    { join => "authorised_values_branches", order_by => "lib" } );
83
} else {
84
    $results = $schema->resultset( "AuthorisedValue" )->search(
85
    undef,
86
    { order_by => "lib" } );
87
}
88
89
foreach my $result ( $results->all ) {
90
    $authorised_values->{$result->category} ||= [];
91
    push @{ $authorised_values->{$result->category} }, { value => $result->authorised_value, lib => $result->lib };
92
}
93
94
$template->{VARS}->{authorised_values} = $authorised_values;
95
96
$template->{VARS}->{authvalcode_notforloan} = C4::Koha::GetAuthValCode('items.notforloan', '' );
97
98
output_html_with_http_headers $query, $cookie, $template->output;
(-)a/circ/circulation.pl (+3 lines)
Lines 230-235 if ($findborrower) { Link Here
230
# get the borrower information.....
230
# get the borrower information.....
231
my $borrower;
231
my $borrower;
232
if ($borrowernumber) {
232
if ($borrowernumber) {
233
    if ( $query->cookie( 'checkout_client' ) eq 'beta' ) {
234
        print $query->redirect( '/cgi-bin/koha/circ/checkout.pl?borrowernumber=' . $borrowernumber );
235
    }
233
    $borrower = GetMemberDetails( $borrowernumber, 0 );
236
    $borrower = GetMemberDetails( $borrowernumber, 0 );
234
    my ( $od, $issue, $fines ) = GetMemberIssuesAndFines( $borrowernumber );
237
    my ( $od, $issue, $fines ) = GetMemberIssuesAndFines( $borrowernumber );
235
238
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/doc-head-open.inc (-1 / +13 lines)
Lines 1-4 Link Here
1
<!DOCTYPE html>
1
<!DOCTYPE html>
2
<!-- TEMPLATE FILE: [% template.name.split('/').last %] -->
2
<!-- TEMPLATE FILE: [% template.name.split('/').last %] -->
3
[% IF ( bidi ) %]<html lang="[% lang %]" dir="[% bidi %]">[% ELSE %]<html lang="[% lang %]">[% END %]
3
[% IF ( bidi ) %]
4
[% IF ( angular_app ) %]
5
<html lang="[% lang %]" dir="[% bidi %]" ng-app="[% angular_app %]>
6
[% ELSE %]
7
<html lang="[% lang %]" dir="[% bidi %]">
8
[% END %]
9
[% ELSE %]
10
[% IF ( angular_app ) %]
11
<html lang="[% lang %]" ng-app="[% angular_app %]">
12
[% ELSE %]
13
<html lang="[% lang %]">
14
[% END %]
15
[% END %]
4
<head>
16
<head>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc (+15 lines)
Lines 62-68 $(document).ready(function(){ Link Here
62
        searchToHold();
62
        searchToHold();
63
        return false;
63
        return false;
64
    })
64
    })
65
    $("#switchclient").click( function() {
66
        var borrowernumber = [% borrowernumber || "null" %];
67
68
        if ( $.cookie('checkout_client' ) == 'beta' ) {
69
            $.cookie( 'checkout_client', 'standard', { expires: 365, path: '/' } );
70
            window.location = '/cgi-bin/koha/circ/circulation.pl?borrowernumber=' + borrowernumber;
71
        }
72
        else {
73
            $.cookie( 'checkout_client', 'beta', { expires: 365, path: '/' } );
74
            window.location = '/cgi-bin/koha/circ/checkout.pl?borrowernumber=' + borrowernumber;
75
        }
76
        return false;
77
    } );
65
});
78
});
79
66
function confirm_deletion() {
80
function confirm_deletion() {
67
    var is_confirmed = window.confirm(_("Are you sure you want to delete this patron? This cannot be undone."));
81
    var is_confirmed = window.confirm(_("Are you sure you want to delete this patron? This cannot be undone."));
68
    if (is_confirmed) {
82
    if (is_confirmed) {
Lines 195-200 function searchToHold(){ Link Here
195
                    <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>
209
                    <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>
196
                [% END %]
210
                [% END %]
197
                <li><a id="exportcheckins" href="#">Export today's checked in barcodes</a></li>
211
                <li><a id="exportcheckins" href="#">Export today's checked in barcodes</a></li>
212
                <li><a id="switchclient" href="#">Switch checkout client</a></li>
198
            </ul>
213
            </ul>
199
    </div>
214
    </div>
200
</div>
215
</div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/js/ajax.js (-3 / +3 lines)
Lines 19-32 KOHA.AJAX = { Link Here
19
            return;
19
            return;
20
        }
20
        }
21
21
22
        var error = eval( '(' + xhr.responseText + ')' );
22
        var data = eval( '(' + xhr.responseText + ')' );
23
23
24
        if ( error.type == 'auth' ) {
24
        if ( data.error == 'auth' ) {
25
            humanMsg.displayMsg( MSG_SESSION_TIMED_OUT );
25
            humanMsg.displayMsg( MSG_SESSION_TIMED_OUT );
26
        }
26
        }
27
27
28
        if ( callback ) {
28
        if ( callback ) {
29
            callback( error );
29
            callback( data );
30
        } else {
30
        } else {
31
            humanMsg.displayAlert( MSG_DATA_NOT_SAVED );
31
            humanMsg.displayAlert( MSG_DATA_NOT_SAVED );
32
        }
32
        }
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/checkout.tt (+945 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% USE Branches %]
3
[% USE KohaDates %]
4
[% IF ( export_remove_fields OR export_with_csv_profile ) %]
5
   [% SET exports_enabled = 1 %]
6
[% END %]
7
[% USE AuthorisedValues %]
8
[% INCLUDE 'doc-head-open.inc' angular_app = "checkoutApp" %]
9
[% SET destination = "circ" %]
10
<title>Koha &rsaquo; Circulation
11
[% IF borrowernumber %]
12
  &rsaquo; Checking out to [% INCLUDE 'patron-title.inc' invert_name = 1 %]
13
[% END %]
14
</title>
15
[% INCLUDE 'doc-head-close.inc' %]
16
[% INCLUDE 'calendar.inc' %]
17
[% IF ( UseTablesortForCirc ) %]<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
18
[% INCLUDE 'datatables.inc' %][% END %]
19
<script src="[% interface %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
20
<script src="[% interface %]/lib/jquery/plugins/jquery-ui-timepicker-addon.min.js"></script>
21
[% INCLUDE 'timepicker.inc' %]
22
[% INCLUDE 'doc-head-angular.inc' %]
23
<script>
24
var authorised_values = {
25
    [% FOREACH category IN authorised_values -%]
26
        "[% category.key %]":{
27
            [% FOREACH value IN category.value -%]
28
                "[% value.value %]":"[% value.lib %]",
29
            [% END -%]
30
        },
31
    [% END -%]
32
}
33
34
var checkoutApp = angular.module( 'checkoutApp', [] )
35
    .filter( 'isNotEmpty', function() {
36
        return function(obj) {
37
            return !$.isEmptyObject(obj);
38
        };
39
    } );
40
41
checkoutApp.controller( 'CheckoutController', function ( $scope, $http ) {
42
    $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
43
    $scope.borrowernumber = [% borrowernumber %];
44
    $scope.authorised_values = authorised_values;
45
46
    $http.get( '/cgi-bin/koha/svc/patrons/' + $scope.borrowernumber + '/patronInfo' ).success( function( data ) {
47
        $scope.borrower = data.patronInfo;
48
        $scope.not_loading = true;
49
        $('#yui-main > .yui-b').show();
50
    });
51
52
    function get_checkouts() {
53
        $http.get( '/cgi-bin/koha/svc/patrons/' + $scope.borrowernumber + '/checkouts' ).success( function( data ) {
54
            $scope.checkouts = data.checkouts;
55
        });
56
    }
57
58
    get_checkouts();
59
60
    $scope.checkout = function(barcode, confirmed) {
61
        var barcode = barcode || $scope.barcode;
62
        var params = { barcode: barcode, duedate: $scope.duedatespec || '' };
63
        if ( confirmed ) {
64
            params.confirmed = 1;
65
            params.cancelreserve = $scope.cancelreserve;
66
        }
67
        $scope.not_loading = false;
68
        $http.post( '/cgi-bin/koha/svc/patrons/' + $scope.borrowernumber + '/checkouts', $.param( params ) ).success(function( data ) {
69
                $scope.not_loading = true;
70
                $scope.last_barcode = barcode;
71
                $scope.barcode = '';
72
73
                if ( data.errors || data.questions ) {
74
                    $scope.last_item = data.item;
75
                    $scope.errors = data.errors;
76
                    $scope.questions = data.questions;
77
                    $scope.cancelreserve = data.questions.RESERVE_WAITING ? "revert" : null;
78
                }else {
79
                    $scope.last_item = null;
80
                    $scope.errors = null;
81
                    $scope.questions = null;
82
                    if ( !$scope.stickyduedate ) $scope.duedatespec = "";
83
                    $('#barcode')[0].focus();
84
                    get_checkouts();
85
                }
86
            });
87
    };
88
89
    $scope.cancelCheckout = function() {
90
        $scope.questions = null;
91
        $scope.errors = null;
92
        $('#barcode')[0].focus();
93
    };
94
95
     
96
} );
97
98
$(document).ready( function() {
99
    $('#patronlists').tabs( [% IF ( UseTablesortForCirc ) %]{
100
        // Correct table sizing for tables hidden in tabs
101
        // http://www.datatables.net/examples/api/tabs_and_scrolling.html
102
        "show": function( event, ui ) {
103
            var oTable = $( 'div.dataTables_wrapper>table', ui.panel ).dataTable();
104
            if ( oTable.length > 0 ) {
105
                oTable.fnAdjustColumnSizing();
106
            }
107
        }
108
    }[% END %] );
109
    $("#duedatespec").datetimepicker({
110
        onClose: function(dateText, inst) { $("#barcode").focus(); },
111
        hour: 23,
112
        minute: 59
113
    });
114
    $("#confduedatespec").datetimepicker({
115
        hour: 23,
116
        minute: 59
117
    });
118
} );
119
</script>
120
</head>
121
<body id="circ_circulation" class="circ" ng-controller="CheckoutController">
122
123
   <div class="loading-overlay" ng-hide="not_loading">
124
       <div>Loading, please wait...</div>
125
   </div>
126
127
[% INCLUDE 'header.inc' %]
128
[% INCLUDE 'circ-search.inc' %]
129
130
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a> &rsaquo; <a href="/cgi-bin/koha/circ/circulation.pl">Checkouts</a>
131
<span ng-show="borrower"> &rsaquo; Checking out to {{ borrower.surname }}, {{ borrower.firstname }}</span>
132
</div>
133
<div id="doc3" class="yui-t2">
134
135
   <div id="bd">
136
	<div id="yui-main">
137
	<div class="yui-b" style="display: none">
138
139
[% INCLUDE 'members-toolbar.inc' %]
140
141
<!--  INITIAL BLOC : PARAMETERS & BORROWER INFO -->
142
<div style="display: none;" id="add_message_form">
143
<form method="post" action="/cgi-bin/koha/circ/add_message.pl" id="message_form" name="message_f">
144
<fieldset id="borrower_messages" class="brief">
145
<legend>Leave a message</legend>
146
	<ol>
147
    <li>
148
            <label for="message_type">Add a message for:</label>
149
          <select name="message_type" id="message_type">
150
            <option value="L">Other librarians</option>
151
            <option value="B">[% firstname %]</option>
152
        </select>
153
    </li>
154
    [% IF ( canned_bor_notes_loop ) %]
155
        <li>
156
                <label for="type">Predefined notes: </label>
157
                <select name="type" id="type" onchange="this.form.borrower_message.value=this.options[this.selectedIndex].value;">
158
                    <option value="">Select note</option>
159
                    [% FOREACH canned_bor_notes_loo IN canned_bor_notes_loop %]
160
                    <option value="[% canned_bor_notes_loo.lib %]">[% canned_bor_notes_loo.lib %]</option>
161
                    [% END %]
162
                </select>
163
        </li>
164
    [% END %]
165
    <li>
166
        <textarea rows="3" cols="60" name="borrower_message" id="borrower_message" ></textarea>
167
    </li>
168
	</ol>
169
    <fieldset class="action">
170
        <input type="submit" value="Save" /> <a href="#" class="cancel">Cancel</a>
171
    </fieldset>
172
173
        <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
174
        <input type="hidden" name="branchcode" value="[% branch %]" />
175
</fieldset>
176
</form>
177
</div>
178
179
<div class="yui-g" ng-show="errors | isNotEmpty">
180
<div id="circ_impossible" class="dialog alert">
181
<!-- RESULT OF ISSUING REQUEST -->
182
    <ul>
183
184
        <li ng-show="errors.STATS">Local use recorded</li>
185
186
        <li ng-show="errors.INVALID_DATE">The due date &quot;{{errors.INVALID_DATE}}&quot; is invalid</li>
187
188
        <li ng-show="errors.UNKNOWN_BARCODE">
189
            The barcode &quot;{{last_barcode}}&quot; was not found
190
191
            <div ng-show="errors.fallback_choices">
192
                The following items were found by searching:
193
                <table>
194
                    <thead>
195
                        <th scope="col">Title</th>
196
                        <th scope="col">Author</th>
197
                        <th scope="col">Barcode</th>
198
                        <th scope="col">Status</th>
199
                        <th scope="col">&nbsp;</th>
200
                    </thead>
201
                    <tbody>
202
                        <tr ng-repeat="choice in errors.fallback_choices">
203
                            <td>{{ choice.title }}</td>
204
                            <td>{{ choice.author }}</td>
205
                            <td>{{ choice.barcode }}</td>
206
                            <td>
207
                                <span ng-hide="choice.available">
208
                                    Available
209
                                </span>
210
                                <span ng-show="choice.available">
211
                                    Not Available
212
                                </span>
213
                            </td>
214
                            <td><button ng-click="checkout(choice.barcode)">Check Out</button></td>
215
                        </tr>
216
                    </tbody>
217
                </table>
218
            </div>
219
        </li>
220
221
        <li ng-show="errors.NOT_FOR_LOAN">
222
            <span ng-show="errors.itemtype_notforloan">Item type not for loan.</span>
223
224
            <span ng-show="errors.item_notforloan">
225
                Item not for loan
226
227
                <span ng-show="authorised_values['[% authvalcode_notforloan %]'][errors.item_notforloan]"> ({{authorised_values['[% authvalcode_notforloan %]'][errors.item_notforloan]}})</span>
228
            </span>
229
        </li>
230
231
        <li ng-show="errors.WTHDRAWN">Item has been withdrawn</li>
232
233
        <li ng-show="errors.RESTRICTED">Item is restricted</li>
234
235
        <li ng-show="errors.GNA">Patron's address is in doubt</li>
236
237
        <li ng-show="errors.CARD_LOST">Patron's card is lost</li>
238
239
        <li ng-show="errors.DERBARRED">Patron is restricted</li>
240
241
        <li ng-show="errors.NO_MORE_RENEWALS">No more renewals possible</li>
242
243
        <li ng-show="errors.AGE_RESTRICTION">Age restriction {{errors.AGE_RESTRICTION}}.</li>
244
245
        <li ng-show="errors.EXPIRED">Patron's card is expired</li>
246
247
        <li ng-show="errors.TOO_MANY">Too many checked out. {{errors.current_loan_count}} checked out, only {{errors.max_loans_allowed}} are allowed.</li>
248
249
        <li ng-show="errors.ITEMNOTSAMEBRANCH">This item belongs to {{authorised_values.branches[errors.itemhomebranch]}} and cannot be checked out from this location.</li>
250
251
        <li ng-show="errors.USERBLOCKEDREMAINING">Patron has had overdue items and is blocked for {{errors.USERBLOCKEDREMAINING}} day(s).</li>
252
253
        <li ng-show="errors.USERBLOCKEDOVERDUE">Checkouts are BLOCKED because patron has overdue items</li>
254
    </ul>
255
256
</div></div>
257
<!-- /impossible -->
258
259
<div class="yui-g" ng-show="questions | isNotEmpty">
260
261
<div id="circ_needsconfirmation" class="dialog alert">
262
[% IF CAN_user_circulate_force_checkout %]
263
  <h3>Please confirm checkout</h3>
264
[% ELSE %]
265
  <h3>Cannot check out</h3>
266
[% END %]
267
268
<ul>
269
    <li ng-show="questions.AGE_RESTRICTION">
270
        Age restriction {{ questions.AGE_RESTRICTION }}.
271
        [% IF CAN_user_circulate_force_checkout %]
272
            Check out anyway?
273
        [% END %]
274
    </li>
275
276
    <li ng-show="questions.DEBT">The patron has a debt of {{ questions.DEBT }}.</li>
277
278
    <li ng-show="questions.RENEW_ISSUE">Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) is currently checked out to this patron.  Renew?</li>
279
280
    <li ng-show="questions.RESERVE_WAITING">
281
        Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) has been waiting for
282
        <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber={{ questions.resborrowernumber }}">{{ questions.resfirstname }} {{ questions.ressurname }}</a>
283
        ({{ questions.rescardnumber }}) at {{ questions.resbranchname }} since {{ questions.reswaitingdate }}.
284
    </li>
285
286
    <li ng-show="questions.RESERVED">
287
        Item <i>{{ last_item.title }}</i> ({{ last_barcode }}) has been on hold for
288
        <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber={{ questions.resborrowernumber }}">{{ questions.resfirstname }} {{ questions.ressurname }}</a>
289
        ({{ questions.rescardnumber }}) at {{ questions.resbranchname }} since {{ questions.resreservedate }}.
290
    </li>
291
292
    <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 }}).
293
      [% IF CAN_user_circulate_force_checkout %]
294
        Check in and check out?
295
      [% END %]
296
    </li>
297
298
    <li ng-show="questions.TOO_MANY">Too many checked out. {{questions.current_loan_count}} checked out, only {{questions.max_loans_allowed}} are allowed.</li>
299
300
    <li ng-show="questions.BORRNOTSAMEBRANCH">This patron is from a different library ({{ questions.BORRNOTSAMEBRANCH }}).</li>
301
302
    <li ng-show="questions.PATRON_CANT">This patron can't check out this item per library circulation policy.</li>
303
304
    <li ng-show="questions.NOT_FOR_LOAN_FORCING">
305
        <span ng-show="questions.itemtype_notforloan">Item type is normally not for loan.</span>
306
307
        <span ng-show="questions.item_notforloan">
308
            Item is normally not for loan
309
310
            <span ng-show="authorised_values['[% authvalcode_notforloan %]'][questions.item_notforloan]"> ({{authorised_values['[% authvalcode_notforloan %]'][questions.item_notforloan]}})</span>.
311
        </span>
312
313
        [% IF CAN_user_circulate_force_checkout %]
314
            Check out anyway?
315
        [% END %]
316
    </li>
317
318
    <li ng-show="questions.USERBLOCKEDOVERDUE">
319
        Patron has {{ questions.USERBLOCKEDOVERDUE }} overdue item(s).
320
        [% IF CAN_user_circulate_force_checkout %]
321
                Check out anyway?
322
        [% END %]
323
    </li>
324
325
    <li ng-show="questions.ITEM_LOST">
326
        This item has been lost with a status of "{{ questions.ITEM_LOST }}".
327
        [% IF CAN_user_circulate_force_checkout %]
328
            Check out anyway?
329
        [% END %]
330
    </li>
331
332
    <li ng-show="questions.HIGHHOLDS">High demand item. Loan period shortened to {{ questions.HIGHHOLDS.duration }} days (due {{ questions.HIGHHOLDS.returndate }}). Check out anyway?</li>
333
334
    <li ng-show="questions.BIBLIO_ALREADY_ISSUED">
335
        Patron has already checked out another item from this record.
336
        [% IF CAN_user_circulate_force_checkout %]
337
            Check out anyway?
338
        [% END %]
339
    </li>
340
</ul>
341
342
[% IF CAN_user_circulate_force_checkout %]
343
<form autocomplete="off">
344
[% ELSE %]
345
<form autocomplete="off" ng-show="questions.HIGHHOLDS">
346
[% END %]
347
348
   <p ng-show="questions.RESERVED">
349
       <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" ng-model="cancelreserve"/>
350
       <label for="cancelreserve">Cancel hold</label>
351
   </p>
352
353
   <p ng-show="questions.RESERVE_WAITING">
354
       <label for="cancelreserve">Cancel hold</label>
355
       <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" ng-model="cancelreserve"/><br />
356
       <label for="revertreserve">Revert waiting status</label>
357
       <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked" ng-model="cancelreserve"/>
358
   </p>
359
360
   <p ng-show="questions.INVALID_DATE">
361
       <input type="text" size="13" id="confduedatespec" name="duedatespec" readonly="readonly" ng-model="duedatespec" />
362
       <label for="confduedatespec">Due date</label>
363
   </p>
364
365
    <input type="submit" class="approve" value="Yes, Renew (Y)" accesskey="y" ng-show="questions.RENEW_ISSUE" ng-click="checkout( last_barcode, true )" />
366
367
    <input type="submit" class="approve" value="Yes, Check Out (Y)" accesskey="y" ng-hide="questions.RENEW_ISSUE" ng-click="checkout( last_barcode, true )" />
368
369
    <input type="submit" class="deny" value="No, Don't Renew (N)" accesskey="n" ng-show="questions.RENEW_ISSUE" ng-click="cancelCheckout()" />
370
   
371
    <input type="submit" class="deny" value="No, Don't Check Out (N)" accesskey="n" ng-hide="questions.RENEW_ISSUE" ng-click="cancelCheckout()" />
372
373
</form>
374
375
[% UNLESS CAN_user_circulate_force_checkout %]
376
<form ng-hide="questions.HIGHHOLDS">
377
   <input type="submit" class="deny" value="Continue" ng-click="cancelCheckout()" />
378
</form>
379
[% END %]
380
381
</div></div>
382
383
[% IF ( was_renewed ) %]<div class="dialog message">Patron's account has been renewed until [% expiry %]</div>[% END %]
384
385
[% IF additional_materials %]
386
    <div id="materials" class="dialog message">Note about the accompanying materials: <br />
387
    [% additional_materials %]
388
    </div>
389
[% END %]
390
391
[% IF ( alert.ITEM_LOST ) %]
392
    <div class="dialog message">This item has been lost with a status of "[% alert.ITEM_LOST %]".</div>
393
[% END %]
394
395
[% IF ( alert.OTHER_CHARGES ) %]
396
    <div class="dialog message">The patron has unpaid charges for reserves, rentals etc of [% alert.OTHER_CHARGES %]</div>
397
[% END %]
398
399
[% IF ( issued ) %]
400
<p>Item checked out</p>
401
[% END %]
402
403
<!-- BARCODE ENTRY -->
404
405
<div class="yui-g" ng-show="borrower">
406
[% UNLESS ( noissues ) %]
407
[% IF ( flagged ) %]
408
<div class="yui-u first">
409
[% ELSE %]
410
<div>
411
412
[% END %]
413
414
<form method="post" id="mainform" name="mainform" autocomplete="off" ng-submit="checkout()">
415
<fieldset id="circ_circulation_issue">
416
    [% IF ( DisplayClearScreenButton ) %]
417
        <span id="clearscreen"><a href="/cgi-bin/koha/circ/circulation.pl" title="Clear screen">x</a></span>
418
    [% END %]
419
420
    <label for="barcode">Checking out to {{ borrower.surname }}, {{ borrower.firstname }} ({{ borrower.cardnumber }})</label>
421
422
	<div class="hint">Enter item barcode:</div>
423
424
	<input type="text" name="barcode" id="barcode" class="barcode focus" size="14" ng-attr-disabled="{{questions? 'disabled':''}}" ng-model="barcode"/>
425
426
    <input type="submit" value="Check Out" />
427
428
    [% IF ( Koha.Preference( 'SpecifyDueDate' ) ) %]<div class="date-select">
429
        <div class="hint">Specify due date [% INCLUDE 'date-format.inc' %]: </div>
430
        <input type="text" size="13" id="duedatespec" name="duedatespec" readonly="readonly" ng-model="duedatespec" />
431
        <label for="stickyduedate"> Remember for session:</label>
432
        <input type="checkbox" id="stickyduedate" onclick="this.form.barcode.focus();" name="stickyduedate" ng-model="stickyduedate"/>
433
        <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;" />
434
</div>[% END %]
435
</fieldset>
436
</form></div>[% END %]<!-- /unless noissues -->
437
438
[% IF ( noissues ) %]<div>[% ELSE %]<div class="yui-u">[% END %]
439
440
    [% IF ( flagged ) %]
441
		[% IF ( noissues ) %]
442
		<h4>Checking out to [% INCLUDE 'patron-title.inc' %]</h4>
443
        <div id="circmessages" class="circmessage warning">
444
		[% ELSE %]
445
        <div id="circmessages" class="circmessage attention">
446
		[% END %]
447
448
		<h3>[% IF ( noissues ) %]
449
		        Cannot check out!
450
		[% ELSE %]Attention:[% END %]</h3>
451
		<ul>
452
453
			[% IF ( warndeparture ) %]
454
			<li><span class="circ-hlt">Expiration:</span> Patron's card will expire soon.
455
			Patron's card expires on [% expiry %] <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% borrowernumber %]&amp;cardnumber=[% cardnumber %]&amp;destination=circ&amp;reregistration=y">Renew</a> or <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&amp;destination=circ&amp;borrowernumber=[% borrowernumber %]&amp;categorycode=[% categorycode %]">Edit Details</a>
456
457
			</li>
458
			[% END %]
459
460
			[% IF ( returnbeforeexpiry ) %]
461
			 <li><span class="circ-hlt">Set due date to expiry:</span> You have the ReturnBeforeExpiry system preference enabled this means if the
462
			 expiry date is before the date due, the date due will be set to the expiry date
463
			 </li>
464
			[% END %]
465
466
			[% IF ( expired ) %]
467
			<li><span class="circ-hlt">Expiration:</span> Patron's card has expired.
468
			[% IF ( expiry ) %]Patron's card expired on [% expiry %][% END %] <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% borrowernumber %]&amp;cardnumber=[% cardnumber %]&amp;destination=circ&amp;reregistration=y">Renew</a> or <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&amp;destination=circ&amp;borrowernumber=[% borrowernumber %]&amp;categorycode=[% categorycode %]">Edit Details</a>
469
470
			</li>
471
			[% END %]
472
473
            [% IF ( gna ) %]
474
			<li class="blocker"><span class="circ-hlt">Address:</span> Patron's address in doubt</li>
475
			[% END %]
476
477
            [% IF ( lost ) %]
478
			<li class="blocker"><span class="circ-hlt">Lost: </span>Patron's card is lost</li>
479
			[% END %]
480
481
            [% IF ( userdebarred ) %]
482
               <li class="blocker">
483
                   <span class="circ-hlt"> Restricted:</span> Patron's account is restricted
484
485
                   [% IF ( userdebarreddate ) %]
486
                       until [% userdebarreddate %]
487
                   [% END %]
488
489
                   [% IF ( debarredcomment ) %]
490
                       with the explanation: <br/><i>[% debarredcomment | html_line_break %]</i>
491
                   [% END %]
492
493
                   <br/>
494
                   <a class="btn btn-small" href="#reldebarments" onclick="$('#debarments-tab-link').click()"><i class="icon-ban-circle"></i> View restrictions</a>
495
               </li>
496
            [% END %]
497
498
		[% 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>
499
            [% END %]
500
501
		[% IF ( charges ) %]
502
			    <li>
503
            <span class="circ-hlt">Fees &amp; Charges:</span> Patron has  <a href="/cgi-bin/koha/members/boraccount.pl?borrowernumber=[% borrowernumber %]">Outstanding fees &amp; charges[% IF ( chargesamount ) %] of [% chargesamount %][% END %]</a>.
504
                [% IF ( charges_is_blocker ) %]
505
                    Checkouts are <span class="circ-hlt">BLOCKED</span> because fine balance is <span class="circ-hlt">OVER THE LIMIT</span>.
506
                [% END %]
507
            <a href="/cgi-bin/koha/members/pay.pl?borrowernumber=[% borrowernumber %]">Make payment</a></li>
508
			[% END %]
509
510
		[% IF ( credits ) %]
511
			<li>
512
                <span class="circ-hlt">Credits:</span> Patron has a credit[% IF ( creditsamount ) %] of [% creditsamount %][% END %]
513
            </li>
514
			[% END %]
515
516
517
518
			</ul>
519
        </div>
520
521
			[% IF ( WaitingReserveLoop ) %]
522
			<div id="holdswaiting" class="circmessage">
523
		    <h4>Holds waiting:</h4>
524
			        [% FOREACH WaitingReserveLoo IN WaitingReserveLoop %]
525
			            <ul>
526
			                <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 %].
527
			            [% IF ( WaitingReserveLoo.waitingat ) %]
528
			                <br />[% IF ( WaitingReserveLoo.waitinghere ) %]<strong class="waitinghere">[% ELSE %]<strong>[% END %]Waiting at [% WaitingReserveLoo.waitingat %]</strong>
529
			            [% END %]
530
			                </li>
531
			            </ul>
532
			    [% END %]
533
			</div>
534
			<!-- /If WaitingReserveLoop -->[% END %]
535
	[% IF ( notes ) %]
536
			<div id="circnotes" class="circmessage">
537
			<h4>Notes:</h4>
538
            <p><span class="circ-hlt">[% notesmsg %]</span></p>
539
			</div>
540
541
542
    <!-- /If notes -->[% END %]
543
544
	<div id="messages" class="circmessage">
545
		<h4>Messages:</h4>
546
		<ul>
547
			[% FOREACH lib_messages_loo IN lib_messages_loop %]
548
				<li>
549
					<span class="circ-hlt">
550
						[% lib_messages_loo.message_date_formatted %]
551
						[% lib_messages_loo.branchcode %]
552
						<i>"[% lib_messages_loo.message %]"</i>
553
					</span>
554
					[% IF ( lib_messages_loo.can_delete ) %]
555
						<a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% lib_messages_loo.message_id %]&amp;borrowernumber=[% lib_messages_loo.borrowernumber %]">[Delete]</a>
556
					[% ELSE %]
557
						[% IF ( all_messages_del ) %]
558
							<a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% lib_messages_loo.message_id %]&amp;borrowernumber=[% lib_messages_loo.borrowernumber %]">[Delete]</a>
559
						[% END %]
560
					[% END %]
561
				</li>
562
			[% END %]
563
			[% FOREACH bor_messages_loo IN bor_messages_loop %]
564
				<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 %]&amp;borrowernumber=[% bor_messages_loo.borrowernumber %]">[Delete]</a>
565
                [% ELSIF ( all_messages_del ) %]
566
                    <a href="/cgi-bin/koha/circ/del_message.pl?message_id=[% bor_messages_loo.message_id %]&amp;borrowernumber=[% bor_messages_loo.borrowernumber %]">[Delete]</a>
567
                [% END %]</li>
568
			[% END %]
569
570
		</ul>
571
	</div>
572
573
     <!-- /If flagged -->[% END %]
574
575
576
577
</div>
578
</div>
579
580
<div class="yui-g" ng-show="borrower"><div id="patronlists" class="toptabs">
581
582
<ul>
583
<li> <a href="#checkouts">{{ checkouts.length }} Checkout(s)</a></li>
584
[% IF ( displayrelissues ) %]
585
<li><a href="#relissues">Relatives' checkouts</a></li>
586
[% END %]
587
<li>[% IF ( countreserv ) %]
588
            <a href="#reserves">[% countreserv %] Hold(s)</a>
589
    [% ELSE %]
590
            <a href="#reserves">0 Holds</a>
591
    [% END %]</li>
592
    <li><a id="debarments-tab-link" href="#reldebarments">[% debarments.size %] Restrictions</a></li>
593
594
</ul>
595
596
<!-- SUMMARY : TODAY & PREVIOUS ISSUES -->
597
<div id="checkouts">
598
599
<form name="issues" action="/cgi-bin/koha/reserve/renewscript.pl" method="post" class="checkboxed" ng-show="checkouts.length">
600
    <input type="hidden" value="circ" name="destination" />
601
    <input type="hidden" name="cardnumber" value="[% cardnumber %]" />
602
    <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
603
    <input type="hidden" name="branch" value="[% branch %]" />
604
        <table id="issuest">
605
    <thead><tr>
606
        <th scope="col" class="title-string">Due date</th>
607
        <th scope="col" class="anti-the">Title</th>
608
        <th scope="col">Item type</th>
609
        <th scope="col" class="title-string">Checked out on</th>
610
        <th scope="col">Checked out from</th>
611
        <th scope="col">Call no</th>
612
        <th scope="col">Charge</th>
613
        <th scope="col">Price</th>
614
        <th scope="col">Renew <p class="column-tool"><a href="#" id="CheckAllitems">select all</a> | <a href="#" id="CheckNoitems">none</a></p></th>
615
        <th scope="col">Check in <p class="column-tool"><a href="#" id="CheckAllreturns">select all</a> | <a href="#" id="CheckNoreturns">none</a></p></th>
616
        [% IF ( exports_enabled ) %]
617
          <th scope="col">Export <p class="column-tool"><a href="#" id="CheckAllexports">select all</a> | <a href="#" id="CheckNoexports">none</a></p></th>
618
        [% END %]
619
    </tr></thead>
620
[% INCLUDE 'checkouts-table-footer.inc' %]
621
	<tbody>
622
623
    <tr ng-repeat="checkout in checkouts" class="{{ $odd ? 'highlight' : '' }}">
624
        [% IF ( todayissue.od ) %]<td class="od">[% ELSE %]<td>[% END %]
625
            <span title="{{ checkout.date_due }}">{{ checkout.date_due }}</span>
626
        </td>
627
        <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber={{ checkout.biblionumber }}&amp;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 }}&amp;itemnumber={{ checkout.itemnumber }}#item{{ checkout.itemnumber }}">{{ checkout.barcode }}</a></td>
628
        <td>[% UNLESS ( noItemTypeImages ) %] [% IF ( todayissue.itemtype_image ) %]<img src="{{ checkout.itemtype_image }}" alt="" />[% END %][% END %]{{ checkout.itemtype }}</td>
629
        <td><span title="{{ checkout.displaydate_sort }}">{{ checkout.issuedate }}</span></td>
630
        <td>{{ checkout.issuingbranchname }}</td>
631
        <td>{{ checkout.itemcallnumber }}</td>
632
            <td>{{ checkout.charge }}</td>
633
            <td>{{ checkout.replacementprice }}</td>
634
        <td><span style="padding: 0 1em;">{{ checkout.renewals || 0 }}</span>
635
        <td><input type="checkbox" class="radio" name="barcodes[]"  value="{{ checkout.barcode }}" />
636
            <input type="checkbox" name="all_barcodes[]" value="{{ checkout.barcode }}" checked="checked" style="display: none;" />
637
        </td>
638
        [% IF ( exports_enabled ) %]
639
          <td style="text-align:center;">
640
            <input type="checkbox" id="export_{{ checkout.biblionumber }}" name="biblionumbers" value="{{ checkout.biblionumber }}" />
641
            <input type="checkbox" name="itemnumbers" value="{{ checkout.itemnumber }}" style="visibility:hidden;" />
642
          </td>
643
        [% END %]
644
    </tr>
645
646
[% IF ( previssues ) %]
647
    [% UNLESS ( todayissues ) %]
648
    [% INCLUDE 'checkouts-table-footer.inc' %]
649
        <tbody>
650
    [% END %]
651
    [% 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 %]
652
    [% FOREACH previssue IN previssues %]
653
    [% IF ( loop.odd ) %]
654
        <tr>
655
    [% ELSE %]
656
        <tr class="highlight">
657
    [% END %]
658
        [% IF ( previssue.od ) %]<td class="od">[% ELSE %]<td>[% END %]
659
        <span title="[% previssue.dd_sort %]">[% previssue.dd %]</span>
660
661
            [% IF ( previssue.itemlost ) %]
662
                <span class="lost">[% AuthorisedValues.GetByCode( 'LOST', previssue.itemlost ) %]</span>
663
            [% END %]
664
            [% IF ( previssue.damaged ) %]
665
                <span class="dmg">[% AuthorisedValues.GetByCode( 'DAMAGED', previssue.damaged ) %]</span>
666
            [% END %]
667
        </td>
668
        <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% previssue.biblionumber %]&amp;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 %]&amp;itemnumber=[% previssue.itemnumber %]#item[% previssue.itemnumber %]">[% previssue.barcode %]</a></td>
669
        <td>
670
            [% previssue.itemtype %]
671
        </td>
672
        <td><span title="[% previssue.displaydate_sort %]">[% previssue.displaydate %]</span></td>
673
        [% IF ( previssue.multiple_borrowers ) %]<td>[% previssue.firstname %] [% previssue.surname %]</td>[% END %]
674
        <td>[% previssue.issuingbranchname %]</td>
675
        <td>[% previssue.itemcallnumber %]</td>
676
        <td>[% previssue.charge %]</td>
677
        <td>[% previssue.replacementprice %]</td>
678
      [% IF ( previssue.renew_failed ) %]
679
            <td class="problem">Renewal failed</td>
680
      [% ELSE %]
681
        <td><span style="padding: 0 1em;">[% IF ( previssue.renewals ) %][% previssue.renewals %][% ELSE %]0[% END %]</span>
682
        [% IF ( previssue.can_renew ) %]
683
        <input type="checkbox" name="all_items[]" value="[% previssue.itemnumber %]" checked="checked" style="display: none;" />
684
        [% IF ( previssue.od ) %]
685
            <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" checked="checked" />
686
        [% ELSE %]
687
            <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" />
688
        [% END %]
689
            [% IF previssue.renewsallowed && previssue.renewsleft %]
690
                <span class="renewals">([% previssue.renewsleft %] of [% previssue.renewsallowed %] renewals remaining)</span>
691
            [% END %]
692
        [% ELSE %]
693
            [% IF ( previssue.can_confirm ) %]<span class="renewals-allowed" style="display: none">
694
                <input type="checkbox" name="all_items[]" value="[% previssue.itemnumber %]" checked="checked" style="display: none;" />
695
                [% IF ( previssue.od ) %]
696
                    <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" checked="checked" />
697
                [% ELSE %]
698
                    <input type="checkbox" class="radio" name="items[]" value="[% previssue.itemnumber %]" />
699
                [% END %]
700
                </span>
701
                [% IF previssue.renewsallowed && previssue.renewsleft && !previssue.renew_error_too_soon %]
702
                    <span class="renewals">([% previssue.renewsleft %] of [% previssue.renewsallowed %] renewals remaining)</span>
703
                [% END %]
704
                <span class="renewals-disabled">
705
            [% END %]
706
            [% IF ( previssue.renew_error_on_reserve ) %]
707
                <a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% previssue.biblionumber %]">On Hold</a>
708
            [% ELSIF ( previssue.renew_error_too_many ) %]
709
                Not renewable
710
            [% ELSIF ( previssue.renew_error_too_soon ) %]
711
                No renewal before [% previssue.soonestrenewdate %]
712
                <span class="renewals">([% previssue.renewsleft %] of [% previssue.renewsallowed %] renewals remaining)</span>
713
            [% END %]
714
            [% IF ( previssue.can_confirm ) %]
715
                </span>
716
            [% END %]
717
        [% END %]
718
        </td>
719
        [% END %]
720
		  [% IF ( previssue.return_failed ) %]
721
            <td class="problem">Check-in failed</td>
722
        [% ELSE %]
723
            [% IF ( previssue.renew_error_on_reserve ) %]
724
               <td><a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% previssue.biblionumber %]">On hold</a>
725
                <input type="checkbox" name="all_barcodes[]" value="[% previssue.barcode %]" checked="checked" style="display: none;" />
726
                </td>
727
            [% ELSE %]
728
            <td><input type="checkbox" class="radio" name="barcodes[]"  value="[% previssue.barcode %]" />
729
                <input type="checkbox" name="all_barcodes[]" value="[% previssue.barcode %]" checked="checked" style="display: none;" />
730
            </td>
731
            [% END %]
732
        [% END %]
733
        [% IF ( exports_enabled ) %]
734
          <td style="text-align:center;">
735
            <input type="checkbox" id="export_[% previssue.biblionumber %]" name="biblionumbers" value="[% previssue.biblionumber %]" />
736
            <input type="checkbox" name="itemnumbers" value="[% previssue.itemnumber %]" style="visibility:hidden;" />
737
          </td>
738
        [% END %]
739
    </tr>
740
    <!-- /loop previssues -->[% END %]
741
<!--/if previssues -->[% END %]
742
      </tbody>
743
    </table>
744
    [% IF ( issuecount ) %]
745
    <fieldset class="action">
746
        [% IF ( CAN_user_circulate_override_renewals ) %]
747
        [% IF ( AllowRenewalLimitOverride ) %]
748
        <label for="override_limit">Override renewal limit:</label>
749
        <input type="checkbox" name="override_limit" id="override_limit" value="1" />
750
        [% END %]
751
        [% END %]
752
        <input type="submit" name="renew_checked" value="Renew or Return checked items" />
753
        <input type="submit" id="renew_all" name="renew_all" value="Renew all" />
754
    </fieldset>
755
        [% IF ( exports_enabled ) %]
756
            <fieldset>
757
            <label for="export_formats"><b>Export checkouts using format:</b></label>
758
            <select name="export_formats" id="export_formats">
759
                <option value="iso2709_995">ISO2709 with items</option>
760
                <option value="iso2709">ISO2709 without items</option>
761
                [% IF ( export_with_csv_profile ) %]
762
                    <option value="csv">CSV</option>
763
                [% END %]
764
765
            </select>
766
           <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" />
767
            <input type="hidden" name="op" value="export" />
768
            <input type="hidden" id="export_format" name="format" value="iso2709" />
769
            <input type="hidden" id="dont_export_item" name="dont_export_item" value="0" />
770
            <input type="hidden" id="record_type" name="record_type" value="bibs" />
771
            <input type="button" id="export_submit" value="Export" />
772
            </fieldset>
773
        [% END %]
774
    [% END %]
775
</form>
776
<p ng-hide="checkouts.length">Patron has nothing checked out.</p>
777
778
</div>
779
780
781
[% IF ( displayrelissues ) %]
782
<div id="relissues">
783
    <table id="relissuest">
784
    <thead>
785
    <tr>
786
        <th scope="col" class="title-string">Due date</th>
787
        <th scope="col" class="anti-the">Title</th>
788
        <th scope="col">Item type</th>
789
        <th scope="col" class="title-string">Checked out on</th>
790
        <th scope="col">Checked out from</th>
791
        <th scope="col">Call no</th>
792
        <th scope="col">Charge</th>
793
        <th scope="col">Price</th>
794
        <th scope="col" class="html-content">Patron</th>
795
    </tr>
796
    </thead>
797
[% IF ( relissues ) %]	<tbody>
798
799
    [% FOREACH relissue IN relissues %]
800
    [% IF ( loop.odd ) %]
801
    <tr>
802
    [% ELSE %]
803
    <tr class="highlight">
804
    [% END %]
805
        [% IF ( relissue.overdue ) %]<td class="od">[% ELSE %]<td>[% END %]
806
            <span title="[% relissue.dd_sort %]">[% relissue.dd %]</span></td>
807
808
            [% IF ( relissue.itemlost ) %]
809
                <span class="lost">[% AuthorisedValues.GetByCode( 'LOST', relissue.itemlost ) %]</span>
810
            [% END %]
811
            [% IF ( relissue.damaged ) %]
812
                <span class="dmg">[% AuthorisedValues.GetByCode( 'DAMAGED', relissue.damaged ) %]</span>
813
            [% END %]
814
        </td>
815
        <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% relissue.biblionumber %]&amp;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 %]&amp;itemnumber=[% relissue.itemnumber %]#item[% relissue.itemnumber %]">[% relissue.barcode %]</a></td>
816
        <td>[% UNLESS ( noItemTypeImages ) %] [% IF ( relissue.itemtype_image ) %]<img src="[% relissue.itemtype_image %]" alt="" />[% END %][% END %][% relissue.itemtype %]</td>
817
        <td><span title="[% relissue.displaydate_sort %]">[% relissue.displaydate %]</span></td>
818
        <td>[% relissue.issuingbranchname %]</td>
819
        <td>[% relissue.itemcallnumber %]</td>
820
        <td>[% relissue.charge %]</td>
821
        <td>[% relissue.replacementprice %]</td><td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% relissue.borrowernumber %]">[% relissue.firstname %] [% relissue.surname %] ([% relissue.cardnumber %])</a></td>
822
     </tr>
823
    [% END %] <!-- /loop relissues -->
824
    <!-- /if relissues -->[% END %]
825
[% IF ( relprevissues ) %]
826
    [% 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 %]
827
    [% FOREACH relprevissue IN relprevissues %]
828
    [% IF ( loop.odd ) %]
829
        <tr>
830
    [% ELSE %]
831
        <tr class="highlight">
832
    [% END %]
833
        [% IF ( relprevissue.overdue ) %]<td class="od">[% ELSE %]<td>[% END %]
834
        <span title="[% relprevissue.dd_sort %]">[% relprevissue.dd %]</span>
835
        </td>
836
        <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% relprevissue.biblionumber %]&amp;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 %]&amp;itemnumber=[% relprevissue.itemnumber %]#item[% relprevissue.itemnumber %]">[% relprevissue.barcode %]</a></td>
837
        <td>[% UNLESS noItemTypeImages %][% IF relprevissue.itemtype_image %]<img src="[% relprevissue.itemtype_image %]" alt="" />[% END %][% END %][% relprevissue.itemtype %]</td>
838
        <td><span title="[% relprevissue.displaydate_sort %]">[% relprevissue.displaydate %]</span></td>
839
        <td>[% relprevissue.issuingbranchname %]</td>
840
        <td>[% relprevissue.itemcallnumber %]</td>
841
	[% IF ( relprevissue.multiple_borrowers ) %]<td>[% relprevissue.firstname %] [% relprevissue.surname %]</td>[% END %]
842
        <td>[% relprevissue.charge %]</td>
843
        <td>[% relprevissue.replacementprice %]</td>
844
        <td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% relprevissue.borrowernumber %]">[% relprevissue.firstname %] [% relprevissue.surname %] ([% relprevissue.cardnumber %])</a></td>
845
846
    </tr>
847
    <!-- /loop relprevissue -->[% END %]
848
<!--/if relprevissues -->[% END %]
849
      </tbody>
850
    </table>
851
852
</div>
853
[% END %]<!-- end displayrelissues -->
854
855
[% INCLUDE borrower_debarments.inc %]
856
857
<div id="reserves">
858
[% IF ( reservloop ) %]
859
    <form action="/cgi-bin/koha/reserve/modrequest.pl" method="post">
860
	<input type="hidden" name="from" value="circ" />
861
    <table id="holdst">
862
        <thead><tr>
863
            <th>Hold date</th>
864
            <th>Title</th>
865
            <th>Call number</th>
866
            <th>Barcode</th>
867
            <th>Expiration</th>
868
            <th>Priority</th>
869
            <th>Delete?</th>
870
            <th>&nbsp;</th>
871
        </tr></thead>
872
		<tbody>
873
        [% FOREACH reservloo IN reservloop %]
874
        <tr class="[% reservloo.color %]">
875
                    <td>[% reservloo.reservedate %]</td>
876
                    <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>
877
                    <td>[% reservloo.itemcallnumber %]</td>
878
					<td><em>[% IF ( reservloo.barcodereserv ) %]Item [% reservloo.barcodereserv %]
879
                        [% END %][% IF ( reservloo.waiting ) %] <strong>waiting at [% reservloo.waitingat %]</strong>
880
                        [% END %]
881
                        [% IF ( reservloo.transfered ) %] <strong>in transit</strong> from
882
                        [% reservloo.frombranch %] since [% reservloo.datesent %]
883
                        [% END %]
884
                        [% IF ( reservloo.nottransfered ) %] hasn't been transferred yet from [% reservloo.nottransferedby %]</i>
885
                        [% END %]</em></td>
886
                    <td>[% reservloo.expirationdate | $KohaDates %]</td>
887
                    <td>
888
                        [% IF ( reservloo.waitingposition ) %]<b> [% reservloo.waitingposition %] </b>[% END %]
889
                    </td>
890
				<td><select name="rank-request">
891
                    <option value="n">No</option>
892
                    <option value="del">Yes</option>
893
                </select>
894
                <input type="hidden" name="biblionumber" value="[% reservloo.biblionumber %]" />
895
                <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
896
                <input type="hidden" name="reserve_id" value="[% reservloo.reserve_id %]" />
897
            </td>
898
            <td>[% IF ( reservloo.suspend ) %]Suspended [% IF ( reservloo.suspend_until ) %] until [% reservloo.suspend_until | $KohaDates %][% END %][% END %]</td>
899
            </tr>
900
        [% END %]</tbody>
901
    </table>
902
            <fieldset class="action"><input type="submit" class="cancel" name="submit" value="Cancel marked holds" /></fieldset>
903
    </form>
904
905
    [% IF SuspendHoldsIntranet %]
906
    <fieldset class="action">
907
        <form action="/cgi-bin/koha/reserve/modrequest_suspendall.pl" method="post">
908
            <input type="hidden" name="from" value="circ" />
909
            <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
910
            <input type="submit" value="Suspend all holds" />
911
912
            [% IF AutoResumeSuspendedHolds %]
913
            <label for="suspend_until">until</label>
914
            <input type="text" size="10" id="suspend_until" name="suspend_until" class="datepicker" />
915
            <span class="hint">Specify date on which to resume [% INCLUDE 'date-format.inc' %]: </span>
916
             [% END %]
917
        </form>
918
    </fieldset>
919
920
    <fieldset class="action">
921
        <form action="/cgi-bin/koha/reserve/modrequest_suspendall.pl" method="post">
922
            <input type="hidden" name="from" value="circ" />
923
            <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
924
            <input type="hidden" name="suspend" value="0" />
925
            <input type="submit" value="Resume all suspended holds" />
926
	</form>
927
    </fieldset>
928
    [% END # IF SuspendHoldsIntranet %]
929
930
[% ELSE %]
931
	<p>Patron has nothing on hold.</p>
932
[% END %]
933
</div> <!-- reservesloop -->
934
935
</div></div>
936
937
938
939
</div>
940
</div>
941
<div class="yui-b">
942
[% INCLUDE 'circ-menu.inc' %]
943
</div>
944
</div>
945
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/svc/patrons (+23 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright (C) 2014 ByWater Solutions
6
#
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.
11
#
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.
16
#
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>.
19
20
use Modern::Perl;
21
22
use Koha::Service::Patrons;
23
Koha::Service::Patrons->new->run;
(-)a/t/db_dependent/Members.t (-1 / +17 lines)
Lines 17-24 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 72;
20
use Test::More tests => 73;
21
use Test::MockModule;
21
use Test::MockModule;
22
use Koha::Service::Patrons;
22
use Data::Dumper;
23
use Data::Dumper;
23
use C4::Context;
24
use C4::Context;
24
25
Lines 113-118 ok ( $member->{firstname} eq $FIRSTNAME && Link Here
113
114
114
is($member->{dateofbirth}, undef, "Empty dates handled correctly");
115
is($member->{dateofbirth}, undef, "Empty dates handled correctly");
115
116
117
my $service = Koha::Service::Patrons->new;
118
my $borrowernumber = $member->{borrowernumber};
119
$service->test( "GET", "/$borrowernumber/patronInfo" );
120
my $jsonresponse = $service->dispatch->{patronInfo};
121
122
ok ( $jsonresponse->{firstname}    eq $FIRSTNAME    &&
123
     $jsonresponse->{surname}      eq $SURNAME      &&
124
     $jsonresponse->{categorycode} eq $CATEGORYCODE &&
125
     $jsonresponse->{branchcode}   eq $BRANCHCODE
126
     , "Got member")
127
  or diag("Mismatching member details: ".Dumper(\%data, $jsonresponse));
128
129
C4::Context->_unset_userenv;
130
C4::Context->set_userenv ( @USERENV );
131
116
$member->{firstname} = $CHANGED_FIRSTNAME;
132
$member->{firstname} = $CHANGED_FIRSTNAME;
117
$member->{email}     = $EMAIL;
133
$member->{email}     = $EMAIL;
118
$member->{ethnicity} = $ETHNICITY;
134
$member->{ethnicity} = $ETHNICITY;
(-)a/t/db_dependent/Reserves.t (-2 / +15 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 53;
20
use Test::More tests => 55;
21
21
22
use MARC::Record;
22
use MARC::Record;
23
use DateTime::Duration;
23
use DateTime::Duration;
Lines 27-41 use C4::Biblio; Link Here
27
use C4::Items;
27
use C4::Items;
28
use C4::Members;
28
use C4::Members;
29
use C4::Circulation;
29
use C4::Circulation;
30
use JSON;
30
use t::lib::Mocks;
31
use t::lib::Mocks;
32
use Test::WWW::Mechanize;
31
33
32
use Koha::DateUtils;
34
use Koha::DateUtils;
33
35
34
use Data::Dumper;
36
use Data::Dumper;
35
BEGIN {
37
BEGIN {
36
    use_ok('C4::Reserves');
38
    use_ok('C4::Reserves');
39
    use_ok('Koha::Service::Patrons');
37
}
40
}
38
41
42
43
39
# a very minimal mack of userenv for use by the test of DelItemCheck
44
# a very minimal mack of userenv for use by the test of DelItemCheck
40
*C4::Context::userenv = sub {
45
*C4::Context::userenv = sub {
41
    return {};
46
    return {};
Lines 127-132 is($status, "Reserved", "CheckReserves Test 2"); Link Here
127
($status, $reserve, $all_reserves) = CheckReserves(undef, $barcode);
132
($status, $reserve, $all_reserves) = CheckReserves(undef, $barcode);
128
is($status, "Reserved", "CheckReserves Test 3");
133
is($status, "Reserved", "CheckReserves Test 3");
129
134
135
my $service = Koha::Service::Patrons->new;
136
$service->test( "GET", "/$borrowernumber/holds" );
137
my $jsonresponse = $service->dispatch;
138
139
ok(
140
    ref($jsonresponse->{holds}) eq "ARRAY" && scalar @{ $jsonresponse->{holds} } == 1,
141
    "checks to make sure that API returns one hold"
142
);
143
130
my $ReservesControlBranch = C4::Context->preference('ReservesControlBranch');
144
my $ReservesControlBranch = C4::Context->preference('ReservesControlBranch');
131
C4::Context->set_preference( 'ReservesControlBranch', 'ItemHomeLibrary' );
145
C4::Context->set_preference( 'ReservesControlBranch', 'ItemHomeLibrary' );
132
ok(
146
ok(
133
- 

Return to bug 13630