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

(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt (+313 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Asset %]
3
[% SET footerjs = 1 %]
4
[% USE Branches %]
5
[% USE Categories %]
6
[% USE KohaDates %]
7
[% USE ItemTypes %]
8
[% PROCESS 'html_helpers.inc' %]
9
10
[% INCLUDE 'doc-head-open.inc' %]
11
<title>Batch modify holds &rsaquo; Tools &rsaquo; Koha</title>
12
[% INCLUDE 'doc-head-close.inc' %]
13
[% Asset.css("css/humanmsg.css") | $raw %]
14
</head>
15
16
<body id="tools_batch_extend_due_dates" class="tools">
17
    [% WRAPPER 'header.inc' %]
18
    [% INCLUDE 'cat-search.inc' %]
19
[% END %]
20
21
    [% WRAPPER 'sub-header.inc' %]
22
    <nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb">
23
        <ol>
24
            <li>
25
                <a href="/cgi-bin/koha/mainpage.pl">Home</a>
26
            </li>
27
            <li>
28
                <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
29
            </li>
30
            <li>
31
                <a href="#" aria-current="page">Batch modify holds</a>
32
            </li>
33
        </ol>
34
    </nav>
35
    [% END %]
36
37
    <div class="main container-fluid">
38
        <div class="row">
39
            <div class="col-sm-10 col-sm-push-2">
40
                <main>
41
42
                    <h1>Batch modify holds</h1>
43
44
                    [% IF view == 'form' %]
45
                        <form method="post" action="/cgi-bin/koha/tools/batch_modify_holds.pl" id="modify_holds_form">
46
                            <fieldset class="rows">
47
                                <legend>Hold search criteria:</legend>
48
                                <span class="hint">NOTE! Column patron_expiration_date is used as a main filter for expiration date field. Otherwise column expirationdate is used.</span>
49
                                <ol>
50
                                    <li>
51
                                        <label for="suspend_until">Expiration date:</label>
52
                                        <li>
53
                                            <label for="expirationdate_from">From</label>
54
                                            <input type="text" size="10" id="from" name="expirationdate_from" class="flatpickr" data-date_to="to" />
55
                                        </li>
56
                                        <li>
57
                                            <label for="expirationdate_to">To</label>
58
                                            <input type="text" size="10" id="to" name="expirationdate_to" class="flatpickr"/>
59
                                        </li>
60
                                    </li>
61
                                    <li>
62
                                        <label for="branchcodes">Libraries:</label>
63
                                        <select name="branchcodes" id="branchcodes" multiple="multiple">
64
                                            [% PROCESS options_for_libraries libraries => Branches.all() %]
65
                                        </select>
66
                                    </li>
67
                                    <li>
68
                                        <label for="found_status">Found status:</label>
69
                                        <select name="found_status" id="found_status" multiple="multiple">
70
                                            <option value="NULL">No status</option>
71
                                            <option value="T">In transit</option>
72
                                            <option value="P">In processing</option>
73
                                            <option value="W">Waiting</option>
74
                                        </select>
75
                                    </li>
76
                                    <li>
77
                                        <label for="suspend_status">Suspended:</label>
78
                                        <select name="suspend_status" id="suspend_status">
79
                                            <option value="none" selected="selected"></option>
80
                                            <option value="0">Not suspended</option>
81
                                            <option value="1">Suspended</option>
82
                                        </select>
83
                                    </li>
84
                                    <li>
85
                                        <label for="suspend_until">Suspended until:</label>
86
                                        <li>
87
                                            <label for="suspend_until_from">From</label>
88
                                            <input type="text" size="10" id="from" name="suspend_until_from" class="flatpickr" data-date_to="to" />
89
                                        </li>
90
                                        <li>
91
                                            <label for="suspend_until_to">To</label>
92
                                            <input type="text" size="10" id="to" name="suspend_until_to" class="flatpickr"/>
93
                                        </li>
94
                                    </li>
95
                                    <li>
96
                                        <label for="reservenotes">Hold note:</label>
97
                                        <input type="text" id="reservenotes" name="reservenotes"/>
98
                                    </li>
99
                                </ol>
100
                            </fieldset>
101
                            <fieldset class="action">
102
                                <input type="hidden" name="op" value="list"/>
103
                                <input type="submit" class="btn btn-primary" value="Continue"/>
104
                                <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a>
105
                            </fieldset>
106
                        </form> <!-- /#modify_holds_form -->
107
                    [% ELSIF view == 'list' %]
108
                        <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process">
109
                        [% IF holds.count %]
110
                            <div class="page-section">
111
                                <div class="btn-toolbar selections-toolbar">
112
                                    <a id="selectall" href="#"><i class="fa fa-check"></i> Select all</a>
113
                                    <a id="clearall" href="#"><i class="fa fa-remove"></i> Clear all</a>
114
                                </div>
115
                                <table id="found_holds">
116
                                    <thead>
117
                                        <tr>
118
                                            <th>&nbsp;</th>
119
                                            <th>Expiration date</th>
120
                                            <th>Patron expiration date</th>
121
                                            <th>Status</th>
122
                                            <th>Hold pickup library</th>
123
                                            <th>Suspended</th>
124
                                            <th>Suspended until</th>
125
                                            <th>Note</th>
126
                                        </tr>
127
                                    </thead>
128
                                    <tbody>
129
                                        [% FOREACH hold IN holds %]
130
                                            <tr>
131
                                                <td><input type="checkbox" name="hold_id" value="[% hold.reserve_id | html %]"/></td>
132
                                                <td>[% hold.expirationdate | $KohaDates %]</td>
133
                                                <td>[% hold.patron_expiration_date | $KohaDates %]</td>
134
                                                <td class="found_status" data-found="[% hold.found | html %]">
135
                                                    [% IF hold.found == "T" %]
136
                                                        In transit
137
                                                    [% ELSIF hold.found == "P" %]
138
                                                        In processing
139
                                                    [% ELSIF hold.found == "W" %]
140
                                                        Waiting
141
                                                    [% ELSE %]
142
                                                        No status
143
                                                    [% END %]
144
                                                </td>
145
                                                <td>[% Branches.GetName( hold.branchcode ) | html %]</td>
146
                                                <td>[% IF hold.suspend %]Yes[% ELSE %]No[% END %]</td>
147
                                                <td>[% hold.suspend_until | $KohaDates %]</td>
148
                                                <td>[% hold.reservenotes | html %]</td>
149
                                            </tr>
150
                                        [% END %]
151
                                    </tbody>
152
                                </table> <!-- /#holds -->
153
                            </div> <!-- /.page-section -->
154
                            <h2>Modify holds</h2>
155
                            <table id="hold_modifies">
156
                                <thead>
157
                                    <tr>
158
                                        <th>New expiration date</th>
159
                                        <th>New pickup library</th>
160
                                        <th>Suspend holds</th>
161
                                        <th>Suspend until</th>
162
                                        <th>New reserve note</th>
163
                                    </tr>
164
                                </thead>
165
                                <tbody>
166
                                    <tr>
167
                                        <td>
168
                                            <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true"/>
169
                                        </td>
170
                                        <td>
171
                                            <select name="new_pickup_loc" id="new_pickup_loc">
172
                                                [% PROCESS options_for_libraries libraries => Branches.pickup_locations({ selected => hold.branchcode }) %]
173
                                            </select>
174
                                        </td>
175
                                        <td>
176
                                            <select id="new_suspend_status" name="new_suspend_status">
177
                                                <option value="" selected="selected"></option>
178
                                                <option value="not_suspended">Not suspended</option>
179
                                                <option value="suspend">Suspend</option>
180
                                            </select>
181
                                        </td>
182
                                        <td>
183
                                            <input type="text" id="new_suspend_date" name="new_suspend_date" class="flatpickr" data-flatpickr-futuredate="true"/>
184
                                        </td>
185
                                        <td>
186
                                           <input type="text" id="new_reserve_note" name="new_reserve_note"/>
187
                                        </td>
188
                                    </tr>
189
                                </tbody>
190
                            </table> <!-- /#hold_modifies -->
191
                            <fieldset class="action">
192
                                <input type="hidden" name="op" value="modify"/>
193
                                <input type="submit" class="btn btn-primary" value="Modify selected holds"/>
194
                                <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a>
195
                            </fieldset>
196
                        </form> <!-- /#process -->
197
                        [% ELSE %]
198
                            <div class="dialog message">
199
                                No holds were found for the selected filters.
200
                            </div>
201
                            <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return</a>
202
                        [% END %]
203
                    [% ELSIF view == 'report' %]
204
                        <div class="dialog message">
205
                            Holds have been modified!
206
                        </div>
207
                        <div class="page-section">
208
                            <div class="btn-toolbar selections-toolbar">
209
                                <a id="selectall" href="#"><i class="fa fa-check"></i> Select all</a>
210
                                <a id="clearall" href="#"><i class="fa fa-remove"></i> Clear all</a>
211
                            </div>
212
                            <table id="holds">
213
                                <thead>
214
                                    <tr>
215
                                        <th>Expiration date</th>
216
                                        <th>Hold pickup library</th>
217
                                        <th>Suspended</th>
218
                                        <th>Suspended until</th>
219
                                        <th>Note</th>
220
                                    </tr>
221
                                </thead>
222
                                <tbody>
223
                                    [% FOREACH hold IN updated_holds %]
224
                                        <tr>
225
                                            <td>[% hold.expirationdate | $KohaDates %]</td>
226
                                            <td>[% Branches.GetName( hold.branchcode ) | html %]</td>
227
                                            <td>[% IF hold.suspend == 0 %]No[% ELSE %]Yes[% END %]</td>
228
                                            <td>[% hold.suspend_until | $KohaDates %]</td>
229
                                            <td>[% hold.reservenotes | html %]</td>
230
                                        </tr>
231
                                    [% END %]
232
                                </tbody>
233
                            </table> <!-- /#holds -->
234
                        </div> <!-- /.page-section -->
235
                        <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a>
236
                    [% END %]
237
                </main>
238
            </div> <!-- /.col-sm-10 col-sm-push-2 -->
239
240
241
            <div class="col-sm-2 col-sm-pull-10">
242
                <aside>
243
                    [% INCLUDE 'tools-menu.inc' %]
244
                </aside>
245
            </div> <!-- /.col-sm-2 col-sm-pull-10 -->
246
        </div> <!-- /.row -->
247
248
[% MACRO jsinclude BLOCK %]
249
    [% Asset.js("js/tools-menu.js") | $raw %]
250
    [% INCLUDE 'calendar.inc' %]
251
    [% INCLUDE 'datatables.inc' %]
252
    [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %]
253
    <script>
254
        $(document).ready(function() {
255
256
            $("#selectall").click(function(e){
257
                e.preventDefault();
258
                $("#found_holds input[type='checkbox']").each(function(){
259
                    $(this).prop("checked", true);
260
                });
261
            });
262
263
            $("#clearall").click(function(e){
264
                e.preventDefault();
265
                $("#found_holds input[type='checkbox']").each(function(){
266
                    $(this).prop("checked", false);
267
                });
268
            });
269
270
            $("#selectall").click();
271
272
            $("#found_holds").dataTable($.extend(true, {}, dataTablesDefaults, {
273
                "aoColumnDefs": [
274
                    { "aTargets": [0], "bSortable": false, "bSearchable": false },
275
                ],
276
                "sDom": 't',
277
                "aaSorting": [],
278
                "bPaginate": false
279
            }));
280
281
            $("#process").on('submit', function(e) {
282
                var reserve_ids = $("input[type=checkbox][name='hold_id']:checked");
283
284
                var new_suspend_status = $("#new_suspend_status").val();
285
                var new_suspend_date = $("#new_suspend_date").val();
286
287
                if ( reserve_ids.length == 0 ) {
288
                    e.preventDefault();
289
                    alert(_("Please select at least one hold to process."));
290
                    return false;
291
                }
292
                if( new_suspend_status ){
293
                    reserve_ids.each(function(){
294
                        if($(this).parents("tr").children(".found_status").data("found") != ""){
295
                            e.preventDefault();
296
                            alert(_("One or more holds have found status and can't be suspended."));
297
                            return false;
298
                        }
299
                    })
300
                }
301
                if( ( !new_suspend_status || new_suspend_status == "not_suspended") && new_suspend_date ){
302
                    e.preventDefault();
303
                    alert(_('You have to suspend holds if new suspend until date is set.'));
304
                    return false;
305
                }
306
307
                return true;
308
            });
309
        });
310
    </script>
311
[% END %]
312
313
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/tools/batch_modify_holds.pl (-1 / +175 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright 2024 Koha Development Team
6
#
7
# Koha is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as
9
# published by the Free Software Foundation; either version 3
10
# of the License, or (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
18
# Public License along with Koha; if not, see
19
# <http://www.gnu.org/licenses>
20
21
use Modern::Perl;
22
23
use CGI;
24
25
use C4::Auth qw( get_template_and_user );
26
use C4::Output qw( output_html_with_http_headers );
27
28
use Koha::DateUtils qw( dt_from_string );
29
use Koha::Holds;
30
31
my $input = CGI->new;
32
my $op = $input->param('op') // q|form|;
33
34
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
35
    {
36
        template_name   => 'tools/batch_modify_holds.tt',
37
        query           => $input,
38
        type            => "intranet",
39
        flagsrequired   => { tools => 'batch_modify_holds.tt' },
40
    }
41
);
42
43
my @hold_ids;
44
45
if ( $op eq 'form' ) {
46
    $template->param( view => 'form', );
47
}
48
elsif ( $op eq 'list' ) {
49
50
    my @reserve_ids         = $input->multi_param('reserve_ids');
51
    my $expirationdate_from = $input->param('expirationdate_from');
52
    my $expirationdate_to   = $input->param('expirationdate_to');
53
    my @branchcodes         = $input->multi_param('branchcodes');
54
    my @found_status        = $input->multi_param('found_status');
55
    my $suspend_status      = $input->param('suspend_status');
56
    my $suspend_until_from  = $input->param('suspend_until_from');
57
    my $suspend_until_to    = $input->param('suspend_until_to');
58
    my $reservenotes        = $input->param('reservenotes');
59
60
    my $search_params;
61
62
    if( @reserve_ids ){
63
        $search_params->{'me.reserve_id'} = { -in => \@reserve_ids };
64
    } else {
65
        if( $expirationdate_from && $expirationdate_to ){
66
            my $expirationdate_params = { -or => {
67
                patron_expiration_date => {
68
                    -between => [
69
                        $expirationdate_from,
70
                        $expirationdate_to
71
                    ]
72
                },
73
                expirationdate => {
74
                    -between => [
75
                        $expirationdate_from,
76
                        $expirationdate_to
77
                    ]
78
                }
79
            }};
80
81
            $search_params = $expirationdate_params;
82
        }
83
84
        if ( @branchcodes ) {
85
            $search_params->{'me.branchcode'} = { -in => \@branchcodes };
86
        }
87
88
        if ( @found_status ) {
89
            my $found_params = {};
90
            # if NULL is used as filter, use it as "is" param
91
            if( grep {$_ eq "NULL"} @found_status ) {
92
                $found_params->{'-or'}{'-is'} = undef;
93
                $found_params->{'-or'}{'-in'} = \@found_status;
94
            } else {
95
                $found_params = { -in => \@found_status };
96
            }
97
98
            $search_params->{'me.found'} = $found_params;
99
        }
100
101
        if( $suspend_status ne "none" ){
102
            $search_params->{'me.suspend'} = $suspend_status;
103
        }
104
105
        if( $suspend_until_from && $suspend_until_to ){
106
            $search_params->{'me.suspend_until'} = {
107
                -between => [
108
                    $suspend_until_from,
109
                    $suspend_until_to,
110
                ]
111
            };
112
        }
113
114
        if( $reservenotes ){
115
            $search_params->{'me.reservenotes'} = { -like => "%".$reservenotes."%" }
116
        }
117
    }
118
119
    my $holds = Koha::Holds->search( $search_params );
120
121
    $template->param(
122
        holds => $holds,
123
        view  => 'list',
124
    );
125
}
126
127
if ( $op eq 'modify' ) {
128
129
    my $new_expiration_date = $input->param('new_expiration_date');
130
    my $new_pickup_loc      = $input->param('new_pickup_loc');
131
    my $new_suspend_status  = $input->param('new_suspend_status');
132
    my $new_suspend_date    = $input->param('new_suspend_date');
133
    my $new_reserve_note    = $input->param('new_reserve_note');
134
135
    @hold_ids = $input->multi_param('hold_id');
136
137
    my $holds_to_update = Koha::Holds->search( { reserve_id => { -in => \@hold_ids } } );
138
139
    while ( my $hold = $holds_to_update->next ) {
140
141
        if( $new_expiration_date ){
142
            $hold->expirationdate($new_expiration_date)->store;
143
        }
144
145
        unless( $hold->branchcode eq $new_pickup_loc){
146
            $hold->branchcode($new_pickup_loc)->store;
147
        }
148
149
        if( $new_suspend_status && !$hold->is_found ){
150
            if( $new_suspend_status eq "suspend" ){
151
                $hold->suspend(1)->store;
152
                if( $new_suspend_date ){
153
                    $hold->suspend_until( $new_suspend_date )->store;
154
                } else {
155
                    $hold->suspend_until( undef )->store;
156
                }
157
            } else {
158
                $hold->suspend(0)->store;
159
                $hold->suspend_until( undef )->store;
160
            }
161
        }
162
163
        if( $new_reserve_note ){
164
            $hold->reservenotes($new_reserve_note)->store;
165
        }
166
    }
167
168
    $template->param(
169
        updated_holds => $holds_to_update,
170
        view          => 'report',
171
    );
172
173
}
174
175
output_html_with_http_headers $input, $cookie, $template->output;

Return to bug 36135