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

(-)a/circ/batch_holding.pl (+181 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2019 BibLibre
4
#
5
# This file is part of Koha.
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
use CGI qw ( -utf8 );
22
23
use C4::Output;
24
use C4::Auth;
25
use Koha::Items;
26
use C4::Reserves;
27
use List::MoreUtils qw/uniq/;
28
29
my $input = new CGI;
30
my $op = $input->param('op') // '';
31
my @itemnumbers;
32
33
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
34
    {
35
        template_name   => "circ/batch_holding.tt",
36
        query           => $input,
37
        type            => "intranet",
38
        authnotrequired => 0,
39
    }
40
);
41
42
if ( $op eq "show" ) {
43
    my $filefh         = $input->upload('uploadfile');
44
    my $filecontent    = $input->param('filecontent');
45
    my $borrowernumber = $input->param('borrowernumber');
46
    my $branch         = $input->param('branch');
47
    my $action         = $input->param('action');
48
    my @notfoundbarcodes;
49
50
    my @contentlist;
51
    if ($filefh) {
52
        while ( my $content = <$filefh> ) {
53
            $content =~ s/[\r\n]*$//;
54
            push @contentlist, $content if $content;
55
        }
56
57
        foreach my $barcode (@contentlist) {
58
            my $item = Koha::Items->find( { barcode => $barcode } );
59
            if ($item) {
60
                push @itemnumbers, $item->itemnumber;
61
            }
62
            else {
63
                push @notfoundbarcodes, $barcode;
64
            }
65
        }
66
    }
67
    else {
68
        if ( my $list = $input->param('barcodelist') ) {
69
            push my @barcodelist, uniq( split( /\s\n/, $list ) );
70
71
            foreach my $barcode (@barcodelist) {
72
73
                my $item = Koha::Items->find( { barcode => $barcode } );
74
                if ($item) {
75
                    push @itemnumbers, $item->itemnumber;
76
                }
77
                else {
78
                    push @notfoundbarcodes, $barcode;
79
                }
80
            }
81
82
        }
83
    }
84
    if (@notfoundbarcodes) {
85
        my @notfoundbarcodesloop = map { { barcode => $_ } } @notfoundbarcodes;
86
        $template->param( notfoundbarcodes => \@notfoundbarcodesloop );
87
    }
88
    my @holdloop;
89
    if (@itemnumbers) {
90
        foreach my $itemnumber (@itemnumbers) {
91
            my $item = Koha::Items->find( { itemnumber => $itemnumber } );
92
            my $biblio =
93
              Koha::Biblios->find( { biblionumber => $item->biblionumber } );
94
            my $holdable =
95
              CanItemBeReserved( $borrowernumber, $item->itemnumber )->{status};
96
            push @holdloop,
97
              {
98
                'biblionumber' => $biblio->biblionumber,
99
                'title'        => $biblio->title,
100
                'itemnumber'   => $item->itemnumber,
101
                'barcode'      => $item->barcode,
102
                'holdable'     => ( $action eq "cancel" || $holdable eq 'OK' )
103
                ? 1
104
                : 0,
105
              };
106
        }
107
108
    }
109
    my $borrower = Koha::Patrons->find( { borrowernumber => $borrowernumber } );
110
    $template->param(
111
        show              => 1,
112
        holdloop          => \@holdloop,
113
        borrowernumber    => $borrowernumber,
114
        borrowersurname   => $borrower->surname,
115
        borrowerfirstname => $borrower->firstname,
116
        branch            => $branch,
117
        action            => $action,
118
        op                => $op
119
    );
120
}
121
122
elsif ( $op eq "result" ) {
123
    my $branch         = $input->param('branch');
124
    my $borrowernumber = $input->param('borrowernumber');
125
    my $action         = $input->param('action');
126
    my @itemnumbers    = $input->multi_param('holdable');
127
    my @holdloop;
128
    foreach my $itemnumber (@itemnumbers) {
129
        if ( $action eq "place" ) {
130
            my $item = Koha::Items->find( { itemnumber => $itemnumber } );
131
            my $biblionumber = $item->biblionumber;
132
            my $biblio =
133
              Koha::Biblios->find( { biblionumber => $item->biblionumber } );
134
            my $reserve_id = AddReserve(
135
                {
136
                    branchcode     => $branch,
137
                    borrowernumber => $borrowernumber,
138
                    biblionumber   => $biblionumber,
139
                    itemnumber     => $itemnumber
140
                }
141
            );
142
            push @holdloop,
143
              {
144
                'biblionumber' => $biblio->biblionumber,
145
                'title'        => $biblio->title,
146
                'itemnumber'   => $itemnumber,
147
                'barcode'      => $item->barcode,
148
                'reserve_id'   => $reserve_id,
149
              };
150
        }
151
        else {
152
            my $holds = Koha::Holds->search(
153
                {
154
                    itemnumber     => $itemnumber,
155
                    borrowernumber => $borrowernumber
156
                }
157
            );
158
            if ($holds) {
159
                while ( my $hold = $holds->next ) {
160
                    $hold->cancel;
161
                }
162
163
                push @holdloop, { 'itemnumber' => $itemnumber, };
164
            }
165
        }
166
    }
167
    my $borrower = Koha::Patrons->find( { borrowernumber => $borrowernumber } );
168
    $template->param(
169
        result            => 1,
170
        holdloop          => \@holdloop,
171
        borrowernumber    => $borrowernumber,
172
        borrowersurname   => $borrower->surname,
173
        borrowerfirstname => $borrower->firstname,
174
        branch            => $branch,
175
        cancel            => $action eq "cancel" ? 1 : 0,
176
        place             => $action eq "place" ? 1 : 0,
177
        op                => $op
178
    );
179
180
}
181
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/batch_holding.tt (+293 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% USE Branches %]
3
[% SET footerjs = 1 %]
4
[% INCLUDE 'doc-head-open.inc' %]
5
<title>
6
    Koha &rsaquo;
7
    Circulation &rsaquo;
8
    [% IF ( op == "show" ) %]
9
        Batch place or cancel holds &rsaquo;
10
        Confirm selection
11
    [% ELSIF ( op == "result") %]
12
        Batch place or cancel holds &rsaquo;
13
        Results
14
    [% ELSE %]
15
        Batch place or cancel holds
16
    [% END %]
17
</title>
18
[% INCLUDE 'doc-head-close.inc' %]
19
</head>
20
<body id="batch_holding">
21
22
[% INCLUDE 'header.inc' %]
23
[% INCLUDE 'cataloging-search.inc' %]
24
<div id="breadcrumbs">
25
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
26
    <a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a> &rsaquo;
27
    [% IF ( op == "show" ) %]
28
        <a href="/cgi-bin/koha/circ/batch_holding.pl">Batch place or cancel holds</a> &rsaquo;
29
        Confirm selection
30
    [% ELSIF ( op == "result") %]
31
        <a href="/cgi-bin/koha/circ/batch_holding.pl">Batch place or cancel holds</a> &rsaquo;
32
        Results
33
    [% ELSE %]
34
        Batch place or cancel holds
35
    [% END %]
36
</div>
37
38
<div class="main container-fluid">
39
    <div class="row">
40
        [% IF Koha.Preference('CircSidebar') %]
41
            <div class="col-sm-10 col-sm-push-2">
42
        [% ELSE %]
43
            <div class="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
44
        [% END %]
45
            <main>
46
47
                [% IF show %]
48
                    [% IF ( notfoundbarcodes ) %]
49
                        <div class="dialog alert"><p>Warning, the following barcodes were not found:</p></div>
50
51
                        <table>
52
                            <thead>
53
                                <tr><th>Barcodes not found</th></tr>
54
                            </thead>
55
                            <tbody>
56
                                [% FOREACH notfoundbarcode IN notfoundbarcodes %]
57
                                    <tr><td>[% notfoundbarcode.barcode | html %]</td></td>
58
                                [% END %]
59
                            </tbody>
60
                        </table>
61
                    [% END #/IF notfoundbarcodes %]
62
63
                    [% IF (holdloop) %]
64
                        [% IF ( action == "place" ) %]
65
                            <h1>Place holds on the following barcodes</h1>
66
                        [% ELSE %]
67
                            <h1>Cancel holds on the following barcodes</h1>
68
                        [% END %]
69
                        <ul>
70
                            <li>Patron: <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber | uri %]">[% borrowersurname | html %][% IF ( borrowerfirstname ) %], [% borrowerfirstname | html %][% END %]</a></li>
71
                            <li>Pickup location: [% Branches.GetName( branch ) | html %]</li>
72
                        </ul>
73
                        <div id="toolbar">
74
                            <a id="SelectAll" href="#"><i class="fa fa-check"></i> Select all</a> |
75
                            <a id="ClearAll" href="#"><i class="fa fa-remove"></i> Clear all</a>
76
                        </div>
77
78
                        <form action="batch_holding.pl" id="hold_selections" method="POST">
79
                            <table>
80
                                <thead><tr><th>Hold</th><th>Title</th><th>Item</th><th>Comment</th></tr></thead>
81
                                <tbody>
82
                                [% FOREACH hold IN holdloop %]
83
                                    <tr>
84
                                        <td>
85
                                            <input type="checkbox" name="holdable" [% IF !hold.holdable %]disabled="disabled"[% END %] checked="checked" value="[% hold.itemnumber | html %]" />
86
                                        </td>
87
                                        <td>
88
                                            <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.title | html %]</a>
89
                                        </td>
90
                                        <td>
91
                                            <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% hold.biblionumber | uri %]&itemnumber=[% hold.itemnumber | uri %]">[% hold.barcode | html %]</a>
92
                                        </td>
93
                                        <td>
94
                                            [% IF !hold.holdable %]
95
                                                <i class="fa fa-warning"></i>
96
                                                This item cannot be placed on hold
97
                                            [% END %]
98
                                        </td>
99
                                    </tr>
100
                                [% END %]
101
                                </tbody>
102
                            </table>
103
                            <input type="hidden" name="op" value="result" />
104
                            <input type="hidden" name="action" value="[% action | html %]" />
105
                            <input type="hidden" name="branch" value="[% branch | html %]" />
106
                            <input type="hidden" name="borrowernumber" value="[% borrowernumber | html %]" />
107
                            <fieldset class="action">
108
                                <input type="submit" class="submit" value="Submit" />
109
                                <a href="/cgi-bin/koha/circ/circulation-home.pl" class="cancel">Cancel</a>
110
                            </fieldset>
111
                        </form>
112
                    [% ELSE %]
113
                        <p>No holds can be placed or cancelled.</p>
114
                        <a href="batch_holding.pl">Batch place or cancel other holds</a>
115
                    [% END # /IF holdloop %]
116
117
                [% ELSIF result %]
118
119
                    [% IF (holdloop) %]
120
                        <h1>Results</h1>
121
                        [% IF place %]
122
                            <p>The following items have been placed on hold:</p>
123
                        [% END %]
124
                        [% IF cancel %]
125
                            <p>The following holds were cancelled:</p>
126
                        [% END %]
127
                        <ul>
128
                            <li>Patron: <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber | uri %]">[% borrowersurname | html %][% IF ( borrowerfirstname ) %], [% borrowerfirstname | html %][% END %]</a></li>
129
                            <li>Pickup location: [% Branches.GetName( branch ) | html %]</li>
130
                        </ul>
131
                        <table>
132
                        <thead><tr><th>Title</th><th>Item</th><th>Holds</th></tr></thead>
133
                        <tbody>
134
                        [% FOREACH hold IN holdloop %]
135
                            <tr>
136
                                <td>
137
                                    <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.title | html %]</a>
138
                                </td>
139
                                <td>
140
                                    <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% hold.biblionumber | uri %]&itemnumber=[% hold.itemnumber | uri %]">[% hold.barcode | html %]</a>
141
                                </td>
142
                                <td>
143
                                    <a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% hold.biblionumber | uri %]">Show holds</a>
144
                                </td>
145
                            </tr>
146
                        [% END %]
147
                        </tbody>
148
                        </table>
149
150
                    [% ELSE %]
151
                        <p>No holds could be placed or cancelled.</p>
152
                    [% END %]
153
154
                    <p><a href="batch_holding.pl">Batch place or cancel other holds</a></p>
155
156
                [% ELSE %]
157
158
                    <h1>Batch place or cancel holds</h1>
159
                    <form method="post" enctype="multipart/form-data" action="/cgi-bin/koha/circ/batch_holding.pl" id="batchholdform">
160
                        <fieldset class="rows">
161
                            <legend>Use a barcode file</legend>
162
                            <label for="uploadfile">File: </label>
163
                            <input type="file" id="uploadfile" name="uploadfile" />
164
                        </fieldset>
165
166
                        <fieldset class="rows">
167
                            <legend>Or scan items one by one</legend>
168
                            <ol>
169
                                <li>
170
                                    <label for="barcodelist">Barcode list (one barcode per line): </label>
171
                                    <textarea rows="10" cols="30" id="barcodelist" name="barcodelist"></textarea>
172
                                </li>
173
                            </ol>
174
                        </fieldset>
175
176
                        <fieldset class="rows">
177
                            <legend>Place hold for</legend>
178
                            <ol>
179
                                <li>
180
                                    <label class="required" for="findborrowerhold">Patron:</label>
181
                                    <input type="hidden" id="borrowernumber" name="borrowernumber" />
182
                                    <div class="autocomplete">
183
                                        <input autocomplete="on" id="findborrowerhold" name="findborrowerhold" size="40" class="required" type="text" required="required" />
184
                                        <span class="required">Required</span>
185
                                    </div>
186
                                    <div class="hint">
187
                                        Enter patron card number or partial name
188
                                    </div>
189
                                </li>
190
                                <li>
191
                                    <label for="branch">Pickup at:</label>
192
                                    <select name="branch" id="branch">
193
                                        [% PROCESS options_for_libraries libraries => Branches.all( unfiltered => 1 ) %]
194
                                    </select>
195
                                </li>
196
                            </ol>
197
                        </fieldset>
198
199
                        <fieldset class="rows">
200
                            <legend>Action</legend>
201
                            <ol>
202
                                <li>
203
                                    <input type="radio" name="action" id="action_place_holds" value="place" checked="checked">
204
                                    <label for="action_place_holds">Place holds</label>
205
                                </li>
206
                                <li>
207
                                    <input type="radio" name="action" id="action_cancel_holds" value="cancel">
208
                                    <label for="action_cancel_holds">Cancel holds</label>
209
                                </li>
210
                            </ol>
211
                        </fieldset>
212
213
                        <fieldset class="action">
214
                            <input type="hidden" name="op" value="show" />
215
                            <input type="submit" value="Continue" class="submit" />
216
                        </fieldset>
217
                    </form> <!-- /#batchholdform -->
218
                [% END # /IF show %]
219
220
            </main>
221
        </div> <!-- /.col-sm-10.col-sm-push-2 -->
222
        [% IF Koha.Preference('CircSidebar') %]
223
            <div class="col-sm-2 col-sm-pull-10">
224
                <aside>
225
                    [% INCLUDE 'circ-nav.inc' %]
226
                </aside>
227
            </div> <!-- /.col-sm-2.col-sm-pull-10 -->
228
        [% END %]
229
    </div> <!-- /.row -->
230
231
[% MACRO jsinclude BLOCK %]
232
    <script>
233
        $(document).ready(function() {
234
            if( $("#findborrowerhold").length > 0 ){
235
                $("#findborrowerhold").autocomplete({
236
                    source: "/cgi-bin/koha/circ/ysearch.pl",
237
                    minLength: 3,
238
                    select: function( event, ui ) {
239
                        $( "#findborrowerhold" ).val( ui.item.surname + ', ' + ui.item.firstname + ' (' + ui.item.cardnumber + ')' );
240
                        $( "#borrowernumber").val( ui.item.borrowernumber );
241
                        return false;
242
                    }
243
                })
244
                    .data( "ui-autocomplete" )._renderItem = function( ul, item ) {
245
                        return $( "<li></li>" )
246
                        .data( "ui-autocomplete-item", item )
247
                    .append( "<a>" + item.surname + ", " + item.firstname + " (" + item.cardnumber + ") <small>" + item.address + " " + item.city + " " + item.zipcode + " " + item.country + "</small></a>" )
248
                    .appendTo( ul );
249
                };
250
            }
251
252
            $("#batchholdform").validate({
253
                rules: {
254
                    uploadfile: {
255
                        required: function(element){
256
                            return $("#barcodelist").val() === "";
257
                        }
258
                    },
259
                    barcodelist: {
260
                        required: function(element){
261
                            return $("#uploadfile").val() === "";
262
                        }
263
                    },
264
                },
265
                messages: {
266
                    uploadfile: {
267
                        required: _("Submit barcodes in a file or by entering them below")
268
                    },
269
                    barcodelist: {
270
                        required: _("Submit barcodes by entering them here or by submitting a file above")
271
                    }
272
                },
273
                findborrowerhold: {
274
                    required: true
275
                }
276
277
            });
278
279
            $("#SelectAll").on("click",function(e){
280
                e.preventDefault();
281
                $("#hold_selections input[type='checkbox']").prop('checked', true);
282
            });
283
284
            $("#ClearAll").on("click",function(e){
285
                e.preventDefault();
286
                $("#hold_selections input[type='checkbox']").prop('checked', false);
287
            });
288
289
        });
290
    </script>
291
[% END %]
292
293
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt (-2 / +5 lines)
Lines 9-15 Link Here
9
[% INCLUDE 'header.inc' %]
9
[% INCLUDE 'header.inc' %]
10
[% INCLUDE 'circ-search.inc' %]
10
[% INCLUDE 'circ-search.inc' %]
11
11
12
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; Circulation</div>
12
<div id="breadcrumbs">
13
<a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; Circulation</div>
13
14
14
    <div class="main container-fluid">
15
    <div class="main container-fluid">
15
16
Lines 73-78 Link Here
73
                    <li>
74
                    <li>
74
                        <a class="circ-button" href="/cgi-bin/koha/circ/reserveratios.pl"><i class="fa fa-line-chart"></i> Hold ratios</a>
75
                        <a class="circ-button" href="/cgi-bin/koha/circ/reserveratios.pl"><i class="fa fa-line-chart"></i> Hold ratios</a>
75
                    </li>
76
                    </li>
77
                    <li>
78
                        <a class="circ-button" href="/cgi-bin/koha/circ/batch_holding.pl"><i class="fa fa-bookmark"></i> Batch place or cancel holds</a>
79
                    </li>
76
                </ul>
80
                </ul>
77
            </div>
81
            </div>
78
82
79
- 

Return to bug 23258