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

(-)a/admin/columns_settings.yml (-1 / +46 lines)
Lines 1987-1993 modules: Link Here
1987
              columnname: suspend
1987
              columnname: suspend
1988
            -
1988
            -
1989
              columnname: print_slip
1989
              columnname: print_slip
1990
1990
      holds_to_modify:
1991
        default_display_length: 20
1992
        columns:
1993
            -
1994
              columnname: checkbox
1995
              cannot_be_toggled: 1
1996
              cannot_be_modified: 1
1997
            -
1998
              columnname: expiration_date
1999
            -
2000
              columnname: title
2001
            -
2002
              columnname: barcode
2003
            -
2004
              columnname: patron
2005
            -
2006
              columnname: status
2007
            -
2008
              columnname: pickup_library
2009
            -
2010
              columnname: suspended
2011
            -
2012
              columnname: suspended_until
2013
            -
2014
              columnname: notes
2015
      modified_holds:
2016
        default_display_length: 20
2017
        columns:
2018
            -
2019
              columnname: expiration_date
2020
            -
2021
              columnname: title
2022
            -
2023
              columnname: barcode
2024
            -
2025
              columnname: patron
2026
            -
2027
              columnname: status
2028
            -
2029
              columnname: pickup_library
2030
            -
2031
              columnname: suspended
2032
            -
2033
              columnname: suspended_until
2034
            -
2035
              columnname: notes
1991
    holdsratios:
2036
    holdsratios:
1992
      holds-ratios:
2037
      holds-ratios:
1993
        default_display_length: 20
2038
        default_display_length: 20
(-)a/api/v1/swagger/definitions/hold.yaml (+5 lines)
Lines 135-139 properties: Link Here
135
      - object
135
      - object
136
      - "null"
136
      - "null"
137
    description: Pickup library
137
    description: Pickup library
138
  patron:
139
    type:
140
      - object
141
      - "null"
142
    description: Patron for hold
138
143
139
additionalProperties: false
144
additionalProperties: false
(-)a/api/v1/swagger/paths/holds.yaml (+1 lines)
Lines 106-111 Link Here
106
            - deleted_biblio
106
            - deleted_biblio
107
            - pickup_library
107
            - pickup_library
108
            - item
108
            - item
109
            - patron
109
        collectionFormat: csv
110
        collectionFormat: csv
110
    produces:
111
    produces:
111
      - application/json
112
      - application/json
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt (-190 / +537 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 35-56 Link Here
35
    [% END #/ WRAPPER breadcrumbs %]
37
    [% END #/ WRAPPER breadcrumbs %]
36
[% END #/ WRAPPER sub-header.inc %]
38
[% END #/ WRAPPER sub-header.inc %]
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
44
                <h1>Batch modify holds</h1>
43
                    <h1>Batch modify holds</h1>
45
                [% IF view == "form" %]
44
46
                    <div id="modify_holds_search">
45
                    [% IF view == 'form' %]
46
                        <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">
47
                            <fieldset class="rows">
48
                            <fieldset class="rows">
48
                                <legend>Hold search criteria:</legend>
49
                                <legend>Hold search criteria:</legend>
49
                                <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>
50
                                <ol>
51
                                    <li>
51
                                    <li>
52
                                        <label for="expirationdate_from">Expiration date from:</label>
52
                                        <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="to" />
53
                                        <input type="text" size="10" id="expirationdate_from" name="expirationdate_from" class="flatpickr" data-date_to="expirationdate_to" />
54
                                    </li>
54
                                    </li>
55
                                    <li>
55
                                    <li>
56
                                        <label for="expirationdate_to">Expiration date to:</label>
56
                                        <label for="expirationdate_to">Expiration date to:</label>
Lines 81-318 Link Here
81
                                    </li>
81
                                    </li>
82
                                    <li>
82
                                    <li>
83
                                        <label for="suspend_until_from">Suspended until from:</label>
83
                                        <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="to" />
84
                                        <input type="text" size="10" id="suspend_until_from" name="suspend_until_from" class="flatpickr" data-date_to="suspend_until_to" />
85
                                    </li>
85
                                    </li>
86
                                    <li>
86
                                    <li>
87
                                        <label for="suspend_until_to">Suspended until to:</label>
87
                                        <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"/>
88
                                        <input type="text" size="10" id="suspend_until_to" name="suspend_until_to" class="flatpickr"/>
89
                                    </li>
89
                                    </li>
90
                                    <li>
90
                                    <li>
91
                                        <label for="reservenotes">Hold note:</label>
91
                                        <label for="holdnotes">Hold note:</label>
92
                                        <input type="text" id="reservenotes" name="reservenotes"/>
92
                                        <input type="text" id="holdnotes" name="holdnotes"/>
93
                                    </li>
93
                                    </li>
94
                                </ol>
94
                                </ol>
95
                            </fieldset>
95
                            </fieldset>
96
                            <fieldset class="action">
96
                            <fieldset class="action">
97
                                <input type="hidden" name="op" value="list"/>
97
                                <input type="submit" class="btn btn-primary" value="Search"/>
98
                                <input type="submit" class="btn btn-primary" value="Continue"/>
99
                                <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a>
100
                            </fieldset>
98
                            </fieldset>
101
                        </form> <!-- /#modify_holds_form -->
99
                        </form> <!-- /#modify_holds_form -->
102
                    [% ELSIF view == 'list' %]
100
                    </div> <!-- /#modify_holds_search -->
103
                        <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process">
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 -->
106
                        <fieldset class="action">
107
                            <a href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a>
108
                        </fieldset >
109
                    </div> <!-- /#modified_holds_results-wrapper -->
110
                [% END %]
111
                <div id="modify_holds_results-wrapper" style="display:none;">
112
                    <div id="toolbar" class="btn-toolbar">
113
                        <a href="#" id="edit_search" class="btn btn-default"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit search</a>
114
                    </div> <!-- /#toolbar -->
115
                    <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process_mods">
104
                        [% INCLUDE 'csrf-token.inc' %]
116
                        [% INCLUDE 'csrf-token.inc' %]
105
                        [% IF holds.count %]
117
                        <div id="modify_holds_results" class="page-section">
106
                            <div><p>Found [% holds.count | html %][% IF holds.count > 1 %] holds [% ELSE %] hold [% END %]to modify.</p></div>
118
                            <div id="searchheader" class="searchheader">
107
                            <div class="page-section">
119
                                <a id="select_all" href="#" class="btn btn-link"><i class="fa fa-check"></i> Select all visible rows</a>
108
                                <div class="btn-toolbar selections-toolbar">
120
                                | <a id="clear_all" href="#" class="btn btn-link"><i class="fa fa-times"></i> Clear selections</a>
109
                                    <a id="selectall" href="#"><i class="fa fa-check"></i> Select all</a>
121
                                <div id="table_search_selections" class="btn-group" style="display:none;">
110
                                    <a id="clearall" href="#"><i class="fa fa-remove"></i> Clear all</a>
122
                                    <span></span>
123
                                    <a href="#" id="clear-row-selection"><i class="fa fa-times"></i> Clear</a>
111
                                </div>
124
                                </div>
112
                                <table id="found_holds">
125
                            </div> <!-- /#searchheader -->
113
                                    <thead>
126
                            <h3>Holds found for: <span class="searchpattern"></span></h3>
114
                                        <tr>
127
                            <table id="holds_to_modify"></table>
115
                                            <th>&nbsp;</th>
128
                        </div> <!-- /#modify_holds_results -->
116
                                            <th>Expiration date</th>
129
                        <div id="modify_holds_form_options" class="page-section">
117
                                            <th>Patron expiration date</th>
118
                                            <th>Title</th>
119
                                            <th>Barcode</th>
120
                                            <th>Status</th>
121
                                            <th>Hold pickup library</th>
122
                                            <th>Suspended</th>
123
                                            <th>Suspended until</th>
124
                                            <th>Note</th>
125
                                        </tr>
126
                                    </thead>
127
                                    <tbody>
128
                                        [% FOREACH hold IN holds %]
129
                                            <tr>
130
                                                <td><input type="checkbox" name="hold_id" value="[% hold.reserve_id | html %]"/></td>
131
                                                <td>[% hold.expirationdate | $KohaDates %]</td>
132
                                                <td>[% hold.patron_expiration_date | $KohaDates %]</td>
133
                                                <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.biblio.title | html %]</a></td>
134
                                                <td><a href="/cgi-bin/koha/catalogue/moredetail.pl?itemnumber=[% hold.itemnumber | uri %]&biblionumber=[% hold.biblionumber | uri %]&bi=[% hold.biblionumber | uri %]#item[% hold.itemnumber | uri %]">[% hold.item.barcode | html %]</a></td>
135
                                                <td class="found_status" data-found="[% hold.found | html %]">
136
                                                    [% IF hold.found == "T" %]
137
                                                        In transit
138
                                                    [% ELSIF hold.found == "P" %]
139
                                                        In processing
140
                                                    [% ELSIF hold.found == "W" %]
141
                                                        Waiting
142
                                                    [% ELSE %]
143
                                                        No status
144
                                                    [% END %]
145
                                                </td>
146
                                                <td>[% Branches.GetName( hold.branchcode ) | html %]</td>
147
                                                <td>[% IF hold.suspend %]Yes[% ELSE %]No[% END %]</td>
148
                                                <td>[% hold.suspend_until | $KohaDates %]</td>
149
                                                <td>[% hold.reservenotes | html %]</td>
150
                                            </tr>
151
                                        [% END %]
152
                                    </tbody>
153
                                </table> <!-- /#holds -->
154
                            </div> <!-- /.page-section -->
155
                            <h2>Modify holds</h2>
130
                            <h2>Modify holds</h2>
156
                            <div class="page-section">
131
                            <table id="modify_holds_options">
157
                                <table id="hold_modifies">
158
                                    <thead>
159
                                        <tr>
160
                                            <th>New expiration date</th>
161
                                            <th>New pickup library</th>
162
                                            <th>Suspend holds</th>
163
                                            <th>Suspend until</th>
164
                                            <th>New reserve note</th>
165
                                        </tr>
166
                                    </thead>
167
                                    <tbody>
168
                                        <tr>
169
                                            <td>
170
                                                <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true"/>
171
                                            </td>
172
                                            <td>
173
                                                <select id="new_pickup_loc" name="new_pickup_loc">
174
                                                    <option value="" selected="selected"></option>
175
                                                    [% PROCESS options_for_libraries libraries => Branches.pickup_locations(selected = "") %]
176
                                                </select>
177
                                            </td>
178
                                            <td>
179
                                                <select id="new_suspend_status" name="new_suspend_status">
180
                                                    <option value="" selected="selected"></option>
181
                                                    <option value="not_suspended">Not suspended</option>
182
                                                    <option value="suspend">Suspend</option>
183
                                                </select>
184
                                            </td>
185
                                            <td>
186
                                                <input type="text" id="new_suspend_date" name="new_suspend_date" class="flatpickr" data-flatpickr-futuredate="true"/>
187
                                            </td>
188
                                            <td>
189
                                               <input type="text" id="new_reserve_note" name="new_reserve_note"/>
190
                                            </td>
191
                                        </tr>
192
                                    </tbody>
193
                                </table> <!-- /#hold_modifies -->
194
                            </div>
195
                            <fieldset class="action">
196
                                <input type="hidden" name="op" value="cud-modify"/>
197
                                <input type="submit" class="btn btn-primary" value="Modify selected holds"/>
198
                                <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a>
199
                            </fieldset>
200
                        </form> <!-- /#process -->
201
                        [% ELSE %]
202
                            <div class="dialog message">
203
                                No holds were found for the selected filters.
204
                            </div>
205
                            <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return</a>
206
                        [% END %]
207
                    [% ELSIF view == 'report' %]
208
                        <div class="dialog message">
209
                            [% updated_holds.count | html %] holds have been modified!
210
                        </div>
211
                        <div class="page-section">
212
                            <table id="holds">
213
                                <thead>
132
                                <thead>
214
                                    <tr>
133
                                    <tr>
215
                                        <th>Expiration date</th>
134
                                        <th>New expiration date</th>
216
                                        <th>Title</th>
135
                                        <th>New pickup library</th>
217
                                        <th>Barcode</th>
136
                                        <th>Suspend holds</th>
218
                                        <th>Hold pickup library</th>
137
                                        <th>Suspend until</th>
219
                                        <th>Suspended</th>
138
                                        <th>New hold note</th>
220
                                        <th>Suspended until</th>
139
                                        <th>Clear hold notes</th>
221
                                        <th>Note</th>
222
                                    </tr>
140
                                    </tr>
223
                                </thead>
141
                                </thead>
224
                                <tbody>
142
                                <tbody>
225
                                    [% FOREACH hold IN updated_holds %]
143
                                    <tr>
226
                                        <tr>
144
                                        <td>
227
                                            <td>[% hold.expirationdate | $KohaDates %]</td>
145
                                            <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true" />
228
                                            <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.biblio.title | html %]</a></td>
146
                                        </td>
229
                                            <td><a href="/cgi-bin/koha/catalogue/moredetail.pl?itemnumber=[% hold.itemnumber | uri %]&biblionumber=[% hold.biblionumber | uri %]&bi=[% hold.biblionumber | uri %]#item[% hold.itemnumber | uri %]">[% hold.item.barcode | html %]</a></td>
147
                                        <td>
230
                                            <td>[% Branches.GetName( hold.branchcode ) | html %]</td>
148
                                            <select name="new_pickup_loc" id="new_pickup_loc">
231
                                            <td>[% IF hold.suspend == 0 %]No[% ELSE %]Yes[% END %]</td>
149
                                                <option value="" selected="selected"></option>
232
                                            <td>[% hold.suspend_until | $KohaDates %]</td>
150
                                                [% PROCESS options_for_libraries libraries => Branches.pickup_locations({ selected => "" }) %]
233
                                            <td>[% hold.reservenotes | html %]</td>
151
                                            </select>
234
                                        </tr>
152
                                        </td>
235
                                    [% END %]
153
                                        <td>
154
                                            <select id="new_suspend_status" name="new_suspend_status">
155
                                                <option value="" selected="selected"></option>
156
                                                <option value="0">Not suspended</option>
157
                                                <option value="1">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_hold_note" name="new_hold_note"/>
165
                                        </td>
166
                                        <td>
167
                                           <input class="selection" type="checkbox" id="clear_hold_notes" name="clear_hold_notes" value="1"/>
168
                                        </td>
169
                                    </tr>
236
                                </tbody>
170
                                </tbody>
237
                            </table> <!-- /#holds -->
171
                            </table> <!-- /#modify_holds_options -->
238
                        </div> <!-- /.page-section -->
172
                        </div> <!-- /#modify_holds_options -->
239
                        <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a>
173
                        <fieldset class="action">
240
                    [% END %]
174
                            <input type="hidden" name="op" value="cud-modify" />
241
                </main>
175
                            <input type="submit" class="btn btn-primary" value="Modify holds" />
242
            </div> <!-- /.col-sm-10 col-sm-push-2 -->
176
                            <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a>
243
177
                        </fieldset>
244
178
                    </form> <!-- /#process_mods -->
245
            <div class="col-sm-2 col-sm-pull-10">
179
                </div> <!-- /#modify_holds_results-wrapper -->
246
                <aside>
180
            </main>
247
                    [% INCLUDE 'tools-menu.inc' %]
181
        </div> <!-- /.col-sm-10 col-sm-push-2 -->
248
                </aside>
182
        <div class="col-sm-2 col-sm-pull-10">
249
            </div> <!-- /.col-sm-2 col-sm-pull-10 -->
183
            <aside>
250
        </div> <!-- /.row -->
184
                [% INCLUDE 'tools-menu.inc' %]
185
            </aside>
186
        </div> <!-- /.col-sm-2 col-sm-pull-10 -->
251
    </div>
187
    </div>
252
[% MACRO jsinclude BLOCK %]
188
[% MACRO jsinclude BLOCK %]
253
    [% Asset.js("js/tools-menu.js") | $raw %]
189
    [% Asset.js("js/tools-menu.js") | $raw %]
254
    [% INCLUDE 'calendar.inc' %]
190
    [% INCLUDE 'calendar.inc' %]
255
    [% INCLUDE 'datatables.inc' %]
191
    [% INCLUDE 'datatables.inc' %]
192
    [% INCLUDE 'select2.inc' %]
193
    [% INCLUDE 'js-patron-format.inc' %]
256
    [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %]
194
    [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %]
257
    <script>
195
    <script>
196
        let hide_patron_name = [% IF Koha.Preference('HidePatronName') %]true[% ELSE %]false[% END %];
197
        let hold_ids = [% IF updated_holds %] [% updated_holds | $raw %] [% ELSE %] [] [% END %];
198
258
        $(document).ready(function() {
199
        $(document).ready(function() {
259
200
260
            $("#selectall").click(function(e){
201
            // Apply select2 to all select fields having a "multiple" attribute
202
            let selectFields = document.querySelectorAll('select[multiple]');
203
            selectFields.forEach((selectField) => {
204
                selectField.style.minWidth = '200px';
205
                $(selectField).select2();
206
            });
207
208
            //Empty local storage from previous selections
209
            localStorage.removeItem("holds_modify_selections");
210
211
            $("#holds_to_modify").on("change", "input[type='checkbox']", function(){
212
                var holds_modify_selections = JSON.parse(localStorage.getItem("holds_modify_selections")) || [];
213
                var hold_id = $(this).val();
214
                if ($(this).prop("checked")) {
215
                    holds_modify_selections.push($(this).val());
216
                    localStorage.setItem("holds_modify_selections", JSON.stringify(holds_modify_selections));
217
                    showHoldSelections(holds_modify_selections.length);
218
                } else {
219
                    var filtered = holds_modify_selections.filter(function( value ){
220
                        return value !== hold_id;
221
                    });
222
                    if( filtered.length > 0 ){
223
                        localStorage.setItem("holds_modify_selections", JSON.stringify( filtered ));
224
                        holds_modify_selections = filtered;
225
                        showHoldSelections( filtered.length );
226
                    } else {
227
                        holds_modify_selections = [];
228
                        localStorage.removeItem("holds_modify_selections");
229
                        showHoldSelections( 0 );
230
                    }
231
                }
232
            });
233
234
            $("#select_all").on("click", function(e){
261
                e.preventDefault();
235
                e.preventDefault();
262
                $("#found_holds input[type='checkbox']").each(function(){
236
                $("#holds_to_modify input[type='checkbox']").each(function(){
263
                    $(this).prop("checked", true);
237
                    $(this).prop("checked", true).change();
264
                });
238
                });
265
            });
239
            });
266
240
267
            $("#clearall").click(function(e){
241
            $("#clear_all").on("click", function(e){
268
                e.preventDefault();
242
                e.preventDefault();
269
                $("#found_holds input[type='checkbox']").each(function(){
243
                $("#holds_to_modify input[type='checkbox']").each(function(){
270
                    $(this).prop("checked", false);
244
                    $(this).prop("checked", false).change();
271
                });
245
                });
272
            });
246
            });
273
247
274
            $("#selectall").click();
248
            $("#clear-row-selection").on("click", function (e) {
249
                e.preventDefault();
250
                $("input[type='checkbox']").prop("checked", false).change();
251
                localStorage.removeItem("holds_modify_selections");
252
                $("#table_search_selections").hide();
253
            });
275
254
276
            $("#found_holds").dataTable($.extend(true, {}, dataTablesDefaults, {
255
            $('#modify_holds_form').submit(function() {
277
                "aoColumnDefs": [
256
                var modify_holds_form = $(this);
278
                    { "aTargets": [0], "bSortable": false, "bSearchable": false },
257
                search_holds(modify_holds_form);
279
                ],
258
                $("#modify_holds_search").hide();
280
                "sDom": 't',
259
                $('#edit_search').show();
281
                "aaSorting": [],
260
                return false;
282
                "bPaginate": false
261
            });
283
            }));
262
263
            $("#edit_search").on("click", function(e){
264
                e.preventDefault();
265
                $('#modify_holds_search').show();
266
                $('#edit_search').hide();
267
            });
268
269
            function prepSelections(){
270
                let holds_modify_selections = JSON.parse( localStorage.getItem("holds_modify_selections") ) || [];
271
                if( holds_modify_selections.length > 0 ){
272
                    showHoldSelections( holds_modify_selections.length );
273
                    $("#holds_to_modify input[type='checkbox']").each(function(){
274
                        var hold_id = $(this).val();
275
                        if( holds_modify_selections.indexOf( hold_id ) >= 0 ){
276
                            $(this).prop("checked", true );
277
                        }
278
                    });
279
                }
280
            }
281
282
            function showHoldSelections( number ){
283
                if (number === 0) {
284
                    $("#table_search_selections").hide();
285
                } else {
286
                    $("#table_search_selections")
287
                        .show()
288
                        .find("span")
289
                        .text(__("Holds selected: %s").format(number));
290
                }
291
            }
292
293
            function search_holds(modify_holds_form){
294
                var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'holds', 'holds_to_modify', 'json' ) | $raw %];
295
                var searchpattern = "";
296
                var filters = {
297
                    "me.expirationdate": function(){
298
                        var expirationdate_from = modify_holds_form.find("#expirationdate_from").val();
299
                        var expirationdate_to   = modify_holds_form.find("#expirationdate_to").val();
300
301
                        searchpattern += expirationdate_from && expirationdate_to ? _("expiration date between ")+ expirationdate_from + " and " + expirationdate_to + " ": "";
302
303
                        return expirationdate_from && expirationdate_to ? {"-between": [expirationdate_from, expirationdate_to]} : "";
304
                    },
305
                    "me.branchcode": function(){
306
                        var branchcodes = modify_holds_form.find("#branchcodes").val();
307
                        if( branchcodes.length > 0 ){
308
                            searchpattern += _("from libraries ");
309
                            var selected_libraries = modify_holds_form.find("#branchcodes option:selected");
310
                            $.each(selected_libraries, function(key, value) {
311
                                searchpattern += value.text + " ";
312
                            });
313
                            return {"-in": branchcodes };
314
                        }
315
                    },
316
                    "me.status": function(){
317
                        var found_status = modify_holds_form.find("#found_status").val();
318
                        if( found_status.length > 0 ){
319
                            var filters = [{"-in": found_status}];
320
                            if(jQuery.inArray("NULL", found_status) !== -1){
321
                                filters.push({"=": null});
322
                            }
323
                            searchpattern += _("found status in ");
324
                            $.each(found_status, function(key, value) {
325
                                let status = _("No status ");
326
                                if ( value == "T" ) {
327
                                    status = _("In transit ");
328
                                } else if( value == "P" ) {
329
                                    status = _("In processing ");
330
                                } else if( value == "W" ) {
331
                                    status = _("Waiting ");
332
                                }
333
                                searchpattern += status;
334
                            });
335
                            return filters;
336
                        }
337
                    },
338
                    "me.suspended": function(){
339
                        var suspend_status = modify_holds_form.find("#suspend_status").val();
340
341
                        if(suspend_status != "none"){
342
                            var suspended_text = suspend_status == 0 ? _("Not suspended") : _("Suspended");
343
                            searchpattern += _("with suspend status ") + suspended_text;
344
345
                            return {"=": suspend_status};
346
                        }
347
                    },
348
                    "me.suspend_until": function(){
349
                        var suspend_until_from  = modify_holds_form.find("#suspend_until_from").val();
350
                        var suspend_until_to    = modify_holds_form.find("#suspend_until_to").val();
351
352
                        searchpattern += suspend_until_from && suspend_until_to ? _("suspend date between ")+ suspend_until_from +"-"+suspend_until_to : "";
353
354
                        return suspend_until_from && suspend_until_to ? {"-between": [suspend_until_from, suspend_until_to]} : "";
355
                    },
356
                    "me.notes": function(){
357
                        var holdnotes = modify_holds_form.find("#holdnotes").val();
358
359
                        searchpattern += holdnotes ? _("hold notes like ") + holdnotes : "";
360
361
                        return holdnotes ? {"-like": "%"+holdnotes+"%"} : "";
362
                    }
363
                }
284
364
285
            $("#process").on('submit', function(e) {
365
                var holds_modify_table = $("#holds_to_modify").kohaTable({
366
                    "ajax": {
367
                        "url": "/api/v1/holds"
368
                    },
369
                    "embed": [
370
                        "biblio",
371
                        "item",
372
                        "pickup_library",
373
                        "patron"
374
                    ],
375
                    "destroy": true,
376
                    "autoWidth": false,
377
                    "processing": true,
378
                    "columns": [
379
                        {
380
                            "data": "hold_id",
381
                            "name": "checkbox",
382
                            "orderable": false,
383
                            "render": function(data, type, row, meta) {
384
                                return '<input class="selection" type="checkbox" name="hold_id" value="'+ encodeURIComponent(data) +'"/>'
385
                            }
386
                        },
387
                        {
388
                            "data": "expiration_date",
389
                            "name": "expiration_date",
390
                            "type": "date",
391
                            "title": _("Expiration date"),
392
                            "orderable": true,
393
                            "render": function( data, type, row, meta ) {
394
                                return $date(data);
395
                            }
396
                        },
397
                        {
398
                            "data": "biblio.title",
399
                            "name": "title",
400
                            "title": _("Title"),
401
                            "orderable": true,
402
                            "render": function( data, type, row, meta ) {
403
                                return '<a href="/cgi-bin/koha/catalogue/detail.pl?' +
404
                                'biblionumber=' + encodeURIComponent( row.biblio_id ) + '">' + escape_str( data ) + '</a>';
405
                            }
406
                        },
407
                        {
408
                            "data": "item.external_id",
409
                            "name": "barcode",
410
                            "title": _("Barcode"),
411
                            "defaultContent": _(""),
412
                            "orderable": true,
413
                            "render": function( data, type, row, meta ) {
414
                                if( row.item ){
415
                                    let item_id = encodeURIComponent( row.item_id );
416
                                    let biblio_id = encodeURIComponent( row.biblio_id );
417
                                    return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?' +
418
                                    'itemnumber='+ item_id +'&biblionumber='+ biblio_id +'&' +
419
                                    'bi='+ biblio_id +'#item'+ item_id +'">' + escape_str( data ) + '</a>'
420
                                }
421
                            }
422
                        },
423
                        {
424
                            "data": "patron_id",
425
                            "name": "patron",
426
                            "title": _("Patron"),
427
                            "orderable": true,
428
                            "render": function( data, type, row, meta ) {
429
                                let patron_to_html = $patron_to_html(row.patron, { url: true, display_cardnumber: true, hide_patron_name });
430
                                return patron_to_html;
431
                            }
432
                        },
433
                        {
434
                            "data": "status",
435
                            "name": "status",
436
                            "className": "found_status",
437
                            "title": _("Status"),
438
                            "orderable": true,
439
                            "render": function( data, type, row, meta ) {
440
                                let status = _("No status");
441
                                if ( data == "T" ) {
442
                                    status = _("In transit");
443
                                } else if( data == "P" ) {
444
                                    status = _("In processing");
445
                                } else if( data == "W" ) {
446
                                    status = _("Waiting");
447
                                }
448
                                return "<span data-found-status='" + escape_str( data ) + "'>" + status + "</span>";
449
                            }
450
                        },
451
                        {
452
                            "data": "pickup_library_id",
453
                            "name": "pickup_library",
454
                            "title": _("Hold pickup library"),
455
                            "orderable": true,
456
                            "render": function( data, type, row, meta ) {
457
                                return escape_str( row.pickup_library.name );
458
                            }
459
                        },
460
                        {
461
                            "data": "suspended",
462
                            "name": "suspended",
463
                            "title": _("Suspended"),
464
                            "orderable": true,
465
                            "render": function( data, type, row, meta ) {
466
                                return data == 0 ? _("No") : _("Yes");
467
                            }
468
                        },
469
                        {
470
                            "data": "suspended_until",
471
                            "name": "suspended_until",
472
                            "title": _("Suspended until"),
473
                            "orderable": true,
474
                            "render": function( data, type, row, meta ) {
475
                                return $date( data );
476
                            }
477
                        },
478
                        {
479
                            "data": "notes",
480
                            "name": "notes",
481
                            "title": _("Notes"),
482
                            "orderable": true,
483
                            "render": function( data, type, row, meta ) {
484
                                return escape_str( data );
485
                            }
486
                        }
487
                    ],
488
                    "drawCallback": function( settings ) {
489
                        prepSelections();
490
                    },
491
                }, table_settings, 0, filters);
492
493
                $(".searchpattern").text(searchpattern);
494
495
                $("#modify_holds_results-wrapper").show();
496
            }
497
498
            $("#process_mods").on('submit', function(e) {
499
500
                var hold_ids = [];
286
                var errors = [];
501
                var errors = [];
287
                var reserve_ids = $("input[type=checkbox][name='hold_id']:checked");
502
                var holds_checked = $("input[type=checkbox][name='hold_id']:checked");
288
503
289
                var new_pickup_loc = $("#new_pickup_loc").val();
504
                var new_expiration_date = $("#new_expiration_date").val();
290
                var new_suspend_status = $("#new_suspend_status").val();
505
                var new_pickup_loc      = $("#new_pickup_loc").val();
291
                var new_suspend_date = $("#new_suspend_date").val();
506
                var new_suspend_status  = $("#new_suspend_status").val();
507
                var new_suspend_date    = $("#new_suspend_date").val();
508
                var new_hold_note       = $("#new_hold_note").val();
509
                var clear_hold_notes    = $("#clear_hold_notes").val();
292
510
293
                if ( reserve_ids.length == 0 ) {
511
                if ( holds_checked.length == 0 ) {
294
                    e.preventDefault();
512
                    e.preventDefault();
295
                    errors.push(_("Please select at least one hold to process."));
513
                    errors.push(_("Please select at least one hold to process."));
296
                    return false;
297
                }
514
                }
515
298
                if( new_pickup_loc || new_suspend_status ){
516
                if( new_pickup_loc || new_suspend_status ){
299
                    reserve_ids.each(function(){
517
                    var found = false;
300
                        if($(this).parents("tr").children(".found_status").data("found") != ""){
518
                    holds_checked.each(function(){
519
                        if($(this).parents("tr").children(".found_status").children("span").data("found-status")){
301
                            e.preventDefault();
520
                            e.preventDefault();
302
                            if( new_suspend_status ){
521
                            found = true;
303
                                errors.push(_("One or more holds have found status and can't be suspended."));
304
                            }
305
                            if( new_pickup_loc ){
306
                                errors.push(_("One or more holds have found status and their pick up location can't be changed."));
307
                            }
308
                            return false;
309
                        }
522
                        }
310
                    })
523
                    });
524
                    if( new_suspend_status && found ){
525
                        errors.push(_("One or more holds have found status and can't be suspended."));
526
                    }
527
                    if( new_pickup_loc && found ){
528
                        errors.push(_("One or more holds have found status and their pick up location can't be changed."));
529
                    }
311
                }
530
                }
312
                if( ( !new_suspend_status || new_suspend_status == "not_suspended") && new_suspend_date ){
531
                if( ( !new_suspend_status || new_suspend_status == "0") && new_suspend_date ){
313
                    e.preventDefault();
532
                    e.preventDefault();
314
                    errors.push(_("You have to suspend holds if new suspend until date is set."));
533
                    errors.push(_("You have to suspend holds if new suspend until date is set."));
315
                    return false;
316
                }
534
                }
317
535
318
                if (errors.length > 0) {
536
                if (errors.length > 0) {
Lines 324-329 Link Here
324
542
325
                return true;
543
                return true;
326
            });
544
            });
545
546
            //Modified holds table
547
            var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'holds', 'modified_holds', 'json' ) | $raw %];
548
            var filters = {
549
                "me.hold_id": function(){
550
                    return {"-in": hold_ids};
551
                }
552
            }
553
554
            var holds_modified_table = $("#modified_holds").kohaTable({
555
                "ajax": {
556
                    "url": "/api/v1/holds"
557
                },
558
                "embed": [
559
                    "biblio",
560
                    "item",
561
                    "pickup_library",
562
                    "patron"
563
                ],
564
                "destroy": true,
565
                "autoWidth": false,
566
                "processing": true,
567
                "columns": [
568
                    {
569
                        "data": "expiration_date",
570
                        "name": "expiration_date",
571
                        "type": "date",
572
                        "title": _("Expiration date"),
573
                        "orderable": true,
574
                        "render": function( data, type, row, meta ) {
575
                            return $date(data);
576
                        }
577
                    },
578
                    {
579
                        "data": "biblio.title",
580
                        "name": "title",
581
                        "title": _("Title"),
582
                        "orderable": true,
583
                        "render": function( data, type, row, meta ) {
584
                            return '<a href="/cgi-bin/koha/catalogue/detail.pl?' +
585
                            'biblionumber=' + encodeURIComponent( row.biblio_id ) + '">' + escape_str( data ) + '</a>';
586
                        }
587
                    },
588
                    {
589
                        "data": "item.external_id",
590
                        "name": "barcode",
591
                        "title": _("Barcode"),
592
                        "defaultContent": _(""),
593
                        "orderable": true,
594
                        "render": function( data, type, row, meta ) {
595
                            if( row.item ){
596
                                let item_id = encodeURIComponent( row.item_id );
597
                                let biblio_id = encodeURIComponent( row.biblio_id );
598
                                return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?' +
599
                                'itemnumber='+ item_id +'&biblionumber='+ biblio_id +'&' +
600
                                'bi='+ biblio_id +'#item'+ item_id +'">' + escape_str( data ) + '</a>'
601
                            }
602
                        }
603
                    },
604
                    {
605
                        "data": "patron_id",
606
                        "name": "patron",
607
                        "title": _("Patron"),
608
                        "orderable": true,
609
                        "render": function( data, type, row, meta ) {
610
                            let patron_to_html = $patron_to_html(row.patron, { url: true, display_cardnumber: true, hide_patron_name });
611
                            return patron_to_html;
612
                        }
613
                    },
614
                    {
615
                        "data": "status",
616
                        "name": "status",
617
                        "className": "found_status",
618
                        "title": _("Status"),
619
                        "orderable": true,
620
                        "render": function( data, type, row, meta ) {
621
                            let status = _("No status");
622
                            if ( data == "T" ) {
623
                                status = _("In transit");
624
                            } else if( data == "P" ) {
625
                                status = _("In processing");
626
                            } else if( data == "W" ) {
627
                                status = _("Waiting");
628
                            }
629
                            return status;
630
                        }
631
                    },
632
                    {
633
                        "data": "pickup_library_id",
634
                        "name": "pickup_library",
635
                        "title": _("Hold pickup library"),
636
                        "orderable": true,
637
                        "render": function( data, type, row, meta ) {
638
                            return escape_str( row.pickup_library.name );
639
                        }
640
                    },
641
                    {
642
                        "data": "suspended",
643
                        "name": "suspended",
644
                        "title": _("Suspended"),
645
                        "orderable": true,
646
                        "render": function( data, type, row, meta ) {
647
                            return data == 0 ? _("No") : _("Yes");
648
                        }
649
                    },
650
                    {
651
                        "data": "suspended_until",
652
                        "name": "suspended_until",
653
                        "title": _("Suspended until"),
654
                        "orderable": true,
655
                        "render": function( data, type, row, meta ) {
656
                            return $date( data );
657
                        }
658
                    },
659
                    {
660
                        "data": "notes",
661
                        "name": "notes",
662
                        "title": _("Notes"),
663
                        "orderable": true,
664
                        "render": function( data, type, row, meta ) {
665
                            return escape_str( data );
666
                        }
667
                    }
668
                ]
669
            }, table_settings, 0, filters);
670
671
            var modified_message = '<div class="alert alert-info">'+hold_ids.length+' hold(s) have been modified!</div>';
672
            $("#modified_holds_results-wrapper").prepend(modified_message);
673
327
        });
674
        });
328
    </script>
675
    </script>
329
[% END %]
676
[% END %]
(-)a/tools/batch_modify_holds.pl (-96 / +15 lines)
Lines 19-27 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
24
use JSON qw( to_json );
25
25
use C4::Auth   qw( get_template_and_user );
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
Lines 36-42 my ( $template, $loggedinuser, $cookie ) = get_template_and_user( Link Here
36
        template_name => 'tools/batch_modify_holds.tt',
37
        template_name => 'tools/batch_modify_holds.tt',
37
        query         => $input,
38
        query         => $input,
38
        type          => "intranet",
39
        type          => "intranet",
39
        flagsrequired => { tools => 'batch_modify_holds.tt' },
40
        flagsrequired => { tools => 'batch_modify_holds' },
40
    }
41
    }
41
);
42
);
42
43
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
} elsif ( $op eq 'cud-modify' ) {
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
}
131
132
if ( $op eq 'cud-modify' ) {
133
134
    my $new_expiration_date = $input->param('new_expiration_date');
49
    my $new_expiration_date = $input->param('new_expiration_date');
135
    my $new_pickup_loc      = $input->param('new_pickup_loc');
50
    my $new_pickup_loc      = $input->param('new_pickup_loc');
136
    my $new_suspend_status  = $input->param('new_suspend_status');
51
    my $new_suspend_status  = $input->param('new_suspend_status');
137
    my $new_suspend_date    = $input->param('new_suspend_date');
52
    my $new_suspend_date    = $input->param('new_suspend_date');
138
    my $new_reserve_note    = $input->param('new_reserve_note');
53
    my $new_hold_note       = $input->param('new_hold_note');
54
    my $clear_hold_notes    = $input->param('clear_hold_notes');
139
55
140
    @hold_ids = $input->multi_param('hold_id');
56
    @hold_ids = $input->multi_param('hold_id');
141
57
Lines 152-159 if ( $op eq 'cud-modify' ) { Link Here
152
            $hold->branchcode($new_pickup_loc)->store;
68
            $hold->branchcode($new_pickup_loc)->store;
153
        }
69
        }
154
70
155
        if ( $new_suspend_status && !$hold->is_found ) {
71
        if ( $new_suspend_status ne "" && !$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;
87
        }
88
89
        if ($clear_hold_notes) {
90
            $hold->reservenotes(undef)->store;
171
        }
91
        }
172
    }
92
    }
173
93
174
    $template->param(
94
    $template->param(
175
        updated_holds => $holds_to_update,
95
        updated_holds => to_json( \@hold_ids ),
176
        view          => 'report',
96
        view          => 'report',
177
    );
97
    );
178
98
179
- 

Return to bug 36135