Bug 30063: Number of checkouts
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / includes / patron-search.inc
1 [% USE Koha %]
2 [% USE Branches %]
3 [% USE raw %]
4 [% USE To %]
5
6 [% BLOCK patron_search_filters_simple  %]
7     <form id="patron_search_form">
8         <div class="hint">Enter patron card number or partial name:</div>
9         <input type="text" size="40" id="search_patron_filter" class="focus" autocomplete="off" />
10         <input type="submit" value="Search" />
11     </form>
12 [% END %]
13
14 [% BLOCK patron_search_filters %]
15     <form id="patron_search_form">
16         <fieldset class="brief">
17             <h3>Search for patron</h3>
18             <ol>
19                 <li>
20                     <label for="search_patron_filter">Search:</label>
21                     <div class="hint">Enter patron card number or partial name:</div>
22                     <input type="text" id="search_patron_filter" value="[% search_filter | html %]" class="focus" />
23                 </li>
24
25                 [% FOR column IN columns %]
26                     [% SWITCH column %]
27                     [% CASE 'branch' %]
28                         <li>
29                             <label for="branchcode_filter">Library:</label>
30                             <select id="branchcode_filter">
31                                 [% SET libraries = Branches.all( only_from_group => 1 ) %]
32                                 [% IF libraries.size != 1 %]
33                                     <option value="">Any</option>
34                                 [% END %]
35                                 [% FOREACH l IN libraries %]
36                                     <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option>
37                                 [% END %]
38                             </select>
39                         </li>
40                     [% CASE 'category' %]
41                         <li>
42                             <label for="categorycode_filter">Category:</label>
43                             <select id="categorycode_filter">
44                                 <option value="">Any</option>
45                                 [% FOREACH category IN categories %]
46                                     <option value="[% category.categorycode | html %]">[% category.description | html %]</option>
47                                 [% END %]
48                             </select>
49                         </li>
50                     [% END %]
51                 [% END %]
52             </ol>
53             <fieldset class="action">
54                 <input type="submit" value="Search" />
55             </fieldset>
56         </fieldset>
57     </form>
58 [% END %]
59
60 [% BLOCK patron_search_table %]
61
62     [% IF filter == 'suggestions_managers' %]
63         <div class="hint">Only staff with superlibrarian or suggestions_manage permissions are returned in the search results</div>
64     [% ELSIF filter == 'orders_managers' %]
65         <div class="hint">Only staff with superlibrarian or acquisitions permissions (or order_manage permission if granular permissions are enabled) are returned in the search results</div>
66     [% ELSIF filter == 'funds_owners' OR filter == 'funds_users' %]
67         <div class="hint">Only staff with superlibrarian or acquisitions permissions (or budget_modify permission if granular permissions are enabled) are returned in the search results</div>
68     [% END %]
69
70     <div class="browse">
71         Browse by last name:
72         [% SET alphabet = Koha.Preference('alphabet').split(' ') %]
73         [% UNLESS alphabet.size %]
74             [% alphabet = ['A' .. 'Z'] %]
75         [% END %]
76         [% FOREACH letter IN alphabet %]
77             <a href="#" class="filterByLetter">[% letter | html %]</a>
78         [% END %]
79     </div>
80
81     <div id="[% table_id | html %]_search_results" style="display:none;">
82
83         <div id="info" class="dialog message" style="display: none;"></div>
84         <div id="error" class="dialog alert" style="display: none;"></div>
85
86
87         <input type="hidden" id="firstletter_filter" value="" />
88         [% IF open_on_row_click %]
89         <table id="[% table_id | html %]" class="selections-table">
90         [% ELSE %]
91         <table id="[% table_id | html %]">
92         [% END %]
93             <thead>
94                 <tr>
95                     [% FOR column IN columns %]
96                         [% SWITCH column %]
97                             [% CASE 'cardnumber' %]<th>Card</th>
98                             [% CASE 'dateofbirth' %]<th>Date of birth</th>
99                             [% CASE 'name' %]<th>Name</th>
100                             [% CASE 'name-address' %]<th>Name</th>
101                             [% CASE 'address' %]<th>Address</th>
102                             [% CASE 'address-library' %]<th>Address</th>
103                             [% CASE 'branch' %]<th data-filter="libraries">Library</th>
104                             [% CASE 'category' %]<th data-filter="categories">Category</th>
105                             [% CASE 'dateexpiry' %]<th>Expires on</td>
106                             [% CASE 'borrowernotes' %]<th>Notes</th>
107                             [% CASE 'phone' %]<th>Phone</th>
108                             [% CASE 'checkouts' %]<th>Checkouts</th>
109                             [% CASE 'action' %]<th>&nbsp;</th>
110                         [% END %]
111                     [% END %]
112                 </tr>
113               </thead>
114             <tbody></tbody>
115         </table>
116     </div>
117
118 <!-- Patron preview modal -->
119 <div class="modal" id="patronPreview" tabindex="-1" role="dialog" aria-labelledby="patronPreviewLabel">
120     <div class="modal-dialog" role="document">
121         <div class="modal-content">
122             <div class="modal-header">
123                 <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
124                 <h4 class="modal-title" id="patronPreviewLabel"></h4>
125             </div>
126             <div class="modal-body">
127                 <div id="loading">
128                     <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading
129                 </div>
130             </div>
131             <div class="modal-footer">
132                 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
133             </div>
134         </div>
135     </div>
136 </div>
137
138 [% END %]
139
140 [% BLOCK patron_search_js %]
141
142     [% IF redirect_if_one_result && !redirect_url %]
143         <script>console.log("Wrong call of patron_searh_js - missing redirect_url");</script>
144     [% END %]
145     <script>
146         let categories = [% To.json(categories) | $raw %];
147         let categories_map = categories.reduce((map, c) => {
148             map[c.categorycode] = c;
149             return map;
150         }, {});
151         let libraries  = [% To.json(libraries) | $raw %];
152         let libraries_map = libraries.reduce((map, l) => {
153             map[l.branchcode] = l;
154             return map;
155         }, {});
156
157         [% IF Koha.Preference('ExtendedPatronAttributes') && extended_attribute_types %]
158             let extended_attribute_types = [% To.json(extended_attribute_types || []) | $raw %];
159         [% END %]
160
161     </script>
162
163     [% INCLUDE 'datatables.inc' %]
164     [% INCLUDE 'js-date-format.inc' %]
165     [% INCLUDE 'js-patron-get-age.inc' %]
166     [% INCLUDE 'js-patron-format.inc' %]
167     [% INCLUDE 'js-patron-format-address.inc' %]
168
169     <script>
170         var first_draw = 0;
171         let patrons_table;
172
173         $(document).ready(function(){
174
175
176             $("#info").hide();
177             $("#error").hide();
178
179             let additional_filters = {
180                 surname: function(){
181                     let start_with = $("#firstletter_filter").val()
182                     if (!start_with) return "";
183                     return { "like": start_with + "%" }
184                 },
185                 "-and": function(){
186                     let filter = $("#search_patron_filter").val();
187                     if (!filter) return "";
188                     [% SET search_fields = Koha.Preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid' %]
189                     return [
190                         [% FOR search_field IN search_fields.split(',') %]
191                         {"me.[% search_field | html %]":{"like":"%"+filter+"%"}},
192                         [% END %]
193                         [% IF Koha.Preference('ExtendedPatronAttributes') && extended_attribute_types %]
194                         {
195                             "extended_attributes.value": { "like": "%" + filter + "%" },
196                             "extended_attributes.code": extended_attribute_types
197                         }
198                         [% END %]
199                     ];
200                 }
201             };
202             [% UNLESS default_sort_column %]
203                 [% default_sort_column = "name" %]
204             [% END %]
205             [% SET order_column_index = 0 %]
206             [% SET embed = ['extended_attributes'] %]
207             patrons_table = $("#[% table_id | html %]").kohaTable({
208                 "ajax": {
209                     [% SWITCH filter %]
210                     [% CASE 'suggestions_managers' %]
211                         "url": '/api/v1/suggestions/managers',
212                     [% CASE 'baskets_managers' %]
213                         "url": '/api/v1/acquisitions/baskets/managers',
214                     [% CASE 'funds_owners' %]
215                         "url": '/api/v1/acquisitions/funds/owners',
216                     [% CASE 'funds_users' %]
217                         "url": '/api/v1/acquisitions/funds/users',
218                     [% CASE %]
219                         "url": '/api/v1/patrons',
220                     [% END %]
221                     "dataSrc": function ( json ) {
222                         [% IF redirect_if_one_result %]
223                             // redirect if there is only 1 result.
224                             if ( first_draw && json.recordsFiltered == 1 ) {
225                                 document.location.href = '[% redirect_url | url %]&borrowernumber=' + json.data[0].patron_id;
226                                 return false;
227                             }
228                             first_draw = 0;
229                         [% END %]
230                         return json.data;
231                     }
232                 },
233                 "drawCallback": function( settings ) {
234                     var api = this.api();
235                     var data = api.data();
236                     if ( data.length == 0 ) return;
237
238                     [% IF open_on_row_click %]
239                     $.each($(this).find("tbody tr"), function(index, tr) {
240                         let url = "[% on_click_url | url %]&borrowernumber=" + data[index].patron_id;
241                         $(tr).off('click').on('click', function() {
242                             document.location.href = url;
243                         }).addClass('clickable');
244                         $(tr).find("a.patron_name").attr('href', url);
245                     });
246                     [% ELSE %]
247                     $.each($(this).find("tbody tr"), function(index, tr) {
248                         $(tr).find("a.patron_name").addClass("patron_preview");
249                     });
250                     [% END %]
251                 },
252                 "iDeferLoading": 0,
253                 "columns": [
254                     [% FOR column IN columns %]
255                         [% IF default_sort_column == column %]
256                             [% order_column_index = loop.count - 1%]
257                         [% END %]
258                         [% SWITCH column %]
259                             [% CASE 'checkbox' %]
260                             {
261                                 "data": "borrowernumber",
262                                 "searchable": true,
263                                 "orderable": true,
264                                 "render": function( data, type, row, meta ) {
265                                     return "<input type=\"checkbox\" name="data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : "";
266
267                                     return "<label for='check" + data + "' class='content_hidden'>" + _("Select patron") + "</label><input type='checkbox' id='check" + data + "' class='selection' name='borrowernumber' value='" + data + "' />",
268                                 }
269                             }
270                             [% CASE 'cardnumber' %]
271                             {
272                                 "data": "cardnumber",
273                                 "searchable": true,
274                                 "orderable": true
275                             }
276                             [% CASE 'dateofbirth' %]
277                             {
278                                 "data": "date_of_birth",
279                                 "searchable": true,
280                                 "orderable": true,
281                                 "render": function( data, type, row, meta ) {
282                                     return data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : "";
283                                 }
284                             }
285                             [% CASE 'address' %]
286                             {
287                                 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
288                                 "searchable": true,
289                                 "orderable": true,
290                                  "render": function( data, type, row, meta ) {
291                                     let r = '<div class="address"><ul>';
292                                     r += $format_address(row, { no_line_break: 1 });
293                                     r += '</div></ul>';
294                                     return r;
295                                 }
296                             }
297                             [% CASE 'address-library' %]
298                             {
299                                 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
300                                 "searchable": true,
301                                 "orderable": true,
302                                 "render": function( data, type, row, meta ) {
303                                     let r = '<div class="address"><ul>';
304                                     r += $format_address(row, { no_line_break: 1 });
305                                     r += '</div></ul>';
306                                     r += " " + escape_str(libraries_map[row.library_id].branchname);
307                                     return r;
308                                 }
309                             }
310                             [% CASE 'name-address' %]
311                             {
312                                 "data": "me.firstname:me.surname:me.othernames:me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
313                                 "searchable": true,
314                                 "orderable": true,
315                                 "render": function( data, type, row, meta ) {
316                                     let patron_id = encodeURIComponent(row.patron_id);
317                                     let r = '';
318                                     [% IF ! open_on_row_click %]
319                                     r += "<a href=\"/cgi-bin/koha/members/moremember.pl?borrowernumber=" + patron_id + "\" class=\"patron_name\" data-borrowernumber=\"" + patron_id + "\" style=\"white-space:nowrap\">" + $patron_to_html(row, { invert_name: 1 }) + "</a>";
320                                     [% ELSE %]
321                                     r += $patron_to_html(row, { invert_name: 1 });
322                                     [% END %]
323                                     r += '<br/>';
324                                     r += '<div class="address"><ul>';
325                                     r += $format_address(row, { no_line_break: 1 });
326
327                                     if ( row.email ) {
328                                         r += "<li>" + _("Email: ") + "<a href='mailto:" + encodeURIComponent(row.email) + "'>" + escape_str(row.email) + "</a></li>";
329                                     }
330                                     r += '</ul></div>'
331
332                                     return r;
333                                 }
334                             }
335                             [% CASE 'name' %]
336                             {
337                                 "data": "me.firstname:me.surname:me.othernames",
338                                 "searchable": true,
339                                 "orderable": true,
340                                 "render": function( data, type, row, meta ) {
341                                     let patron_id = encodeURIComponent(row.patron_id);
342                                     return "<a href=\"/cgi-bin/koha/members/moremember.pl?borrowernumber=" + patron_id + "\" class=\"patron_name\" data-borrowernumber=\"" + patron_id + "\" style=\"white-space:nowrap\">" + $patron_to_html(row, { invert_name: 1 }) + "</a>";
343                                 }
344                             }
345                             [% CASE 'branch' %]
346                             {
347                                 "data": "library_id",
348                                 "searchable": true,
349                                 "orderable": true,
350                                 "render": function( data, type, row, meta ) {
351                                     return escape_str(libraries_map[data].branchname);
352                                 }
353                             }
354                             [% CASE 'category' %]
355                             {
356                                 "data": "category_id",
357                                 "searchable": true,
358                                 "orderable": true,
359                                 "render": function( data, type, row, meta ) {
360                                     return escape_str(categories_map[data].description);
361                                 }
362                             }
363                             [% CASE 'dateexpiry' %]
364                             {
365                                 "data": "expiry_date",
366                                 "searchable": true,
367                                 "orderable": true,
368                                 "render": function( data, type, row, meta ) {
369                                     return data ? escape_str($date(data)) : "";
370                                 }
371                             }
372                             [% CASE 'borrowernotes' %]
373                             {
374                                 "data": "staff_notes",
375                                 "searchable": true,
376                                 "orderable": true,
377                                 [%# We don't escape here, we allow html tag in staff notes %]
378                             }
379                             [% CASE 'phone' %]
380                             {
381                                 "data": "phone",
382                                 "searchable": true,
383                                 "orderable": true,
384                                 "render": function( data, type, row, meta ) {
385                                     return escape_str(data);
386                                 }
387                             }
388                             [% CASE 'checkouts' %][% embed.push('checkouts+count') %]
389                             {
390                                 "data": "",
391                                 "searchable": false,
392                                 "orderable": false,
393                                 "render": function( data, type, row, meta ) {
394                                     return escape_str(row.checkouts_count);
395                                 }
396                             }
397                             [% CASE 'action' %]
398                             {
399                                 "data": function( row, type, val, meta ) {
400
401                                     let patron_id = encodeURIComponent(row.patron_id);
402                                     [% IF selection_type == 'select' %]
403                                         return '<a href="#" class="btn btn-default btn-xs select_user" data-borrowernumber="' + patron_id + '">Select</a><input type="hidden" id="borrower_data' + patron_id + '" name="borrower_data'+ patron_id + '" value=\''+JSON.stringify(row)+'\' />';
404                                     [% ELSE %]
405                                         return '<a href="#" class="btn btn-default btn-xs add_user" data-borrowernumber="' + patron_id + '" data-firstname="' + encodeURIComponent(row.firstname) + '" data-surname="' + encodeURIComponent(row.surname) + '">Add</a><input type="hidden" id="borrower_data' + patron_id + '" name="borrower_data'+ patron_id + '" />';
406                                     [% END %]
407                                 },
408                                 "searchable": false,
409                                 "orderable": false
410                             }
411                         [% END %]
412                         [% UNLESS loop.last %],[% END %]
413                     [% END %]
414                 ],
415                 'embed': [% To.json(embed) | $raw %],
416                 "order": [[ [% order_column_index | html %], "asc" ]],
417                 'bAutoWidth': false,
418                 'sPaginationType': 'full_numbers',
419                 "iDisplayLength": [% Koha.Preference('PatronsPerPage') | html %],
420             }, typeof table_settings !== 'undefined' ? table_settings : null, 1, additional_filters);
421
422             $("#patron_search_form").on('submit', filter);
423             $(".filterByLetter").on("click",function(e){
424                 e.preventDefault();
425                 filterByFirstLetterSurname($(this).text());
426             });
427             $("body").on("click",".add_user",function(e){
428                 e.preventDefault();
429                 var borrowernumber = $(this).data("borrowernumber");
430                 var firstname = $(this).data("firstname");
431                 var surname = $(this).data("surname");
432                 add_user( borrowernumber, firstname + " " + surname );
433             });
434
435             $("body").on("click",".select_user",function(e){
436                 e.preventDefault();
437                 var borrowernumber = $(this).data("borrowernumber");
438                 var borrower_data = $("#borrower_data"+borrowernumber).val();
439                 select_user( borrowernumber, JSON.parse(borrower_data) );
440             });
441
442             $("body").on("click",".patron_preview", function( e ){
443                 e.preventDefault();
444                 var borrowernumber = $(this).data("borrowernumber");
445                 var page = "/cgi-bin/koha/members/moremember.pl?print=brief&borrowernumber=" + borrowernumber;
446                 $("#patronPreview .modal-body").load( page + " div.container-fluid" );
447                 $('#patronPreview').modal({show:true});
448             });
449
450             $("#patronPreview").on('hidden.bs.modal', function (e) {
451                 $("#patronPreview .modal-body").html("<img src=\"[% interface | html %]/[% theme | html %]/img/spinner-small.gif\" alt=\"\" /> Loading");
452             });
453
454         });
455
456         function filter() {
457             $("#firstletter_filter").val('');
458             $("#[% table_id | html %]_search_results").show();
459
460             let table_dt = patrons_table.DataTable();
461             [% FOR c IN columns %]
462                 [% SWITCH c %]
463                 [% CASE 'branch' %]
464                     library_id = $("#branchcode_filter").val() || "";
465                     patrons_table.find('thead tr:eq(1) th[data-filter="libraries"] select').val(library_id);
466                     table_dt.column([% loop.count - 1 %]).search(library_id ? '^'+library_id+'$' : '');
467                 [% CASE 'category' %]
468                     let category_id = $("#categorycode_filter").val() || "";
469                     patrons_table.find('thead tr:eq(1) th[data-filter="categories"] select').val(category_id);
470                     table_dt.column([% loop.count - 1 %]).search(category_id ? '^'+category_id+'$' : '');
471                 [% END %]
472             [% END %]
473             table_dt.search("");
474             first_draw = 1; // Only redirect if we are coming from here
475             table_dt.draw();
476             return false;
477         }
478
479         // User has clicked on a letter
480         function filterByFirstLetterSurname(letter) {
481             $("#firstletter_filter").val(letter);
482             $("#[% table_id | html %]_search_results").show();
483             patrons_table.DataTable().draw();
484         }
485
486         // modify parent window owner element
487         [% IF selection_type == 'add' %]
488             function add_user(borrowernumber, borrowername) {
489                 var p = window.opener;
490                 // In one place (serials/routing.tt), the page is reload on every add
491                 // We have to wait for the page to be there
492                 function wait_for_opener () {
493                     if ( ! $(opener.document).find('body').size() ) {
494                         setTimeout(wait_for_opener, 500);
495                     } else {
496                         [%# Note that add_user could sent data instead of borrowername too %]
497                         $("#info").hide();
498                         $("#error").hide();
499                         if ( p.add_user(borrowernumber, borrowername) < 0 ) {
500                             $("#error").html(_("Patron '%s' is already in the list.").format(borrowername));
501                             $("#error").show();
502                         } else {
503                             $("#info").html(_("Patron '%s' added.").format(borrowername));
504                             $("#info").show();
505                         }
506                     }
507                 }
508                 wait_for_opener();
509             }
510         [% ELSIF selection_type == 'select' %]
511             function select_user(borrowernumber, data) {
512                 var p = window.opener;
513                 [%  IF callback %]
514                     p.[% callback | html %](borrowernumber, data);
515                 [%  ELSE %]
516                     p.select_user(borrowernumber, data);
517                 [%  END %]
518                 window.close();
519             }
520         [% END %]
521     </script>
522 [% END %]