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

(-)a/admin/columns_settings.yml (-1 / +46 lines)
Lines 1955-1961 modules: Link Here
1955
              columnname: suspend
1955
              columnname: suspend
1956
            -
1956
            -
1957
              columnname: print_slip
1957
              columnname: print_slip
1958
1958
      holds_to_modify:
1959
        default_display_length: 20
1960
        columns:
1961
            -
1962
              columnname: checkbox
1963
              cannot_be_toggled: 1
1964
              cannot_be_modified: 1
1965
            -
1966
              columnname: expiration_date
1967
            -
1968
              columnname: title
1969
            -
1970
              columnname: barcode
1971
            -
1972
              columnname: patron
1973
            -
1974
              columnname: status
1975
            -
1976
              columnname: pickup_library
1977
            -
1978
              columnname: suspended
1979
            -
1980
              columnname: suspended_until
1981
            -
1982
              columnname: notes
1983
      modified_holds:
1984
        default_display_length: 20
1985
        columns:
1986
            -
1987
              columnname: expiration_date
1988
            -
1989
              columnname: title
1990
            -
1991
              columnname: barcode
1992
            -
1993
              columnname: patron
1994
            -
1995
              columnname: status
1996
            -
1997
              columnname: pickup_library
1998
            -
1999
              columnname: suspended
2000
            -
2001
              columnname: suspended_until
2002
            -
2003
              columnname: notes
1959
    holdsratios:
2004
    holdsratios:
1960
      holds-ratios:
2005
      holds-ratios:
1961
        default_display_length: 20
2006
        default_display_length: 20
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt (-275 / +513 lines)
Lines 5-10 Link Here
5
[% USE Categories %]
5
[% USE Categories %]
6
[% USE KohaDates %]
6
[% USE KohaDates %]
7
[% USE ItemTypes %]
7
[% USE ItemTypes %]
8
[% USE TablesSettings %]
9
[% USE To %]
8
[% PROCESS 'html_helpers.inc' %]
10
[% PROCESS 'html_helpers.inc' %]
9
11
10
[% INCLUDE 'doc-head-open.inc' %]
12
[% INCLUDE 'doc-head-open.inc' %]
Lines 37-230 Link Here
37
39
38
<div class="main container-fluid">
40
<div class="main container-fluid">
39
    <div class="row">
41
    <div class="row">
40
        <div class="col-sm-10 order-md-2 order-sm-1">
42
        <div class="col-sm-10 col-sm-push-2 order-sm-1">
41
            <main>
43
            <main>
42
                <h1>Batch modify holds</h1>
44
                <h1>Batch modify holds</h1>
43
45
                [% IF view == "form" %]
44
                [% IF view == 'form' %]
46
                    <div id="modify_holds_search">
45
                    <form method="get" action="/cgi-bin/koha/tools/batch_modify_holds.pl" id="modify_holds_form">
47
                        <form method="get" action="/cgi-bin/koha/tools/batch_modify_holds.pl" id="modify_holds_form">
46
                        <fieldset class="rows">
48
                            <fieldset class="rows">
47
                            <legend>Hold search criteria:</legend>
49
                                <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>
50
                                <ol>
49
                            <ol>
51
                                    <li>
50
                                <li>
52
                                        <label for="expirationdate_from">Expiration date from:</label>
51
                                    <label for="expirationdate_from">Expiration date from:</label>
53
                                        <input type="text" size="10" id="expirationdate_from" name="expirationdate_from" class="flatpickr" data-date_to="expirationdate_to" />
52
                                    <input type="text" size="10" id="expirationdate_from" name="expirationdate_from" class="flatpickr" data-date_to="to" />
54
                                    </li>
53
                                </li>
55
                                    <li>
54
                                <li>
56
                                        <label for="expirationdate_to">Expiration date to:</label>
55
                                    <label for="expirationdate_to">Expiration date to:</label>
57
                                        <input type="text" size="10" id="expirationdate_to" name="expirationdate_to" class="flatpickr"/>
56
                                    <input type="text" size="10" id="expirationdate_to" name="expirationdate_to" class="flatpickr" />
58
                                    </li>
57
                                </li>
59
                                    <li>
58
                                <li>
60
                                        <label for="branchcodes">Libraries:</label>
59
                                    <label for="branchcodes">Libraries:</label>
61
                                        <select name="branchcodes" id="branchcodes" multiple="multiple">
60
                                    <select name="branchcodes" id="branchcodes" multiple="multiple">
62
                                            [% PROCESS options_for_libraries libraries => Branches.all(selected => "") %]
61
                                        [% PROCESS options_for_libraries libraries => Branches.all(selected => "") %]
63
                                        </select>
62
                                    </select>
64
                                    </li>
63
                                </li>
65
                                    <li>
64
                                <li>
66
                                        <label for="found_status">Found status:</label>
65
                                    <label for="found_status">Found status:</label>
67
                                        <select name="found_status" id="found_status" multiple="multiple">
66
                                    <select name="found_status" id="found_status" multiple="multiple">
68
                                            <option value="NULL">No status</option>
67
                                        <option value="NULL">No status</option>
69
                                            <option value="T">In transit</option>
68
                                        <option value="T">In transit</option>
70
                                            <option value="P">In processing</option>
69
                                        <option value="P">In processing</option>
71
                                            <option value="W">Waiting</option>
70
                                        <option value="W">Waiting</option>
72
                                        </select>
71
                                    </select>
73
                                    </li>
72
                                </li>
74
                                    <li>
73
                                <li>
75
                                        <label for="suspend_status">Suspended:</label>
74
                                    <label for="suspend_status">Suspended:</label>
76
                                        <select name="suspend_status" id="suspend_status">
75
                                    <select name="suspend_status" id="suspend_status">
77
                                            <option value="none" selected="selected"></option>
76
                                        <option value="none" selected="selected"></option>
78
                                            <option value="0">Not suspended</option>
77
                                        <option value="0">Not suspended</option>
79
                                            <option value="1">Suspended</option>
78
                                        <option value="1">Suspended</option>
80
                                        </select>
79
                                    </select>
81
                                    </li>
80
                                </li>
82
                                    <li>
81
                                <li>
83
                                        <label for="suspend_until_from">Suspended until from:</label>
82
                                    <label for="suspend_until_from">Suspended until from:</label>
84
                                        <input type="text" size="10" id="suspend_until_from" name="suspend_until_from" class="flatpickr" data-date_to="suspend_until_to" />
83
                                    <input type="text" size="10" id="suspend_until_from" name="suspend_until_from" class="flatpickr" data-date_to="to" />
85
                                    </li>
84
                                </li>
86
                                    <li>
85
                                <li>
87
                                        <label for="suspend_until_to">Suspended until to:</label>
86
                                    <label for="suspend_until_to">Suspended until to:</label>
88
                                        <input type="text" size="10" id="suspend_until_to" name="suspend_until_to" class="flatpickr"/>
87
                                    <input type="text" size="10" id="suspend_until_to" name="suspend_until_to" class="flatpickr" />
89
                                    </li>
88
                                </li>
90
                                    <li>
89
                                <li>
91
                                        <label for="holdnotes">Hold note:</label>
90
                                    <label for="reservenotes">Hold note:</label>
92
                                        <input type="text" id="holdnotes" name="holdnotes"/>
91
                                    <input type="text" id="reservenotes" name="reservenotes" />
93
                                    </li>
92
                                </li>
94
                                </ol>
93
                            </ol>
95
                            </fieldset>
94
                        </fieldset>
96
                            <fieldset class="action">
97
                                <input type="submit" class="btn btn-primary" value="Search"/>
98
                            </fieldset>
99
                        </form> <!-- /#modify_holds_form -->
100
                    </div> <!-- /#modify_holds_search -->
101
                [% ELSIF view == "report" %]
102
                    <div id="modified_holds_results-wrapper">
103
                        <div id="modified_holds_results" class="page-section">
104
                            <table id="modified_holds"></table>
105
                        </div> <!-- /#modified_holds_results -->
95
                        <fieldset class="action">
106
                        <fieldset class="action">
96
                            <input type="hidden" name="op" value="list" />
107
                            <a href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a>
97
                            <input type="submit" class="btn btn-primary" value="Continue" />
108
                        </fieldset >
98
                            <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a>
109
                    </div> <!-- /#modified_holds_results-wrapper -->
99
                        </fieldset>
110
                [% END %]
100
                    </form>
111
                <div id="modify_holds_results-wrapper" style="display:none;">
101
                    <!-- /#modify_holds_form -->
112
                    <div id="toolbar" class="btn-toolbar">
102
                [% ELSIF view == 'list' %]
113
                        <a href="#" id="edit_search" class="btn btn-default"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit search</a>
103
                    [% IF holds.count %]
114
                    </div> <!-- /#toolbar -->
104
                        <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process">
115
                    <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process_mods">
105
                            [% INCLUDE 'csrf-token.inc' %]
116
                        [% INCLUDE 'csrf-token.inc' %]
106
                            <div><p>Found [% holds.count | html %][% IF holds.count > 1 %]holds[% ELSE %]hold[% END %]to modify.</p></div>
117
                        <div id="modify_holds_results" class="page-section">
107
                            <div class="page-section">
118
                            <div id="searchheader" class="searchheader">
108
                                <div class="btn-toolbar selections-toolbar">
119
                                <a id="select_all" href="#" class="btn btn-link"><i class="fa fa-check"></i> Select all visible rows</a>
109
                                    <a id="selectall" href="#"><i class="fa fa-check"></i> Select all</a>
120
                                | <a id="clear_all" href="#" class="btn btn-link"><i class="fa fa-times"></i> Clear selections</a>
110
                                    <a id="clearall" href="#"><i class="fa fa-remove"></i> Clear all</a>
111
                                </div>
112
                                <table id="found_holds">
113
                                    <thead>
114
                                        <tr>
115
                                            <th>Hold ID</th>
116
                                            <th>Expiration date</th>
117
                                            <th>Title</th>
118
                                            <th>Barcode</th>
119
                                            <th>Status</th>
120
                                            <th>Hold pickup library</th>
121
                                            <th>Suspended</th>
122
                                            <th>Suspended until</th>
123
                                            <th>Note</th>
124
                                        </tr>
125
                                    </thead>
126
                                </table>
127
                                <!-- /#holds -->
128
                            </div>
121
                            </div>
129
                            <!-- /.page-section -->
122
                            <h3>Holds found for: <span class="searchpattern"></span></h3>
123
                            <table id="holds_to_modify"></table>
124
                        </div> <!-- /#modify_holds_results -->
125
                        <div id="modify_holds_form_options" class="page-section">
130
                            <h2>Modify holds</h2>
126
                            <h2>Modify holds</h2>
131
                            <div class="page-section">
127
                            <table id="modify_holds_options">
132
                                <table id="hold_modifies">
128
                                <thead>
133
                                    <thead>
134
                                        <tr>
135
                                            <th>New expiration date</th>
136
                                            <th>New pickup library</th>
137
                                            <th>Suspend holds</th>
138
                                            <th>Suspend until</th>
139
                                            <th>New reserve note</th>
140
                                        </tr>
141
                                    </thead>
142
                                    <tbody>
143
                                        <tr>
144
                                            <td>
145
                                                <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true" />
146
                                            </td>
147
                                            <td>
148
                                                <select id="new_pickup_loc" name="new_pickup_loc">
149
                                                    <option value="" selected="selected"></option>
150
                                                    [% PROCESS options_for_libraries libraries => Branches.pickup_locations(selected = "") %]
151
                                                </select>
152
                                            </td>
153
                                            <td>
154
                                                <select id="new_suspend_status" name="new_suspend_status">
155
                                                    <option value="" selected="selected"></option>
156
                                                    <option value="not_suspended">Not suspended</option>
157
                                                    <option value="suspend">Suspend</option>
158
                                                </select>
159
                                            </td>
160
                                            <td>
161
                                                <input type="text" id="new_suspend_date" name="new_suspend_date" class="flatpickr" data-flatpickr-futuredate="true" />
162
                                            </td>
163
                                            <td>
164
                                                <input type="text" id="new_reserve_note" name="new_reserve_note" />
165
                                            </td>
166
                                        </tr>
167
                                    </tbody>
168
                                </table>
169
                                <!-- /#hold_modifies -->
170
                            </div>
171
                            <fieldset class="action">
172
                                <input type="hidden" name="op" value="cud-modify" />
173
                                <input type="submit" class="btn btn-primary" value="Modify holds" />
174
                                <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a>
175
                            </fieldset>
176
                        </form>
177
                        <!-- /#process -->
178
                    [% ELSE %]
179
                        <div class="dialog message"> No holds were found for the selected filters. </div>
180
                        <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return</a>
181
                    [% END %]
182
                [% ELSIF view == 'report' %]
183
                    <div class="dialog message"> [% updated_holds.count | html %] holds have been modified! </div>
184
                    <div class="page-section">
185
                        <table id="holds">
186
                            <thead>
187
                                <tr>
188
                                    <th>Expiration date</th>
189
                                    <th>Title</th>
190
                                    <th>Barcode</th>
191
                                    <th>Hold pickup library</th>
192
                                    <th>Suspended</th>
193
                                    <th>Suspended until</th>
194
                                    <th>Note</th>
195
                                </tr>
196
                            </thead>
197
                            <tbody>
198
                                [% FOREACH hold IN updated_holds %]
199
                                    <tr>
129
                                    <tr>
200
                                        <td>[% hold.expirationdate | $KohaDates %]</td>
130
                                        <th>New expiration date</th>
201
                                        <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.biblio.title | html %]</a></td>
131
                                        <th>New pickup library</th>
202
                                        <td
132
                                        <th>Suspend holds</th>
203
                                            ><a href="/cgi-bin/koha/catalogue/moredetail.pl?itemnumber=[% hold.itemnumber | uri %]&biblionumber=[% hold.biblionumber | uri %]&bi=[% hold.biblionumber | uri %]#item[% hold.itemnumber | uri %]"
133
                                        <th>Suspend until</th>
204
                                                >[% hold.item.barcode | html %]</a
134
                                        <th>New hold note</th>
205
                                            ></td
206
                                        >
207
                                        <td>[% Branches.GetName( hold.branchcode ) | html %]</td>
208
                                        <td>[% IF hold.suspend == 0 %]No[% ELSE %]Yes[% END %]</td>
209
                                        <td>[% hold.suspend_until | $KohaDates %]</td>
210
                                        <td>[% hold.reservenotes | html %]</td>
211
                                    </tr>
135
                                    </tr>
212
                                [% END %]
136
                                </thead>
213
                            </tbody>
137
                                <tbody>
214
                        </table>
138
                                    <tr>
215
                        <!-- /#holds -->
139
                                        <td>
216
                    </div>
140
                                            <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true" />
217
                    <!-- /.page-section -->
141
                                        </td>
218
                    <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a>
142
                                        <td>
219
                [% END %]
143
                                            <select name="new_pickup_loc" id="new_pickup_loc">
144
                                                <option value="" selected="selected"></option>
145
                                                [% PROCESS options_for_libraries libraries => Branches.pickup_locations({ selected => "" }) %]
146
                                            </select>
147
                                        </td>
148
                                        <td>
149
                                            <select id="new_suspend_status" name="new_suspend_status">
150
                                                <option value="" selected="selected"></option>
151
                                                <option value="0">Not suspended</option>
152
                                                <option value="1">Suspend</option>
153
                                            </select>
154
                                        </td>
155
                                        <td>
156
                                            <input type="text" id="new_suspend_date" name="new_suspend_date" class="flatpickr" data-flatpickr-futuredate="true" />
157
                                        </td>
158
                                        <td>
159
                                           <input type="text" id="new_hold_note" name="new_hold_note"/>
160
                                        </td>
161
                                    </tr>
162
                                </tbody>
163
                            </table> <!-- /#modify_holds_options -->
164
                        </div> <!-- /#modify_holds_options -->
165
                        <fieldset class="action">
166
                            <input type="hidden" name="op" value="cud-modify" />
167
                            <input type="submit" class="btn btn-primary" value="Modify holds" />
168
                            <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a>
169
                        </fieldset>
170
                    </form> <!-- /#process_mods -->
171
                </div> <!-- /#modify_holds_results-wrapper -->
220
            </main>
172
            </main>
221
        </div>
173
        </div> <!-- /.col-sm-10 col-sm-push-2 -->
222
        <!-- /.col-sm-10 col-sm-push-2 -->
223
224
        <div class="col-sm-2 col-sm-pull-10">
174
        <div class="col-sm-2 col-sm-pull-10">
225
            <aside> [% INCLUDE 'tools-menu.inc' %] </aside>
175
            <aside>
226
        </div>
176
                [% INCLUDE 'tools-menu.inc' %]
227
        <!-- /.col-sm-2 col-sm-pull-10 -->
177
            </aside>
178
        </div> <!-- /.col-sm-2 col-sm-pull-10 -->
228
    </div>
179
    </div>
229
    <!-- /.row -->
180
    <!-- /.row -->
230
</div>
181
</div>
Lines 232-363 Link Here
232
    [% Asset.js("js/tools-menu.js") | $raw %]
183
    [% Asset.js("js/tools-menu.js") | $raw %]
233
    [% INCLUDE 'calendar.inc' %]
184
    [% INCLUDE 'calendar.inc' %]
234
    [% INCLUDE 'datatables.inc' %]
185
    [% INCLUDE 'datatables.inc' %]
186
    [% INCLUDE 'js-patron-format.inc' %]
235
    [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %]
187
    [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %]
236
    <script>
188
    <script>
237
        $(document).ready(function () {
189
        let hide_patron_name = [% IF Koha.Preference('HidePatronName') %]true[% ELSE %]false[% END %];
238
            var holds_table_url = "/api/v1/holds";
190
        let hold_ids = [% IF updated_holds %] [% updated_holds | $raw %] [% ELSE %] [] [% END %];
239
            var table = $("#found_holds").kohaTable({
191
240
                ajax: {
192
        $(document).ready(function() {
241
                    url: holds_table_url,
193
242
                },
194
            $("#select_all").click(function(e){
243
                autoWidth: false,
195
                e.preventDefault();
244
                embed: ["biblio", "pickup_library", "item"],
196
                $("#holds_to_modify input[type='checkbox']").each(function(){
245
                columns: [
197
                    $(this).prop("checked", true);
246
                    {
198
                });
247
                        data: "hold_id",
199
            });
248
                        searchable: true,
200
249
                        orderable: true,
201
            $("#clear_all").click(function(e){
250
                    },
202
                e.preventDefault();
251
                    {
203
                $("#holds_to_modify input[type='checkbox']").each(function(){
252
                        data: "expiration_date",
204
                    $(this).prop("checked", false);
253
                        title: "Expiration date",
205
                });
254
                        searchable: true,
206
            });
255
                        orderable: true,
207
256
                        render: function (data, type, row, meta) {
208
            $('#modify_holds_form').submit(function() {
257
                            return $date(row.expiration_date);
209
                var modify_holds_form = $(this);
258
                        },
210
                search_holds(modify_holds_form);
211
                $("#modify_holds_search").hide();
212
                $('#edit_search').show();
213
                return false;
214
            });
215
216
            $("#edit_search").on("click", function(e){
217
                e.preventDefault();
218
                $('#modify_holds_search').show();
219
                $('#edit_search').hide();
220
            });
221
222
            function search_holds(modify_holds_form){
223
                var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'holds', 'holds_to_modify', 'json' ) | $raw %];
224
                var searchpattern = "";
225
                var filters = {
226
                    "me.expirationdate": function(){
227
                        var expirationdate_from = modify_holds_form.find("#expirationdate_from").val();
228
                        var expirationdate_to   = modify_holds_form.find("#expirationdate_to").val();
229
230
                        searchpattern += expirationdate_from && expirationdate_to ? _("expiration date between ")+ expirationdate_from + " and " + expirationdate_to + " ": "";
231
232
                        return expirationdate_from && expirationdate_to ? {"-between": [expirationdate_from, expirationdate_to]} : "";
259
                    },
233
                    },
260
                    {
234
                    "me.branchcode": function(){
261
                        data: "",
235
                        var branchcodes = modify_holds_form.find("#branchcodes").val();
262
                        title: "Title",
236
                        if( branchcodes.length > 0 ){
263
                        searchable: true,
237
                            searchpattern += _("from libraries ");
264
                        orderable: true,
238
                            var selected_libraries = modify_holds_form.find("#branchcodes option:selected");
265
                        render: function (data, type, row, meta) {
239
                            $.each(selected_libraries, function(key, value) {
266
                            return row.biblio.title;
240
                                searchpattern += value.text + " ";
267
                        },
241
                            });
242
                            return {"-in": branchcodes };
243
                        }
268
                    },
244
                    },
269
                    {
245
                    "me.status": function(){
270
                        data: "",
246
                        var found_status = modify_holds_form.find("#found_status").val();
271
                        title: "Barcode",
247
                        if( found_status.length > 0 ){
272
                        searchable: true,
248
                            var filters = [{"-in": found_status}];
273
                        orderable: true,
249
                            if(jQuery.inArray("NULL", found_status) !== -1){
274
                        render: function (data, type, row, meta) {
250
                                filters.push({"=": null});
275
                            if (row.item_level) {
276
                                return row.item.external_id;
277
                            } else {
278
                                return "Bibliographic level hold";
279
                            }
251
                            }
280
                        },
252
                            searchpattern += _("found status in ");
253
                            $.each(found_status, function(key, value) {
254
                                let status = _("No status ");
255
                                if ( value == "T" ) {
256
                                    status = _("In transit ");
257
                                } else if( value == "P" ) {
258
                                    status = _("In processing ");
259
                                } else if( value == "W" ) {
260
                                    status = _("Waiting ");
261
                                }
262
                                searchpattern += status;
263
                            });
264
                            return filters;
265
                        }
281
                    },
266
                    },
282
                    {
267
                    "me.suspended": function(){
283
                        data: "status",
268
                        var suspend_status = modify_holds_form.find("#suspend_status").val();
284
                        title: "Status",
269
285
                        searchable: true,
270
                        if(suspend_status != "none"){
286
                        orderable: true,
271
                            var suspended_text = suspend_status == 0 ? _("Not suspended") : _("Suspended");
272
                            searchpattern += _("with suspend status ") + suspended_text;
273
274
                            return {"=": suspend_status};
275
                        }
287
                    },
276
                    },
288
                    {
277
                    "me.suspend_until": function(){
289
                        data: "pickup_library_id",
278
                        var suspend_until_from  = modify_holds_form.find("#suspend_until_from").val();
290
                        title: "Pickup library",
279
                        var suspend_until_to    = modify_holds_form.find("#suspend_until_to").val();
291
                        searchable: true,
280
292
                        orderable: true,
281
                        searchpattern += suspend_until_from && suspend_until_to ? _("suspend date between ")+ suspend_until_from +"-"+suspend_until_to : "";
293
                        render: function (data, type, row, meta) {
282
294
                            return row.pickup_library.name;
283
                        return suspend_until_from && suspend_until_to ? {"-between": [suspend_until_from, suspend_until_to]} : "";
295
                        },
296
                    },
284
                    },
297
                    {
285
                    "me.notes": function(){
298
                        data: "suspended",
286
                        var holdnotes = modify_holds_form.find("#holdnotes").val();
299
                        title: "Suspended",
287
300
                        searchable: true,
288
                        searchpattern += holdnotes ? _("hold notes like ") + holdnotes : "";
301
                        orderable: true,
289
302
                        render: function (data) {
290
                        return holdnotes ? {"-like": "%"+holdnotes+"%"} : "";
303
                            return data ? "Yes" : "No";
291
                    }
304
                        },
292
                }
293
294
                var holds_modify_table = $("#holds_to_modify").kohaTable({
295
                    "ajax": {
296
                        "url": "/api/v1/holds"
305
                    },
297
                    },
306
                    {
298
                    "embed": [
307
                        data: "suspended_until",
299
                        "biblio",
308
                        title: "Suspended until",
300
                        "item",
309
                        searchable: true,
301
                        "pickup_library",
310
                        orderable: true,
302
                        "patron"
311
                        render: function (data, type, row, meta) {
303
                    ],
312
                            return $date(row.suspended_until);
304
                    "destroy": true,
305
                    "autoWidth": false,
306
                    "processing": true,
307
                    "columns": [
308
                        {
309
                            "data": "hold_id",
310
                            "name": "checkbox",
311
                            "orderable": false,
312
                            "render": function(data, type, row, meta) {
313
                                return '<input type="checkbox" name="hold_id" value="'+ encodeURIComponent(data) +'"/>'
314
                            }
313
                        },
315
                        },
314
                    },
316
                        {
315
                    {
317
                            "data": "expiration_date",
316
                        data: "notes",
318
                            "name": "expiration_date",
317
                        title: "Notes",
319
                            "type": "date",
318
                        searchable: true,
320
                            "title": _("Expiration date"),
319
                        orderable: true,
321
                            "orderable": true,
320
                    },
322
                            "render": function( data, type, row, meta ) {
321
                ],
323
                                return $date(data);
322
            });
324
                            }
325
                        },
326
                        {
327
                            "data": "biblio.title",
328
                            "name": "title",
329
                            "title": _("Title"),
330
                            "orderable": true,
331
                            "render": function( data, type, row, meta ) {
332
                                return '<a href="/cgi-bin/koha/catalogue/detail.pl?' +
333
                                'biblionumber=' + encodeURIComponent( row.biblio_id ) + '">' + escape_str( data ) + '</a>';
334
                            }
335
                        },
336
                        {
337
                            "data": "item.external_id",
338
                            "name": "barcode",
339
                            "title": _("Barcode"),
340
                            "defaultContent": _(""),
341
                            "orderable": true,
342
                            "render": function( data, type, row, meta ) {
343
                                if( row.item ){
344
                                    let item_id = encodeURIComponent( row.item_id );
345
                                    let biblio_id = encodeURIComponent( row.biblio_id );
346
                                    return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?' +
347
                                    'itemnumber='+ item_id +'&biblionumber='+ biblio_id +'&' +
348
                                    'bi='+ biblio_id +'#item'+ item_id +'">' + escape_str( data ) + '</a>'
349
                                }
350
                            }
351
                        },
352
                        {
353
                            "data": "patron_id",
354
                            "name": "patron",
355
                            "title": _("Patron"),
356
                            "orderable": true,
357
                            "render": function( data, type, row, meta ) {
358
                                let patron_to_html = $patron_to_html(row.patron, { url: true, display_cardnumber: true, hide_patron_name });
359
                                return patron_to_html;
360
                            }
361
                        },
362
                        {
363
                            "data": "status",
364
                            "name": "status",
365
                            "className": "found_status",
366
                            "title": _("Status"),
367
                            "orderable": true,
368
                            "render": function( data, type, row, meta ) {
369
                                let status = _("No status");
370
                                if ( data == "T" ) {
371
                                    status = _("In transit");
372
                                } else if( data == "P" ) {
373
                                    status = _("In processing");
374
                                } else if( data == "W" ) {
375
                                    status = _("Waiting");
376
                                }
377
                                return status;
378
                            }
379
                        },
380
                        {
381
                            "data": "pickup_library_id",
382
                            "name": "pickup_library",
383
                            "title": _("Hold pickup library"),
384
                            "orderable": true,
385
                            "render": function( data, type, row, meta ) {
386
                                return escape_str( row.pickup_library.name );
387
                            }
388
                        },
389
                        {
390
                            "data": "suspended",
391
                            "name": "suspended",
392
                            "title": _("Suspended"),
393
                            "orderable": true,
394
                            "render": function( data, type, row, meta ) {
395
                                return data == 0 ? _("No") : _("Yes");
396
                            }
397
                        },
398
                        {
399
                            "data": "suspended_until",
400
                            "name": "suspended_until",
401
                            "title": _("Suspended until"),
402
                            "orderable": true,
403
                            "render": function( data, type, row, meta ) {
404
                                return $date( data );
405
                            }
406
                        },
407
                        {
408
                            "data": "notes",
409
                            "name": "notes",
410
                            "title": _("Notes"),
411
                            "orderable": true,
412
                            "render": function( data, type, row, meta ) {
413
                                return escape_str( data );
414
                            }
415
                        }
416
                    ]
417
                }, table_settings, 0, filters);
418
419
                $(".searchpattern").text(searchpattern);
420
421
                $("#modify_holds_results-wrapper").show();
422
            }
323
423
324
            $("#process").on("submit", function (e) {
424
            $("#process_mods").on('submit', function(e) {
425
426
                var hold_ids = [];
325
                var errors = [];
427
                var errors = [];
326
                var reserve_ids = $("input[type=checkbox][name='hold_id']:checked");
428
                var holds_checked = $("input[type=checkbox][name='hold_id']:checked");
429
430
                var new_expiration_date = $("#new_expiration_date").val();
431
                var new_pickup_loc      = $("#new_pickup_loc").val();
432
                var new_suspend_status  = $("#new_suspend_status").val();
433
                var new_suspend_date    = $("#new_suspend_date").val();
434
                var new_hold_note       = $("#new_hold_note").val();
327
435
328
                var new_pickup_loc = $("#new_pickup_loc").val();
436
                if ( holds_checked.length == 0 ) {
329
                var new_suspend_status = $("#new_suspend_status").val();
437
                    e.preventDefault();
330
                var new_suspend_date = $("#new_suspend_date").val();
438
                    errors.push(_("Please select at least one hold to process."));
439
                }
331
440
332
                if (new_pickup_loc || new_suspend_status) {
441
                if( new_pickup_loc || new_suspend_status ){
333
                    reserve_ids.each(function () {
442
                    var found = false;
334
                        if ($(this).parents("tr").children(".found_status").data("found") != "") {
443
                    holds_checked.each(function(){
444
                        if($(this).parents("tr").children(".found_status").html() !== "No status"){
335
                            e.preventDefault();
445
                            e.preventDefault();
336
                            if (new_suspend_status) {
446
                            found = true;
337
                                errors.push(_("One or more holds have found status and can't be suspended."));
338
                            }
339
                            if (new_pickup_loc) {
340
                                errors.push(_("One or more holds have found status and their pick up location can't be changed."));
341
                            }
342
                            return false;
343
                        }
447
                        }
344
                    });
448
                    });
449
                    if( new_suspend_status && found ){
450
                        errors.push(_("One or more holds have found status and can't be suspended."));
451
                    }
452
                    if( new_pickup_loc && found ){
453
                        errors.push(_("One or more holds have found status and their pick up location can't be changed."));
454
                    }
345
                }
455
                }
346
                if ((!new_suspend_status || new_suspend_status == "not_suspended") && new_suspend_date) {
456
                if( ( !new_suspend_status || new_suspend_status == "0") && new_suspend_date ){
347
                    e.preventDefault();
457
                    e.preventDefault();
348
                    errors.push(_("You have to suspend holds if new suspend until date is set."));
458
                    errors.push(_("You have to suspend holds if new suspend until date is set."));
349
                    return false;
350
                }
459
                }
351
460
352
                if (errors.length > 0) {
461
                if (errors.length > 0) {
353
                    e.preventDefault();
462
                    e.preventDefault();
354
                    $("#errorList").html(errors.map(error => `<li>${error}</li>`).join(""));
463
                    $("#errorList").html(errors.map(error => `<li>${error}</li>`).join(""));
355
                    $("#errorModal").modal("show");
464
                    $("#errorModal").modal('show');
356
                    return false;
465
                    return false;
357
                }
466
                }
358
467
359
                return true;
468
                return true;
360
            });
469
            });
470
471
            //Modified holds table
472
            var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'holds', 'modified_holds', 'json' ) | $raw %];
473
                var filters = {
474
                    "me.hold_id": function(){
475
                        return {"-in": hold_ids};
476
                    }
477
                }
478
479
                var holds_modified_table = $("#modified_holds").kohaTable({
480
                    "ajax": {
481
                        "url": "/api/v1/holds"
482
                    },
483
                    "embed": [
484
                        "biblio",
485
                        "item",
486
                        "pickup_library",
487
                        "patron"
488
                    ],
489
                    "destroy": true,
490
                    "autoWidth": false,
491
                    "processing": true,
492
                    "columns": [
493
                        {
494
                            "data": "expiration_date",
495
                            "name": "expiration_date",
496
                            "type": "date",
497
                            "title": _("Expiration date"),
498
                            "orderable": true,
499
                            "render": function( data, type, row, meta ) {
500
                                return $date(data);
501
                            }
502
                        },
503
                        {
504
                            "data": "biblio.title",
505
                            "name": "title",
506
                            "title": _("Title"),
507
                            "orderable": true,
508
                            "render": function( data, type, row, meta ) {
509
                                return '<a href="/cgi-bin/koha/catalogue/detail.pl?' +
510
                                'biblionumber=' + encodeURIComponent( row.biblio_id ) + '">' + escape_str( data ) + '</a>';
511
                            }
512
                        },
513
                        {
514
                            "data": "item.external_id",
515
                            "name": "barcode",
516
                            "title": _("Barcode"),
517
                            "defaultContent": _(""),
518
                            "orderable": true,
519
                            "render": function( data, type, row, meta ) {
520
                                if( row.item ){
521
                                    let item_id = encodeURIComponent( row.item_id );
522
                                    let biblio_id = encodeURIComponent( row.biblio_id );
523
                                    return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?' +
524
                                    'itemnumber='+ item_id +'&biblionumber='+ biblio_id +'&' +
525
                                    'bi='+ biblio_id +'#item'+ item_id +'">' + escape_str( data ) + '</a>'
526
                                }
527
                            }
528
                        },
529
                        {
530
                            "data": "patron_id",
531
                            "name": "patron",
532
                            "title": _("Patron"),
533
                            "orderable": true,
534
                            "render": function( data, type, row, meta ) {
535
                                let patron_to_html = $patron_to_html(row.patron, { url: true, display_cardnumber: true, hide_patron_name });
536
                                return patron_to_html;
537
                            }
538
                        },
539
                        {
540
                            "data": "status",
541
                            "name": "status",
542
                            "className": "found_status",
543
                            "title": _("Status"),
544
                            "orderable": true,
545
                            "render": function( data, type, row, meta ) {
546
                                let status = _("No status");
547
                                if ( data == "T" ) {
548
                                    status = _("In transit");
549
                                } else if( data == "P" ) {
550
                                    status = _("In processing");
551
                                } else if( data == "W" ) {
552
                                    status = _("Waiting");
553
                                }
554
                                return status;
555
                            }
556
                        },
557
                        {
558
                            "data": "pickup_library_id",
559
                            "name": "pickup_library",
560
                            "title": _("Hold pickup library"),
561
                            "orderable": true,
562
                            "render": function( data, type, row, meta ) {
563
                                return escape_str( row.pickup_library.name );
564
                            }
565
                        },
566
                        {
567
                            "data": "suspended",
568
                            "name": "suspended",
569
                            "title": _("Suspended"),
570
                            "orderable": true,
571
                            "render": function( data, type, row, meta ) {
572
                                return data == 0 ? _("No") : _("Yes");
573
                            }
574
                        },
575
                        {
576
                            "data": "suspended_until",
577
                            "name": "suspended_until",
578
                            "title": _("Suspended until"),
579
                            "orderable": true,
580
                            "render": function( data, type, row, meta ) {
581
                                return $date( data );
582
                            }
583
                        },
584
                        {
585
                            "data": "notes",
586
                            "name": "notes",
587
                            "title": _("Notes"),
588
                            "orderable": true,
589
                            "render": function( data, type, row, meta ) {
590
                                return escape_str( data );
591
                            }
592
                        }
593
                    ]
594
                }, table_settings, 0, filters);
595
596
                var modified_message = '<div class="alert alert-info">'+hold_ids.length+' hold(s) have been modified!</div>';
597
                $("#modified_holds_results-wrapper").prepend(modified_message);
598
361
        });
599
        });
362
    </script>
600
    </script>
363
[% END %]
601
[% END %]
(-)a/tools/batch_modify_holds.pl (-94 / +9 lines)
Lines 19-28 Link Here
19
# <http://www.gnu.org/licenses>
19
# <http://www.gnu.org/licenses>
20
20
21
use Modern::Perl;
21
use Modern::Perl;
22
23
use CGI;
22
use CGI;
24
23
25
use C4::Auth   qw( get_template_and_user );
24
use JSON qw( to_json );
25
26
use C4::Auth qw( get_template_and_user );
26
use C4::Output qw( output_html_with_http_headers );
27
use C4::Output qw( output_html_with_http_headers );
27
28
28
use Koha::DateUtils qw( dt_from_string );
29
use Koha::DateUtils qw( dt_from_string );
Lines 44-141 my @hold_ids; Link Here
44
45
45
if ( $op eq 'form' ) {
46
if ( $op eq 'form' ) {
46
    $template->param( view => 'form', );
47
    $template->param( view => 'form', );
47
} elsif ( $op eq 'list' ) {
48
49
    my @reserve_ids         = $input->multi_param('reserve_ids');
50
    my $expirationdate_from = $input->param('expirationdate_from');
51
    my $expirationdate_to   = $input->param('expirationdate_to');
52
    my @branchcodes         = $input->multi_param('branchcodes');
53
    my @found_status        = $input->multi_param('found_status');
54
    my $suspend_status      = $input->param('suspend_status');
55
    my $suspend_until_from  = $input->param('suspend_until_from');
56
    my $suspend_until_to    = $input->param('suspend_until_to');
57
    my $reservenotes        = $input->param('reservenotes');
58
59
    my $search_params;
60
61
    if (@reserve_ids) {
62
        $search_params->{'me.reserve_id'} = { -in => \@reserve_ids };
63
    } else {
64
        if ( $expirationdate_from && $expirationdate_to ) {
65
            my $expirationdate_params = {
66
                -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
82
            $search_params = $expirationdate_params;
83
        }
84
85
        if (@branchcodes) {
86
            $search_params->{'me.branchcode'} = { -in => \@branchcodes };
87
        }
88
89
        if (@found_status) {
90
            my $found_params = {};
91
92
            # if NULL is used as filter, use it as "is" param
93
            if ( grep { $_ eq "NULL" } @found_status ) {
94
                $found_params->{'-or'}{'-is'} = undef;
95
                $found_params->{'-or'}{'-in'} = \@found_status;
96
            } else {
97
                $found_params = { -in => \@found_status };
98
            }
99
100
            $search_params->{'me.found'} = $found_params;
101
        }
102
103
        if ( $suspend_status ne "none" ) {
104
            $search_params->{'me.suspend'} = $suspend_status;
105
        }
106
107
        if ( $suspend_until_from && $suspend_until_to ) {
108
            $search_params->{'me.suspend_until'} = {
109
                -between => [
110
                    $suspend_until_from,
111
                    $suspend_until_to,
112
                ]
113
            };
114
        }
115
116
        if ($reservenotes) {
117
            $search_params->{'me.reservenotes'} = { -like => "%" . $reservenotes . "%" };
118
        }
119
    }
120
121
    my $holds = Koha::Holds->search(
122
        $search_params,
123
        { join => [ "item", "biblio" ] }
124
    );
125
126
    $template->param(
127
        holds => $holds,
128
        view  => 'list',
129
    );
130
}
48
}
131
49
elsif ( $op eq 'cud-modify' ) {
132
if ( $op eq 'cud-modify' ) {
133
134
    my $new_expiration_date = $input->param('new_expiration_date');
50
    my $new_expiration_date = $input->param('new_expiration_date');
135
    my $new_pickup_loc      = $input->param('new_pickup_loc');
51
    my $new_pickup_loc      = $input->param('new_pickup_loc');
136
    my $new_suspend_status  = $input->param('new_suspend_status');
52
    my $new_suspend_status  = $input->param('new_suspend_status');
137
    my $new_suspend_date    = $input->param('new_suspend_date');
53
    my $new_suspend_date    = $input->param('new_suspend_date');
138
    my $new_reserve_note    = $input->param('new_reserve_note');
54
    my $new_hold_note       = $input->param('new_hold_note');
139
55
140
    @hold_ids = $input->multi_param('hold_id');
56
    @hold_ids = $input->multi_param('hold_id');
141
57
Lines 153-159 if ( $op eq 'cud-modify' ) { Link Here
153
        }
69
        }
154
70
155
        if ( $new_suspend_status && !$hold->is_found ) {
71
        if ( $new_suspend_status && !$hold->is_found ) {
156
            if ( $new_suspend_status eq "suspend" ) {
72
            if ( $new_suspend_status ) {
157
                $hold->suspend(1)->store;
73
                $hold->suspend(1)->store;
158
                if ($new_suspend_date) {
74
                if ($new_suspend_date) {
159
                    $hold->suspend_until($new_suspend_date)->store;
75
                    $hold->suspend_until($new_suspend_date)->store;
Lines 166-178 if ( $op eq 'cud-modify' ) { Link Here
166
            }
82
            }
167
        }
83
        }
168
84
169
        if ($new_reserve_note) {
85
        if ($new_hold_note) {
170
            $hold->reservenotes($new_reserve_note)->store;
86
            $hold->reservenotes($new_hold_note)->store;
171
        }
87
        }
172
    }
88
    }
173
89
174
    $template->param(
90
    $template->param(
175
        updated_holds => $holds_to_update,
91
        updated_holds => to_json(\@hold_ids),
176
        view          => 'report',
92
        view          => 'report',
177
    );
93
    );
178
94
179
- 

Return to bug 36135