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" />
14 [% BLOCK patron_search_filters %]
15 <form id="patron_search_form">
16 <fieldset class="brief">
17 <h3>Search for patron</h3>
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" />
25 [% FOR column IN columns %]
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>
35 [% FOREACH l IN libraries %]
36 <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option>
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>
53 <fieldset class="action">
54 <input type="submit" value="Search" />
60 [% BLOCK patron_search_table %]
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>
72 [% SET alphabet = Koha.Preference('alphabet').split(' ') %]
73 [% UNLESS alphabet.size %]
74 [% alphabet = ['A' .. 'Z'] %]
76 [% FOREACH letter IN alphabet %]
77 <a href="#" class="filterByLetter">[% letter | html %]</a>
81 <div id="[% table_id | html %]_search_results" style="display:none;">
83 <div id="info" class="dialog message" style="display: none;"></div>
84 <div id="error" class="dialog alert" style="display: none;"></div>
87 <input type="hidden" id="firstletter_filter" value="" />
88 [% IF open_on_row_click %]
89 <table id="[% table_id | html %]" class="selections-table">
91 <table id="[% table_id | html %]">
95 [% FOR column IN columns %]
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 'action' %]<th> </th>
117 <!-- Patron preview modal -->
118 <div class="modal" id="patronPreview" tabindex="-1" role="dialog" aria-labelledby="patronPreviewLabel">
119 <div class="modal-dialog" role="document">
120 <div class="modal-content">
121 <div class="modal-header">
122 <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
123 <h4 class="modal-title" id="patronPreviewLabel"></h4>
125 <div class="modal-body">
127 <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading
130 <div class="modal-footer">
131 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
139 [% BLOCK patron_search_js %]
141 [% IF redirect_if_one_result && !redirect_url %]
142 <script>console.log("Wrong call of patron_searh_js - missing redirect_url");</script>
145 let categories = [% To.json(categories) | $raw %];
146 let categories_map = categories.reduce((map, c) => {
147 map[c.categorycode] = c;
150 let libraries = [% To.json(libraries) | $raw %];
151 let libraries_map = libraries.reduce((map, l) => {
152 map[l.branchcode] = l;
156 [% IF Koha.Preference('ExtendedPatronAttributes') %]
157 let extended_attribute_types = [% To.json(attribute_type_codes || []) | $raw %];
162 [% INCLUDE 'datatables.inc' %]
163 [% INCLUDE 'js-date-format.inc' %]
164 [% INCLUDE 'js-patron-get-age.inc' %]
165 [% INCLUDE 'js-patron-format.inc' %]
166 [% INCLUDE 'js-patron-format-address.inc' %]
172 $(document).ready(function(){
178 let additional_filters = {
180 let start_with = $("#firstletter_filter").val()
181 if (!start_with) return "";
182 return { "like": start_with + "%" }
185 let filter = $("#search_patron_filter").val();
186 if (!filter) return "";
187 [% SET search_fields = Koha.Preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid' %]
189 [% FOR search_field IN search_fields.split(',') %]
190 {"me.[% search_field | html %]":{"like":"%"+filter+"%"}},
192 [% IF Koha.Preference('ExtendedPatronAttributes') %]
194 "extended_attributes.value": { "like": "%" + filter + "%" },
195 "extended_attributes.code": extended_attribute_types
201 [% UNLESS default_sort_column %]
202 [% default_sort_column = "name" %]
204 [% SET order_column_index = 0 %]
205 patrons_table = $("#[% table_id | html %]").kohaTable({
208 [% CASE 'suggestions_managers' %]
209 "url": '/api/v1/suggestions/managers',
210 [% CASE 'baskets_managers' %]
211 "url": '/api/v1/acquisitions/baskets/managers',
212 [% CASE 'funds_owners' %]
213 "url": '/api/v1/acquisitions/funds/owners',
214 [% CASE 'funds_users' %]
215 "url": '/api/v1/acquisitions/funds/users',
217 "url": '/api/v1/patrons',
219 "dataSrc": function ( json ) {
220 [% IF redirect_if_one_result %]
221 // redirect if there is only 1 result.
222 if ( first_draw && json.recordsFiltered == 1 ) {
223 document.location.href = '[% redirect_url | url %]&borrowernumber=' + json.data[0].patron_id;
231 embed: ['extended_attributes'],
232 "drawCallback": function( settings ) {
233 var api = this.api();
234 var data = api.data();
235 if ( data.length == 0 ) return;
237 [% IF open_on_row_click %]
238 $.each($(this).find("tbody tr"), function(index, tr) {
239 let url = "[% on_click_url | url %]&borrowernumber=" + data[index].patron_id;
240 $(tr).off('click').on('click', function() {
241 document.location.href = url;
242 }).addClass('clickable');
243 $(tr).find("a.patron_name").attr('href', url);
246 $.each($(this).find("tbody tr"), function(index, tr) {
247 $(tr).find("a.patron_name").addClass("patron_preview");
253 [% FOR column IN columns %]
254 [% IF default_sort_column == column %]
255 [% order_column_index = loop.count - 1%]
258 [% CASE 'cardnumber' %]
260 "data": "cardnumber",
264 [% CASE 'dateofbirth' %]
266 "data": "date_of_birth",
269 "render": function( data, type, row, meta ) {
270 return data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : "";
275 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
278 "render": function( data, type, row, meta ) {
279 let r = '<div class="address"><ul>';
280 r += $format_address(row, { no_line_break: 1 });
285 [% CASE 'address-library' %]
287 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
290 "render": function( data, type, row, meta ) {
291 let r = '<div class="address"><ul>';
292 r += $format_address(row, { no_line_break: 1 });
294 r += " " + escape_str(libraries_map[row.library_id].branchname);
298 [% CASE 'name-address' %]
300 "data": "me.firstname:me.surname:me.othernames:me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
303 "render": function( data, type, row, meta ) {
304 let patron_id = encodeURIComponent(row.patron_id);
306 [% IF ! open_on_row_click %]
307 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>";
309 r += $patron_to_html(row, { invert_name: 1 });
312 r += '<div class="address"><ul>';
313 r += $format_address(row, { no_line_break: 1 });
316 r += "<li>" + _("Email: ") + "<a href='mailto:" + encodeURIComponent(row.email) + "'>" + escape_str(row.email) + "</a></li>";
325 "data": "me.firstname:me.surname:me.othernames",
328 "render": function( data, type, row, meta ) {
329 let patron_id = encodeURIComponent(row.patron_id);
330 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>";
335 "data": "library_id",
338 "render": function( data, type, row, meta ) {
339 return escape_str(libraries_map[data].branchname);
342 [% CASE 'category' %]
344 "data": "category_id",
347 "render": function( data, type, row, meta ) {
348 return escape_str(categories_map[data].description);
351 [% CASE 'dateexpiry' %]
353 "data": "expiry_date",
356 "render": function( data, type, row, meta ) {
357 return data ? escape_str($date(data)) : "";
360 [% CASE 'borrowernotes' %]
362 "data": "staff_notes",
365 [%# We don't escape here, we allow html tag in staff notes %]
372 "render": function( data, type, row, meta ) {
373 return escape_str(data);
378 "data": function( row, type, val, meta ) {
380 let patron_id = encodeURIComponent(row.patron_id);
381 [% IF selection_type == 'select' %]
382 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)+'\' />';
384 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 + '" />';
391 [% UNLESS loop.last %],[% END %]
394 "order": [[ [% order_column_index | html %], "asc" ]],
396 'sPaginationType': 'full_numbers',
397 "iDisplayLength": [% Koha.Preference('PatronsPerPage') | html %],
398 }, typeof table_settings !== 'undefined' ? table_settings : null, 1, additional_filters);
400 $("#patron_search_form").on('submit', filter);
401 $(".filterByLetter").on("click",function(e){
403 filterByFirstLetterSurname($(this).text());
405 $("body").on("click",".add_user",function(e){
407 var borrowernumber = $(this).data("borrowernumber");
408 var firstname = $(this).data("firstname");
409 var surname = $(this).data("surname");
410 add_user( borrowernumber, firstname + " " + surname );
413 $("body").on("click",".select_user",function(e){
415 var borrowernumber = $(this).data("borrowernumber");
416 var borrower_data = $("#borrower_data"+borrowernumber).val();
417 select_user( borrowernumber, JSON.parse(borrower_data) );
420 $("body").on("click",".patron_preview", function( e ){
422 var borrowernumber = $(this).data("borrowernumber");
423 var page = "/cgi-bin/koha/members/moremember.pl?print=brief&borrowernumber=" + borrowernumber;
424 $("#patronPreview .modal-body").load( page + " div.container-fluid" );
425 $('#patronPreview').modal({show:true});
428 $("#patronPreview").on('hidden.bs.modal', function (e) {
429 $("#patronPreview .modal-body").html("<img src=\"[% interface | html %]/[% theme | html %]/img/spinner-small.gif\" alt=\"\" /> Loading");
435 $("#firstletter_filter").val('');
436 $("#[% table_id | html %]_search_results").show();
438 let table_dt = patrons_table.DataTable();
439 [% FOR c IN columns %]
442 library_id = $("#branchcode_filter").val() || "";
443 patrons_table.find('thead tr:eq(1) th[data-filter="libraries"] select').val(library_id);
444 table_dt.column([% loop.count - 1 %]).search(library_id ? '^'+library_id+'$' : '');
445 [% CASE 'category' %]
446 let category_id = $("#categorycode_filter").val() || "";
447 patrons_table.find('thead tr:eq(1) th[data-filter="categories"] select').val(category_id);
448 table_dt.column([% loop.count - 1 %]).search(category_id ? '^'+category_id+'$' : '');
452 first_draw = 1; // Only redirect if we are coming from here
457 // User has clicked on a letter
458 function filterByFirstLetterSurname(letter) {
459 $("#firstletter_filter").val(letter);
460 $("#[% table_id | html %]_search_results").show();
461 patrons_table.DataTable().draw();
464 // modify parent window owner element
465 [% IF selection_type == 'add' %]
466 function add_user(borrowernumber, borrowername) {
467 var p = window.opener;
468 // In one place (serials/routing.tt), the page is reload on every add
469 // We have to wait for the page to be there
470 function wait_for_opener () {
471 if ( ! $(opener.document).find('body').size() ) {
472 setTimeout(wait_for_opener, 500);
474 [%# Note that add_user could sent data instead of borrowername too %]
477 if ( p.add_user(borrowernumber, borrowername) < 0 ) {
478 $("#error").html(_("Patron '%s' is already in the list.").format(borrowername));
481 $("#info").html(_("Patron '%s' added.").format(borrowername));
488 [% ELSIF selection_type == 'select' %]
489 function select_user(borrowernumber, data) {
490 var p = window.opener;
492 p.[% callback | html %](borrowernumber, data);
494 p.select_user(borrowernumber, data);