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

(-)a/Koha/REST/V1/Patrons/Holds.pm (+80 lines)
Line 0 Link Here
1
package Koha::REST::V1::Patrons::Holds;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# along with Koha; if not, see <http://www.gnu.org/licenses>.
16
17
use Modern::Perl;
18
19
use Mojo::Base 'Mojolicious::Controller';
20
21
use Koha::Patrons;
22
23
use Scalar::Util qw(blessed);
24
use Koha::DateUtils;
25
use Try::Tiny;
26
27
=head1 NAME
28
29
Koha::REST::V1::Patrons::Holds
30
31
=head1 API
32
33
=head2 Methods
34
35
=head3 list
36
37
Mehtod that handles listing patron's holds
38
39
=cut
40
41
sub list {
42
    my $c = shift->openapi->valid_input or return;
43
    my $patron_id = $c->validation->param('patron_id');
44
    my $old = $c->validation->param('old');
45
    my $patron    = Koha::Patrons->find($patron_id);
46
47
    unless ($patron) {
48
        return $c->render( status => 404, openapi => { error => "Patron not found." } );
49
    }
50
51
    return try {
52
        my $holds_set;
53
        if($old) {
54
            $holds_set = Koha::Old::Holds->new;
55
        } else {
56
            $holds_set = Koha::Holds->new;
57
        }
58
59
        delete $c->validation->output->{old};
60
        #$c->stash_embed( {spec => $c->match->endpoint->pattern->defaults->{'openapi.op_spec'}} );
61
        my $holds     = $c->objects->search( $holds_set );
62
        return $c->render( status => 200, openapi => $holds );
63
    }
64
    catch {
65
        if ( blessed $_ && $_->isa('Koha::Exceptions') ) {
66
            return $c->render(
67
                status  => 500,
68
                openapi => { error => "$_" }
69
            );
70
        }
71
        else {
72
            return $c->render(
73
                status  => 500,
74
                openapi => { error => "Something went wrong, check Koha logs for details." }
75
            );
76
        }
77
    };
78
}
79
80
1;
(-)a/api/v1/swagger/paths.json (+3 lines)
Lines 104-109 Link Here
104
  "/public/patrons/{patron_id}/guarantors/can_see_checkouts": {
104
  "/public/patrons/{patron_id}/guarantors/can_see_checkouts": {
105
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts"
105
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts"
106
  },
106
  },
107
  "/public/patrons/{patron_id}/holds": {
108
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1holds"
109
  },
107
  "/return_claims": {
110
  "/return_claims": {
108
    "$ref": "paths/return_claims.json#/~1return_claims"
111
    "$ref": "paths/return_claims.json#/~1return_claims"
109
  },
112
  },
(-)a/api/v1/swagger/paths/public_patrons.json (+188 lines)
Lines 236-240 Link Here
236
                "allow-owner": true
236
                "allow-owner": true
237
            }
237
            }
238
        }
238
        }
239
    },
240
    "/public/patrons/{patron_id}/holds": {
241
        "get": {
242
            "x-mojo-to": "Patrons::Holds#list",
243
            "operationId": "listPatronsOldHolds",
244
            "tags": [
245
                "patron"
246
            ],
247
            "parameters": [
248
                {
249
                    "$ref": "../parameters.json#/patron_id_pp"
250
                },
251
                {
252
                    "name": "hold_id",
253
                    "in": "query",
254
                    "description": "Internal reserve identifier",
255
                    "type": "integer"
256
                  },
257
                  {
258
                    "name": "hold_date",
259
                    "in": "query",
260
                    "description": "Hold",
261
                    "type": "string",
262
                    "format": "date"
263
                  },
264
                  {
265
                    "name": "biblio_id",
266
                    "in": "query",
267
                    "description": "Internal biblio identifier",
268
                    "type": "integer"
269
                  },
270
                  {
271
                    "name": "pickup_library_id",
272
                    "in": "query",
273
                    "description": "Internal library identifier for the pickup library",
274
                    "type": "string"
275
                  },
276
                  {
277
                    "name": "cancelation_date",
278
                    "in": "query",
279
                    "description": "The date the hold was cancelled",
280
                    "type": "string",
281
                    "format": "date"
282
                  },
283
                  {
284
                    "name": "notes",
285
                    "in": "query",
286
                    "description": "Notes related to this hold",
287
                    "type": "string"
288
                  },
289
                  {
290
                    "name": "priority",
291
                    "in": "query",
292
                    "description": "Where in the queue the patron sits",
293
                    "type": "integer"
294
                  },
295
                  {
296
                    "name": "status",
297
                    "in": "query",
298
                    "description": "Found status",
299
                    "type": "string"
300
                  },
301
                  {
302
                    "name": "timestamp",
303
                    "in": "query",
304
                    "description": "Time of latest update",
305
                    "type": "string"
306
                  },
307
                  {
308
                    "name": "item_id",
309
                    "in": "query",
310
                    "description": "Internal item identifier",
311
                    "type": "integer"
312
                  },
313
                  {
314
                    "name": "waiting_date",
315
                    "in": "query",
316
                    "description": "Date the item was marked as waiting for the patron",
317
                    "type": "string"
318
                  },
319
                  {
320
                    "name": "expiration_date",
321
                    "in": "query",
322
                    "description": "Date the hold expires",
323
                    "type": "string"
324
                  },
325
                  {
326
                    "name": "lowest_priority",
327
                    "in": "query",
328
                    "description": "Lowest priority",
329
                    "type": "boolean"
330
                  },
331
                  {
332
                    "name": "suspended",
333
                    "in": "query",
334
                    "description": "Suspended",
335
                    "type": "boolean"
336
                  },
337
                  {
338
                    "name": "suspended_until",
339
                    "in": "query",
340
                    "description": "Suspended until",
341
                    "type": "string"
342
                  },
343
                  {
344
                    "name": "old",
345
                    "in": "query",
346
                    "description": "Fetch holds history",
347
                    "type": "boolean"
348
                  },
349
                  {
350
                      "$ref": "../parameters.json#/match"
351
                  },
352
                  {
353
                      "$ref": "../parameters.json#/order_by"
354
                  },
355
                  {
356
                      "$ref": "../parameters.json#/page"
357
                  },
358
                  {
359
                      "$ref": "../parameters.json#/per_page"
360
                  },
361
                  {
362
                      "$ref": "../parameters.json#/q_param"
363
                  },
364
                  {
365
                      "$ref": "../parameters.json#/q_body"
366
                  },
367
                  {
368
                      "$ref": "../parameters.json#/q_header"
369
                  }
370
            ],
371
            "produces": [
372
                "application/json"
373
            ],
374
            "responses": {
375
                "200": {
376
                    "description": "List of holds history",
377
                    "schema": {
378
                        "$ref": "../definitions.json#/holds"
379
                    }
380
                },
381
                "400": {
382
                    "description": "Bad request",
383
                    "schema": {
384
                        "$ref": "../definitions.json#/error"
385
                    }
386
                },
387
                "401": {
388
                    "description": "Authentication required",
389
                    "schema": {
390
                        "$ref": "../definitions.json#/error"
391
                    }
392
                },
393
                "403": {
394
                    "description": "Access forbidden",
395
                    "schema": {
396
                        "$ref": "../definitions.json#/error"
397
                    }
398
                },
399
                "404": {
400
                    "description": "Patron not found",
401
                    "schema": {
402
                        "$ref": "../definitions.json#/error"
403
                    }
404
                },
405
                "500": {
406
                    "description": "Internal server error",
407
                    "schema": {
408
                        "$ref": "../definitions.json#/error"
409
                    }
410
                },
411
                "503": {
412
                    "description": "Under maintenance",
413
                    "schema": {
414
                        "$ref": "../definitions.json#/error"
415
                    }
416
                }
417
            },
418
            "x-koha-authorization": {
419
                "allow-owner": true
420
            },
421
            "x-koha-embed": [
422
                "item",
423
                "biblio",
424
                "branch"
425
            ]
426
        }
239
    }
427
    }
240
}
428
}
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc (-7 / +17 lines)
Lines 58-71 Link Here
58
                <a href="/cgi-bin/koha/opac-search-history.pl">your search history</a></li>
58
                <a href="/cgi-bin/koha/opac-search-history.pl">your search history</a></li>
59
            [% END %]
59
            [% END %]
60
60
61
            [% IF ( opacreadinghistory ) %]
61
            [% IF opacreadinghistory || Koha.Preference('OPACHoldsHistory') == 1 %]
62
                [% IF ( readingrecview ) %]
62
                [% IF opacreadinghistory %]
63
                    <li class="active">
63
                    [% IF ( readingrecview ) %]
64
                [% ELSE %]
64
                        <li class="active">
65
                    <li>
65
                    [% ELSE %]
66
                        <li>
67
                    [% END %]
68
                    <a href="/cgi-bin/koha/opac-readingrecord.pl">your reading history</a></li>
69
                [% END %]
70
                [% IF Koha.Preference('OPACHoldsHistory') == 1 %]
71
                    [% IF ( holdsrecview ) %]
72
                        <li class="active">
73
                    [% ELSE %]
74
                        <li>
75
                    [% END %]
76
                    <a href="/cgi-bin/koha/opac-holdsrecord.pl">your holds history</a></li>
66
                [% END %]
77
                [% END %]
67
                <a href="/cgi-bin/koha/opac-readingrecord.pl">your reading history</a></li>
78
                [% IF ( OPACPrivacy || Koha.Preference('OPACHoldsPrivacy') == 1 ) %]
68
                [% IF ( OPACPrivacy ) %]
69
                    [% IF ( privacyview ) %]
79
                    [% IF ( privacyview ) %]
70
                        <li class="active">
80
                        <li class="active">
71
                    [% ELSE %]
81
                    [% ELSE %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-holdsrecord.tt (+239 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Koha %]
3
[% USE KohaDates %]
4
[% INCLUDE 'doc-head-open.inc' %]
5
<title>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle | html %][% ELSE %]Koha online[% END %] catalog &rsaquo; Your holds history</title>
6
[% INCLUDE 'doc-head-close.inc' %]
7
[% BLOCK cssinclude %]
8
    <style>
9
        .controls {
10
            font-size: 75%;
11
        }
12
13
        .controls .paginate_button {
14
            font-family: 'FontAwesome';
15
            text-decoration: none;
16
        }
17
18
        .controls .paginate_button:not(.disabled) {
19
            cursor: pointer;
20
        }
21
22
        .controls .paginate_button.disabled {
23
            color: grey;
24
        }
25
26
        .controls .previous:before {
27
            content: "\f104";
28
            padding-right: .5em;
29
        }
30
31
        .controls .next:after {
32
            content: "\f105";
33
            padding-left: .5em;
34
        }
35
    </style>
36
[% END %]
37
</head>
38
[% INCLUDE 'bodytag.inc' bodyid='opac-holdsrecord' %]
39
[% INCLUDE 'masthead.inc' %]
40
41
<div class="main">
42
    <ul class="breadcrumb">
43
        <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">&rsaquo;</span></li>
44
        <li><a href="/cgi-bin/koha/opac-user.pl">[% INCLUDE 'patron-title.inc' patron = logged_in_user %]</a> <span class="divider">&rsaquo;</span></li>
45
        <li><a href="#">Your holds history</a></li>
46
    </ul>
47
48
    <div class="container-fluid">
49
        <div class="row-fluid">
50
            <div class="span2">
51
                <div id="navigation">
52
                    [% INCLUDE 'navigation.inc' IsPatronPage=1 %]
53
                </div>
54
            </div>
55
            <div class="span10">
56
                <div id="userholdsrecord">
57
                    <h3>Holds history</h3>
58
59
                    [% IF READING_RECORD.size == 0 %]
60
                        You have never placed a hold from this library.
61
                    [% ELSE %]
62
                        <div id="opac-user-holdsrec">
63
                            <div id="tabs-container" style="overflow:auto">
64
                                <div class="controls">
65
                                    <div class="span3 info">
66
67
                                    </div>
68
                                    <div class="span2">
69
                                        <input type="text" class="search" placeholder="search" autofocus/>
70
                                    </div>
71
                                    <div class="span1">
72
                                        <a class="paginate_button previous disabled" aria-controls="holdsrec" data-dt-idx="1">Previous</a>
73
                                    </div>
74
                                    <div class="span1">
75
                                        <a class="paginate_button next disabled" aria-controls="holdsrec" data-dt-idx="1">Next</a>
76
                                    </div>
77
                                </div>
78
                                <table id="holdsrec" class="table table-bordered table-striped">
79
                                    <thead>
80
                                        <tr>
81
                                            <th class="anti-the">Title</th>
82
                                            <th class="">Author</th>
83
                                            <th class="">Barcode</th>
84
                                            <th class="">Library</th>
85
                                            <th class="title-string">Hold date</th>
86
                                            <th class="title-string">Expiration date</th>
87
                                            <th class="title-string">Waiting date</th>
88
                                            <th class="title-string">Cancellation date</th>
89
                                            <th>Status</th>
90
                                        </tr>
91
                                    </thead>
92
                                    <tbody>
93
94
                                    </tbody>
95
                                </table>
96
                                <div class="controls">
97
                                    <div class="span3 info">
98
99
                                    </div>
100
                                    <div class="span1">
101
                                        <a class="paginate_button previous disabled" aria-controls="holdsrec" data-dt-idx="1">Previous</a>
102
                                    </div>
103
                                    <div class="span1">
104
                                        <a class="paginate_button next disabled" aria-controls="holdsrec" data-dt-idx="1">Next</a>
105
                                    </div>
106
                                </div>
107
                            </div> <!-- / .tabs-container -->
108
                        </div> <!-- / .opac-user-readingrec -->
109
                    [% END # / IF READING_RECORD.size %]
110
                </div> <!-- / .userreadingrecord -->
111
            </div> <!-- / .span10 -->
112
        </div> <!-- / .row-fluid -->
113
    </div> <!-- / .container-fluid -->
114
</div> <!-- / .main -->
115
116
[% INCLUDE 'opac-bottom.inc' %]
117
[% BLOCK jsinclude %]
118
[% INCLUDE 'datatables.inc' %]
119
[% INCLUDE 'js-date-format.inc' %]
120
<script>
121
    $(document).ready(function(){
122
        [% IF ( GoogleJackets ) %]KOHA.Google.GetCoverFromIsbn();[% END %]
123
        $('#order').change(function() {
124
            $('#sortform').submit();
125
        });
126
127
        var table = $("#holdsrec").api({
128
            "ajax": {
129
                "url": "/api/v1/public/patrons/[% borrowernumber | html %]/holds?old=true"
130
            },
131
            "embed": [
132
                "item",
133
                "biblio",
134
                "branch"
135
            ],
136
            'order': [[ 4, "desc" ]],
137
            "columnDefs": [
138
                { "targets": [ "nosort" ],"sortable": false,"searchable": false },
139
                { "type": "anti-the", "targets" : [ "anti-the" ] },
140
                { "type": "title-string", "targets" : [ "title-string" ] }
141
            ],
142
            "columns": [
143
                {
144
                    "data": "biblio.title",
145
                    "render": function(data, type, row, meta) {
146
                        if (type != 'display') return data;
147
                        return '<a href="opac-detail.pl?biblionumber='+row.biblio_id+'">'+data+'</a>';
148
                    }
149
                },
150
                {"data": "biblio.author"},
151
                {
152
                    "data": "item.external_id",
153
                    "render": function(data, type, row, meta) {
154
                        if(data) return data;
155
                        return '';
156
                    }
157
                },
158
                {"data": "branch.name"},
159
                {
160
                    "data": "hold_date",
161
                    "render": function(data, type, row, meta) {
162
                        return (data&&$date(data))||'';
163
                    }
164
                },
165
                {
166
                    "data": "expiration_date",
167
                    "render": function(data, type, row, meta) {
168
                        return (data&&$date(data))||'';
169
                    }
170
                },
171
                {
172
                    "data": "waiting_date",
173
                    "render": function(data, type, row, meta) {
174
                        return (data&&$date(data))||'';
175
                    }
176
                },
177
                {
178
                    "data": "cancelation_date",
179
                    "render": function(data, type, row, meta) {
180
                        return (data&&$date(data))||'';
181
                    }
182
                },
183
                {
184
                    "data": "status",
185
                    "render": function(data, type, row, meta) {
186
                        if (type != 'display') return data;
187
                        if(row.cancelation_date) return 'Canceled';
188
                        if (data == 'F') return 'Fulfilled';
189
                        if (data == 'W') return 'Waiting';
190
                        if (data == 'T') return 'In Transit';
191
                        return 'Pending';
192
                    }
193
                }
194
            ]
195
        }).DataTable();
196
197
        table.on('draw.dt', function(event, settings) {
198
            var page_info = table.page.info();
199
            $(".controls .info").html(_("Showing page %s of %s").format(page_info.page+1, page_info.pages));
200
            if(!page_info.page) {
201
                $('.controls .previous').addClass('disabled');
202
            } else {
203
                $('.controls .previous').removeClass('disabled');
204
            }
205
            if(page_info.pages-1===page_info.page) {
206
                $('.controls .next').addClass('disabled');
207
            } else {
208
                $('.controls .next').removeClass('disabled');
209
            }
210
        })
211
212
        $('.controls .previous').click(function() {
213
            table.page('previous').draw('page');
214
        });
215
216
        $('.controls .next').click(function() {
217
            table.page('next').draw('page');
218
        });
219
        function debounce(func, wait, immediate) {
220
            var timeout;
221
            return function() {
222
                var context = this, args = arguments;
223
                var later = function() {
224
                    timeout = null;
225
                    if (!immediate) func.apply(context, args);
226
                };
227
                var callNow = immediate && !timeout;
228
                clearTimeout(timeout);
229
                timeout = setTimeout(later, wait);
230
                if (callNow) func.apply(context, args);
231
            };
232
        };
233
234
        $(".controls .search").keypress(debounce(function(event) {
235
            table.search(this.value).draw();
236
        }, 300));
237
    });
238
</script>
239
[% END %]
(-)a/opac/opac-holdsrecord.pl (-1 / +63 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
19
use Modern::Perl;
20
21
use CGI qw ( -utf8 );
22
23
use C4::Auth;
24
use C4::Koha;
25
use C4::Biblio;
26
use C4::Circulation;
27
use C4::Members;
28
use Koha::DateUtils;
29
use MARC::Record;
30
31
use C4::Output;
32
use C4::Charset qw(StripNonXmlChars);
33
use Koha::Patrons;
34
35
use Koha::ItemTypes;
36
use Koha::Ratings;
37
38
my $query = new CGI;
39
40
# if opacreadinghistory is disabled, leave immediately
41
if ( ! C4::Context->preference('OPACHoldsHistory') ) {
42
    print $query->redirect("/cgi-bin/koha/errors/404.pl");
43
    exit;
44
}
45
46
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
47
    {
48
        template_name   => "opac-holdsrecord.tt",
49
        query           => $query,
50
        type            => "opac",
51
        debug           => 1,
52
    }
53
);
54
55
my $borr = Koha::Patrons->find( $borrowernumber )->unblessed;
56
57
$template->param(%{$borr});
58
59
$template->param(
60
    holdsrecview => 1,
61
);
62
63
output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 };

Return to bug 20936