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 'checkouts' %]<th>Checkouts</th>
109 [% CASE 'action' %]<th> </th>
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">×</span></button>
124 <h4 class="modal-title" id="patronPreviewLabel"></h4>
126 <div class="modal-body">
128 <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading
131 <div class="modal-footer">
132 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
140 [% BLOCK patron_search_js %]
142 [% IF redirect_if_one_result && !redirect_url %]
143 <script>console.log("Wrong call of patron_searh_js - missing redirect_url");</script>
146 let categories = [% To.json(categories) | $raw %];
147 let categories_map = categories.reduce((map, c) => {
148 map[c.categorycode] = c;
151 let libraries = [% To.json(libraries) | $raw %];
152 let libraries_map = libraries.reduce((map, l) => {
153 map[l.branchcode] = l;
157 [% IF Koha.Preference('ExtendedPatronAttributes') && extended_attribute_types %]
158 let extended_attribute_types = [% To.json(extended_attribute_types || []) | $raw %];
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' %]
173 $(document).ready(function(){
179 let additional_filters = {
181 let start_with = $("#firstletter_filter").val()
182 if (!start_with) return "";
183 return { "like": start_with + "%" }
186 let filter = $("#search_patron_filter").val();
187 if (!filter) return "";
188 [% SET search_fields = Koha.Preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid' %]
190 [% FOR search_field IN search_fields.split(',') %]
191 {"me.[% search_field | html %]":{"like":"%"+filter+"%"}},
193 [% IF Koha.Preference('ExtendedPatronAttributes') && extended_attribute_types %]
195 "extended_attributes.value": { "like": "%" + filter + "%" },
196 "extended_attributes.code": extended_attribute_types
202 [% UNLESS default_sort_column %]
203 [% default_sort_column = "name" %]
205 [% SET order_column_index = 0 %]
206 [% SET embed = ['extended_attributes'] %]
207 patrons_table = $("#[% table_id | html %]").kohaTable({
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',
219 "url": '/api/v1/patrons',
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;
233 "drawCallback": function( settings ) {
234 var api = this.api();
235 var data = api.data();
236 if ( data.length == 0 ) return;
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);
247 $.each($(this).find("tbody tr"), function(index, tr) {
248 $(tr).find("a.patron_name").addClass("patron_preview");
254 [% FOR column IN columns %]
255 [% IF default_sort_column == column %]
256 [% order_column_index = loop.count - 1%]
259 [% CASE 'checkbox' %]
261 "data": "borrowernumber",
264 "render": function( data, type, row, meta ) {
265 return "<input type=\"checkbox\" name="data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : "";
267 return "<label for='check" + data + "' class='content_hidden'>" + _("Select patron") + "</label><input type='checkbox' id='check" + data + "' class='selection' name='borrowernumber' value='" + data + "' />",
270 [% CASE 'cardnumber' %]
272 "data": "cardnumber",
276 [% CASE 'dateofbirth' %]
278 "data": "date_of_birth",
281 "render": function( data, type, row, meta ) {
282 return data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : "";
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 });
297 [% CASE 'address-library' %]
299 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
302 "render": function( data, type, row, meta ) {
303 let r = '<div class="address"><ul>';
304 r += $format_address(row, { no_line_break: 1 });
306 r += " " + escape_str(libraries_map[row.library_id].branchname);
310 [% CASE 'name-address' %]
312 "data": "me.firstname:me.surname:me.othernames:me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
315 "render": function( data, type, row, meta ) {
316 let patron_id = encodeURIComponent(row.patron_id);
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>";
321 r += $patron_to_html(row, { invert_name: 1 });
324 r += '<div class="address"><ul>';
325 r += $format_address(row, { no_line_break: 1 });
328 r += "<li>" + _("Email: ") + "<a href='mailto:" + encodeURIComponent(row.email) + "'>" + escape_str(row.email) + "</a></li>";
337 "data": "me.firstname:me.surname:me.othernames",
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>";
347 "data": "library_id",
350 "render": function( data, type, row, meta ) {
351 return escape_str(libraries_map[data].branchname);
354 [% CASE 'category' %]
356 "data": "category_id",
359 "render": function( data, type, row, meta ) {
360 return escape_str(categories_map[data].description);
363 [% CASE 'dateexpiry' %]
365 "data": "expiry_date",
368 "render": function( data, type, row, meta ) {
369 return data ? escape_str($date(data)) : "";
372 [% CASE 'borrowernotes' %]
374 "data": "staff_notes",
377 [%# We don't escape here, we allow html tag in staff notes %]
384 "render": function( data, type, row, meta ) {
385 return escape_str(data);
388 [% CASE 'checkouts' %][% embed.push('checkouts+count') %]
393 "render": function( data, type, row, meta ) {
394 return escape_str(row.checkouts_count);
399 "data": function( row, type, val, meta ) {
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)+'\' />';
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 + '" />';
412 [% UNLESS loop.last %],[% END %]
415 'embed': [% To.json(embed) | $raw %],
416 "order": [[ [% order_column_index | html %], "asc" ]],
418 'sPaginationType': 'full_numbers',
419 "iDisplayLength": [% Koha.Preference('PatronsPerPage') | html %],
420 }, typeof table_settings !== 'undefined' ? table_settings : null, 1, additional_filters);
422 $("#patron_search_form").on('submit', filter);
423 $(".filterByLetter").on("click",function(e){
425 filterByFirstLetterSurname($(this).text());
427 $("body").on("click",".add_user",function(e){
429 var borrowernumber = $(this).data("borrowernumber");
430 var firstname = $(this).data("firstname");
431 var surname = $(this).data("surname");
432 add_user( borrowernumber, firstname + " " + surname );
435 $("body").on("click",".select_user",function(e){
437 var borrowernumber = $(this).data("borrowernumber");
438 var borrower_data = $("#borrower_data"+borrowernumber).val();
439 select_user( borrowernumber, JSON.parse(borrower_data) );
442 $("body").on("click",".patron_preview", function( e ){
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});
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");
457 $("#firstletter_filter").val('');
458 $("#[% table_id | html %]_search_results").show();
460 let table_dt = patrons_table.DataTable();
461 [% FOR c IN columns %]
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+'$' : '');
474 first_draw = 1; // Only redirect if we are coming from here
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();
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);
496 [%# Note that add_user could sent data instead of borrowername too %]
499 if ( p.add_user(borrowernumber, borrowername) < 0 ) {
500 $("#error").html(_("Patron '%s' is already in the list.").format(borrowername));
503 $("#info").html(_("Patron '%s' added.").format(borrowername));
510 [% ELSIF selection_type == 'select' %]
511 function select_user(borrowernumber, data) {
512 var p = window.opener;
514 p.[% callback | html %](borrowernumber, data);
516 p.select_user(borrowernumber, data);