Bug 23493: (follow-up) Switch to aDataSort for multi-column sorting
[koha.git] / koha-tmpl / intranet-tmpl / prog / js / checkouts.js
1 /* global PATRON_NOTE */
2
3 $(document).ready(function() {
4     $.ajaxSetup ({ cache: false });
5
6     var barcodefield = $("#barcode");
7
8     var onHoldDueDateSet = false;
9
10     var onHoldChecked = function() {
11         var isChecked = false;
12         $('input[data-on-reserve]').each(function() {
13             if ($(this).is(':checked')) {
14                 isChecked=true;
15             }
16         });
17         return isChecked;
18     };
19
20     var showHideOnHoldRenewal = function() {
21         // Display the date input
22         if (onHoldChecked()) {
23             $('#newonholdduedate').show()
24         } else {
25             $('#newonholdduedate').hide();
26         }
27     };
28
29     // Handle the select all/none links for checkouts table columns
30     $("#CheckAllRenewals").on("click",function(){
31         $("#UncheckAllCheckins").click();
32         $(".renew:visible").prop("checked", true);
33         showHideOnHoldRenewal();
34         return false;
35     });
36     $("#UncheckAllRenewals").on("click",function(){
37         $(".renew:visible").prop("checked", false);
38         showHideOnHoldRenewal();
39         return false;
40     });
41
42     $("#CheckAllCheckins").on("click",function(){
43         $("#UncheckAllRenewals").click();
44         $(".checkin:visible").prop("checked", true);
45         return false;
46     });
47     $("#UncheckAllCheckins").on("click",function(){
48         $(".checkin:visible").prop("checked", false);
49         return false;
50     });
51
52     $("#newduedate").on("change", function() {
53         if (!onHoldDueDateSet) {
54             $('#newonholdduedate input').val($('#newduedate').val());
55         }
56     });
57
58     $("#newonholdduedate").on("change", function() {
59         onHoldDueDateSet = true;
60     });
61
62     // Don't allow both return and renew checkboxes to be checked
63     $(document).on("change", '.renew', function(){
64         if ( $(this).is(":checked") ) {
65             $( "#checkin_" + $(this).val() ).prop("checked", false);
66         }
67     });
68     $(document).on("change", '.checkin', function(){
69         if ( $(this).is(":checked") ) {
70             $( "#renew_" + $(this).val() ).prop("checked", false);
71         }
72     });
73
74     // Display on hold due dates input when an on hold item is
75     // selected
76     $(document).on('change', '.renew', function(){
77         showHideOnHoldRenewal();
78     });
79
80     $("#output_format > option:first-child").attr("selected", "selected");
81     $("select[name='csv_profile_id']").hide();
82     $(document).on("change", '#issues-table-output-format', function(){
83         if ( $(this).val() == 'csv' ) {
84             $("select[name='csv_profile_id']").show();
85         } else {
86             $("select[name='csv_profile_id']").hide();
87         }
88     });
89
90     // Clicking the table cell checks the checkbox inside it
91     $(document).on("click", 'td', function(e){
92         if(e.target.tagName.toLowerCase() == 'td'){
93           $(this).find("input:checkbox:visible").each( function() {
94             $(this).click();
95           });
96         }
97     });
98
99     // Handle renewals and returns
100     $("#RenewCheckinChecked").on("click",function(){
101         $(".checkin:checked:visible").each(function() {
102             itemnumber = $(this).val();
103
104             $(this).replaceWith("<img id='checkin_" + itemnumber + "' src='" + interface + "/" + theme + "/img/spinner-small.gif' />");
105
106             params = {
107                 itemnumber:     itemnumber,
108                 borrowernumber: borrowernumber,
109                 branchcode:     branchcode,
110                 exempt_fine:    $("#exemptfine").is(':checked')
111             };
112
113             $.post( "/cgi-bin/koha/svc/checkin", params, function( data ) {
114                 id = "#checkin_" + data.itemnumber;
115
116                 content = "";
117                 if ( data.returned ) {
118                     content = CIRCULATION_RETURNED;
119                     $(id).parent().parent().addClass('ok');
120                     $('#date_due_' + data.itemnumber).html(CIRCULATION_RETURNED);
121                     if ( data.patronnote != null ) {
122                         $('.patron_note_' + data.itemnumber).html( PATRON_NOTE + ": " + data.patronnote);
123                     }
124                 } else {
125                     content = CIRCULATION_NOT_RETURNED;
126                     $(id).parent().parent().addClass('warn');
127                 }
128
129                 $(id).replaceWith( content );
130             }, "json")
131         });
132
133         $(".renew:checked:visible").each(function() {
134             var override_limit = $("#override_limit").is(':checked') ? 1 : 0;
135
136             var isOnReserve = $(this).data().hasOwnProperty('onReserve');
137
138             var itemnumber = $(this).val();
139
140             $(this).parent().parent().replaceWith("<img id='renew_" + itemnumber + "' src='" + interface + "/" + theme + "/img/spinner-small.gif' />");
141
142             var params = {
143                 itemnumber:      itemnumber,
144                 borrowernumber:  borrowernumber,
145                 branchcode:      branchcode,
146                 override_limit:  override_limit,
147             };
148
149             // Determine which due date we need to use
150             var dueDate = isOnReserve ?
151                 $("#newonholdduedate input").val() :
152                 $("#newduedate").val();
153
154             if (dueDate && dueDate.length > 0) {
155                 params.date_due = dueDate
156             }
157
158             $.post( "/cgi-bin/koha/svc/renew", params, function( data ) {
159                 var id = "#renew_" + data.itemnumber;
160
161                 var content = "";
162                 if ( data.renew_okay ) {
163                     content = CIRCULATION_RENEWED_DUE + " " + data.date_due;
164                     $('#date_due_' + data.itemnumber).replaceWith( data.date_due );
165                 } else {
166                     content = CIRCULATION_RENEW_FAILED + " ";
167                     if ( data.error == "no_checkout" ) {
168                         content += NOT_CHECKED_OUT;
169                     } else if ( data.error == "too_many" ) {
170                         content += TOO_MANY_RENEWALS;
171                     } else if ( data.error == "on_reserve" ) {
172                         content += ON_RESERVE;
173                     } else if ( data.error == "restriction" ) {
174                         content += NOT_RENEWABLE_RESTRICTION;
175                     } else if ( data.error == "overdue" ) {
176                         content += NOT_RENEWABLE_OVERDUE;
177                     } else if ( data.error ) {
178                         content += data.error;
179                     } else {
180                         content += REASON_UNKNOWN;
181                     }
182                 }
183
184                 $(id).replaceWith( content );
185             }, "json")
186         });
187
188         // Refocus on barcode field if it exists
189         if ( $("#barcode").length ) {
190             $("#barcode").focus();
191         }
192
193         // Prevent form submit
194         return false;
195     });
196
197     $("#RenewAll").on("click",function(){
198         $("#CheckAllRenewals").click();
199         $("#UncheckAllCheckins").click();
200         showHideOnHoldRenewal();
201         $("#RenewCheckinChecked").click();
202
203         // Prevent form submit
204         return false;
205     });
206
207     var ymd = $.datepicker.formatDate('yy-mm-dd', new Date());
208
209     $('#issues-table').hide();
210     $('#issues-table-actions').hide();
211     $('#issues-table-load-immediately').change(function(){
212         if ( this.checked && typeof issuesTable === 'undefined') {
213             $('#issues-table-load-now-button').click();
214         }
215         barcodefield.focus();
216     });
217     $('#issues-table-load-now-button').click(function(){
218         LoadIssuesTable();
219         barcodefield.focus();
220         return false;
221     });
222
223     if ( Cookies.get("issues-table-load-immediately-" + script) == "true" ) {
224         LoadIssuesTable();
225         $('#issues-table-load-immediately').prop('checked', true);
226     }
227     $('#issues-table-load-immediately').on( "change", function(){
228         Cookies.set("issues-table-load-immediately-" + script, $(this).is(':checked'), { expires: 365 });
229     });
230
231     function LoadIssuesTable() {
232         $('#issues-table-loading-message').hide();
233         $('#issues-table').show();
234         $('#issues-table-actions').show();
235
236         issuesTable = KohaTable("issues-table", {
237             "oLanguage": {
238                 "sEmptyTable" : MSG_DT_LOADING_RECORDS,
239                 "sProcessing": MSG_DT_LOADING_RECORDS,
240             },
241             "bAutoWidth": false,
242             "dom": 'B<"clearfix">rt',
243             "aoColumns": [
244                 {
245                     "mDataProp": function( oObj ) {
246                         return oObj.sort_order;
247                     }
248                 },
249                 {
250                     "mDataProp": function( oObj ) {
251                         if ( oObj.issued_today ) {
252                             return "<strong>" + TODAYS_CHECKOUTS + "</strong>";
253                         } else {
254                             return "<strong>" + PREVIOUS_CHECKOUTS + "</strong>";
255                         }
256                     }
257                 },
258                 {
259                     "mDataProp": "date_due",
260                     "bVisible": false,
261                 },
262                 {
263                     "aDataSort": [1,2], // Sort on hidden unformatted date due column
264                     "mDataProp": function( oObj ) {
265                         var due = oObj.date_due_formatted;
266
267                         if ( oObj.date_due_overdue ) {
268                             due = "<span class='overdue'>" + due + "</span>";
269                         }
270
271                         due = "<span id='date_due_" + oObj.itemnumber + "' class='date_due'>" + due + "</span>";
272
273                         if ( oObj.lost && oObj.claims_returned ) {
274                             due += "<span class='lost claims_returned'>" + oObj.lost.escapeHtml() + "</span>";
275                         } else if ( oObj.lost ) {
276                             due += "<span class='lost'>" + oObj.lost.escapeHtml() + "</span>";
277                         }
278
279                         if ( oObj.damaged ) {
280                             due += "<span class='dmg'>" + oObj.damaged.escapeHtml() + "</span>";
281                         }
282
283                         var patron_note = " <span class='patron_note_" + oObj.itemnumber + "'></span>";
284                         due +="<br>" + patron_note;
285
286                         return due;
287                     }
288                 },
289                 {
290                     "mDataProp": function ( oObj ) {
291                         title = "<span id='title_" + oObj.itemnumber + "' class='strong'><a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber="
292                               + oObj.biblionumber
293                               + "'>"
294                               + oObj.title.escapeHtml();
295
296                         $.each(oObj.subtitle, function( index, value ) {
297                                   title += " " + value.escapeHtml();
298                         });
299
300                         title += " " + oObj.part_number + " " + oObj.part_name;
301
302                         if ( oObj.enumchron ) {
303                             title += " (" + oObj.enumchron.escapeHtml() + ")";
304                         }
305
306                         title += "</a></span>";
307
308                         if ( oObj.author ) {
309                             title += " " + BY.replace( "_AUTHOR_",  " " + oObj.author.escapeHtml() );
310                         }
311
312                         if ( oObj.itemnotes ) {
313                             var span_class = "text-muted";
314                             if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) {
315                                 span_class = "circ-hlt";
316                             }
317                             title += " - <span class='" + span_class + " item-note-public'>" + oObj.itemnotes.escapeHtml() + "</span>";
318                         }
319
320                         if ( oObj.itemnotes_nonpublic ) {
321                             var span_class = "text-danger";
322                             if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) {
323                                 span_class = "circ-hlt";
324                             }
325                             title += " - <span class='" + span_class + " item-note-nonpublic'>" + oObj.itemnotes_nonpublic.escapeHtml() + "</span>";
326                         }
327
328                         var onsite_checkout = '';
329                         if ( oObj.onsite_checkout == 1 ) {
330                             onsite_checkout += " <span class='onsite_checkout'>(" + INHOUSE_USE + ")</span>";
331                         }
332
333                         title += " "
334                               + "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber="
335                               + oObj.biblionumber
336                               + "&itemnumber="
337                               + oObj.itemnumber
338                               + "#"
339                               + oObj.itemnumber
340                               + "'>"
341                               + (oObj.barcode ? oObj.barcode.escapeHtml() : "")
342                               + "</a>"
343                               + onsite_checkout
344
345                         return title;
346                     },
347                     "sType": "anti-the",
348                     "aDataSort": [1, 3]
349                 },
350                 {
351                     "mDataProp": function ( oObj ) {
352                         return oObj.recordtype_description.escapeHtml();
353                     },
354                     "aDataSort": [1, 4]
355                 },
356                 {
357                     "mDataProp": function ( oObj ) {
358                         return oObj.itemtype_description.escapeHtml();
359                     },
360                     "aDataSort": [1, 5]
361                 },
362                 {
363                     "mDataProp": function ( oObj ) {
364                         return ( oObj.collection ? oObj.collection.escapeHtml() : '' );
365                     },
366                     "aDataSort": [1,6]
367                 },
368                 {
369                     "mDataProp": function ( oObj ) {
370                         return ( oObj.location ? oObj.location.escapeHtml() : '' );
371                     },
372                     "aDataSort": [1,7]
373                 },
374                 {
375                     "mDataProp": function ( oObj ) {
376                         return oObj.homebranch.escapeHtml();
377                     },
378                     "aDataSort": [1,8]
379                 },
380                 {
381                     "mDataProp": "issuedate",
382                     "bVisible": false,
383                 },
384                 {
385                     "aDataSort": [1,9], // Sort on hidden unformatted issuedate column
386                     "mDataProp": "issuedate_formatted",
387                 },
388                 {
389                     "mDataProp": function ( oObj ) {
390                         return oObj.branchname.escapeHtml();
391                     },
392                     "aDataSort": [1,11]
393                 },
394                 {
395                     "mDataProp": function ( oObj ) {
396                         return ( oObj.itemcallnumber ? oObj.itemcallnumber.escapeHtml() : '' );
397                     },
398                     "aDataSort": [1,12]
399                 },
400                 {
401                     "mDataProp": function ( oObj ) {
402                         if ( ! oObj.charge ) oObj.charge = 0;
403                         return '<span style="text-align: right; display: block;">' + parseFloat(oObj.charge).toFixed(2) + '<span>';
404                     },
405                     "aDataSort": [1,13]
406                 },
407                 {
408                     "mDataProp": function ( oObj ) {
409                         if ( ! oObj.fine ) oObj.fine = 0;
410                         return '<span style="text-align: right; display: block;">' + parseFloat(oObj.fine).toFixed(2)  + '<span>';
411                     },
412                     "aDataSort": [1,14]
413                 },
414                 {
415                     "mDataProp": function ( oObj ) {
416                         if ( ! oObj.price ) oObj.price = 0;
417                         return '<span style="text-align: right; display: block;">' + parseFloat(oObj.price).toFixed(2) + '<span>';
418                     },
419                     "aDataSort": [1,15]
420                 },
421                 {
422                     "bSortable": false,
423                     "bVisible": AllowCirculate ? true : false,
424                     "mDataProp": function ( oObj ) {
425                         var content = "";
426                         var msg = "";
427                         var span_style = "";
428                         var span_class = "";
429
430                         if ( oObj.can_renew ) {
431                             // Do nothing
432                         } else if ( oObj.can_renew_error == "on_reserve" ) {
433                             msg += "<span>"
434                                     + "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" + oObj.biblionumber + "'>" + ON_HOLD + "</a>"
435                                     + "</span>";
436
437                             span_style = "display: none";
438                             span_class = "renewals-allowed-on_reserve";
439                         } else if ( oObj.can_renew_error == "too_many" ) {
440                             msg += "<span class='renewals-disabled'>"
441                                     + NOT_RENEWABLE
442                                     + "</span>";
443
444                             span_style = "display: none";
445                             span_class = "renewals-allowed";
446                         } else if ( oObj.can_renew_error == "restriction" ) {
447                             msg += "<span class='renewals-disabled'>"
448                                     + NOT_RENEWABLE_RESTRICTION
449                                     + "</span>";
450
451                             span_style = "display: none";
452                             span_class = "renewals-allowed";
453                         } else if ( oObj.can_renew_error == "overdue" ) {
454                             msg += "<span class='renewals-disabled'>"
455                                     + NOT_RENEWABLE_OVERDUE
456                                     + "</span>";
457
458                             span_style = "display: none";
459                             span_class = "renewals-allowed";
460                         } else if ( oObj.can_renew_error == "too_soon" ) {
461                             msg += "<span class='renewals-disabled'>"
462                                     + NOT_RENEWABLE_TOO_SOON.format( oObj.can_renew_date )
463                                     + "</span>";
464
465                             span_style = "display: none";
466                             span_class = "renewals-allowed";
467                         } else if ( oObj.can_renew_error == "auto_too_soon" ) {
468                             msg += "<span class='renewals-disabled'>"
469                                     + NOT_RENEWABLE_AUTO_TOO_SOON
470                                     + "</span>";
471
472                             span_style = "display: none";
473                             span_class = "renewals-allowed";
474                         } else if ( oObj.can_renew_error == "auto_too_late" ) {
475                             msg += "<span class='renewals-disabled'>"
476                                     + NOT_RENEWABLE_AUTO_TOO_LATE
477                                     + "</span>";
478
479                             span_style = "display: none";
480                             span_class = "renewals-allowed";
481                         } else if ( oObj.can_renew_error == "auto_too_much_oweing" ) {
482                             msg += "<span class='renewals-disabled'>"
483                                     + NOT_RENEWABLE_AUTO_TOO_MUCH_OWEING
484                                     + "</span>";
485
486                             span_style = "display: none";
487                             span_class = "renewals-allowed";
488                         } else if ( oObj.can_renew_error == "auto_account_expired" ) {
489                             msg += "<span class='renewals-disabled'>"
490                                     + NOT_RENEWABLE_AUTO_ACCOUNT_EXPIRED
491                                     + "</span>";
492
493                             span_style = "display: none";
494                             span_class = "renewals-allowed";
495                         } else if ( oObj.can_renew_error == "auto_renew" ) {
496                             msg += "<span class='renewals-disabled'>"
497                                     + NOT_RENEWABLE_AUTO_RENEW
498                                     + "</span>";
499
500                             span_style = "display: none";
501                             span_class = "renewals-allowed";
502                         } else if ( oObj.can_renew_error == "onsite_checkout" ) {
503                             // Don't display something if it's an onsite checkout
504                         } else if ( oObj.can_renew_error == "item_denied_renewal" ) {
505                             content += "<span class='renewals-disabled'>"
506                                     + NOT_RENEWABLE_DENIED
507                                     + "</span>";
508
509                             span_style = "display: none";
510                             span_class = "renewals-allowed";
511                         } else {
512                             msg += "<span class='renewals-disabled'>"
513                                     + oObj.can_renew_error
514                                     + "</span>";
515
516                             span_style = "display: none";
517                             span_class = "renewals-allowed";
518                         }
519
520                         var can_force_renew = ( oObj.onsite_checkout == 0 ) &&
521                             ( oObj.can_renew_error != "on_reserve" || (oObj.can_renew_error == "on_reserve" && AllowRenewalOnHoldOverride))
522                             ? true : false;
523                         var can_renew = ( oObj.renewals_remaining > 0  && !oObj.can_renew_error );
524                         content += "<span>";
525                         if ( can_renew || can_force_renew ) {
526                             content += "<span style='padding: 0 1em;'>" + oObj.renewals_count + "</span>";
527                             content += "<span class='" + span_class + "' style='" + span_style + "'>"
528                                     +  "<input type='checkbox' ";
529                             if ( oObj.date_due_overdue && can_renew ) {
530                                 content += "checked='checked' ";
531                             }
532                             if (oObj.can_renew_error == "on_reserve") {
533                                 content += "data-on-reserve ";
534                             }
535                             content += "class='renew' id='renew_" + oObj.itemnumber + "' name='renew' value='" + oObj.itemnumber +"'/>"
536                                     +  "</span>";
537                         }
538                         content += msg;
539                         if ( can_renew || can_force_renew ) {
540                             content += "<span class='renewals'>("
541                                     + RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed )
542                                     + ")</span>";
543                         }
544
545                         content += "</span>";
546
547                         return content;
548                     }
549                 },
550                 {
551                     "bSortable": false,
552                     "bVisible": AllowCirculate ? true : false,
553                     "mDataProp": function ( oObj ) {
554                         if ( oObj.can_renew_error == "on_reserve" ) {
555                             return "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" + oObj.biblionumber + "'>" + ON_HOLD + "</a>";
556                         } else {
557                             return "<input type='checkbox' class='checkin' id='checkin_" + oObj.itemnumber + "' name='checkin' value='" + oObj.itemnumber +"'></input>";
558                         }
559                     }
560                 },
561                 {
562                     "bVisible": ClaimReturnedLostValue ? true : false,
563                     "bSortable": false,
564                     "mDataProp": function ( oObj ) {
565                         let content = "";
566
567                         if ( oObj.return_claim_id ) {
568                           content = `<span class="badge">${oObj.return_claim_created_on_formatted}</span>`;
569                         } else {
570                           content = `<a class="btn btn-default btn-xs claim-returned-btn" data-itemnumber="${oObj.itemnumber}"><i class="fa fa-exclamation-circle"></i> ${RETURN_CLAIMED_MAKE}</a>`;
571                         }
572                         return content;
573                     }
574                 },
575                 {
576                     "bVisible": exports_enabled == 1 ? true : false,
577                     "bSortable": false,
578                     "mDataProp": function ( oObj ) {
579                         var s = "<input type='checkbox' name='itemnumbers' value='" + oObj.itemnumber + "' style='visibility:hidden;' />";
580
581                         s += "<input type='checkbox' class='export' id='export_" + oObj.biblionumber + "' name='biblionumbers' value='" + oObj.biblionumber + "' />";
582                         return s;
583                     }
584                 }
585             ],
586             "fnFooterCallback": function ( nRow, aaData, iStart, iEnd, aiDisplay ) {
587                 var total_charge = 0;
588                 var total_fine  = 0;
589                 var total_price = 0;
590                 for ( var i=0; i < aaData.length; i++ ) {
591                     total_charge += aaData[i]['charge'] * 1;
592                     total_fine += aaData[i]['fine'] * 1;
593                     total_price  += aaData[i]['price'] * 1;
594                 }
595                 $("#totaldue").html(total_charge.toFixed(2));
596                 $("#totalfine").html(total_fine.toFixed(2));
597                 $("#totalprice").html(total_price.toFixed(2));
598             },
599             "bPaginate": false,
600             "bProcessing": true,
601             "bServerSide": false,
602             "sAjaxSource": '/cgi-bin/koha/svc/checkouts',
603             "fnServerData": function ( sSource, aoData, fnCallback ) {
604                 aoData.push( { "name": "borrowernumber", "value": borrowernumber } );
605
606                 $.getJSON( sSource, aoData, function (json) {
607                     fnCallback(json)
608                 } );
609             },
610             "rowGroup":{
611                 "dataSrc": "issued_today",
612                 "startRender": function ( rows, group ) {
613                     if ( group ) {
614                         return $('<tr/>').append("<td colspan='100%'><strong>" + TODAYS_CHECKOUTS + "</strong></td>");
615                     } else {
616                         return $('<tr/>').append("<td colspan='100%'><strong>" + PREVIOUS_CHECKOUTS + "</strong></td>");
617                     }
618                 }
619             },
620             "fnInitComplete": function(oSettings, json) {
621                 // Build a summary of checkouts grouped by itemtype
622                 var checkoutsByItype = json.aaData.reduce(function (obj, row) {
623                     obj[row.type_for_stat] = (obj[row.type_for_stat] || 0) + 1;
624                     return obj;
625                 }, {});
626                 var ul = $('<ul>');
627                 Object.keys(checkoutsByItype).sort().forEach(function (itype) {
628                     var li = $('<li>')
629                         .append($('<strong>').html(itype || MSG_NO_ITEMTYPE))
630                         .append(': ' + checkoutsByItype[itype]);
631                     ul.append(li);
632                 })
633                 $('<details>')
634                     .addClass('checkouts-by-itemtype')
635                     .append($('<summary>').html(MSG_CHECKOUTS_BY_ITEMTYPE))
636                     .append(ul)
637                     .insertBefore(oSettings.nTableWrapper)
638             },
639         }, columns_settings_issues_table);
640
641         if ( $("#issues-table").length ) {
642             $("#issues-table_processing").position({
643                 of: $( "#issues-table" ),
644                 collision: "none"
645             });
646         }
647     }
648
649     // Don't load relatives' issues table unless it is clicked on
650     var relativesIssuesTable;
651     $("#relatives-issues-tab").click( function() {
652         if ( ! relativesIssuesTable ) {
653             relativesIssuesTable = $("#relatives-issues-table").dataTable($.extend(true, {}, dataTablesDefaults, {
654                 "bAutoWidth": false,
655                 "sDom": "rt",
656                 "aaSorting": [],
657                 "aoColumns": [
658                     {
659                         "mDataProp": "date_due",
660                         "bVisible": false,
661                     },
662                     {
663                         "iDataSort": 0, // Sort on hidden unformatted date due column
664                         "mDataProp": function( oObj ) {
665                             var today = new Date();
666                             var due = new Date( oObj.date_due );
667                             if ( today > due ) {
668                                 return "<span class='overdue'>" + oObj.date_due_formatted + "</span>";
669                             } else {
670                                 return oObj.date_due_formatted;
671                             }
672                         }
673                     },
674                     {
675                         "mDataProp": function ( oObj ) {
676                             title = "<span class='strong'><a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber="
677                                   + oObj.biblionumber
678                                   + "'>"
679                                   + oObj.title.escapeHtml();
680
681                             $.each(oObj.subtitle, function( index, value ) {
682                                       title += " " + value.escapeHtml();
683                             });
684
685                             title += " " + oObj.part_number + " " + oObj.part_name;
686
687                             if ( oObj.enumchron ) {
688                                 title += " (" + oObj.enumchron.escapeHtml() + ")";
689                             }
690
691                             title += "</a></span>";
692
693                             if ( oObj.author ) {
694                                 title += " " + BY.replace( "_AUTHOR_", " " + oObj.author.escapeHtml() );
695                             }
696
697                             if ( oObj.itemnotes ) {
698                                 var span_class = "";
699                                 if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) {
700                                     span_class = "circ-hlt";
701                                 }
702                                 title += " - <span class='" + span_class + "'>" + oObj.itemnotes.escapeHtml() + "</span>"
703                             }
704
705                             if ( oObj.itemnotes_nonpublic ) {
706                                 var span_class = "";
707                                 if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) {
708                                     span_class = "circ-hlt";
709                                 }
710                                 title += " - <span class='" + span_class + "'>" + oObj.itemnotes_nonpublic.escapeHtml() + "</span>"
711                             }
712
713                             var onsite_checkout = '';
714                             if ( oObj.onsite_checkout == 1 ) {
715                                 onsite_checkout += " <span class='onsite_checkout'>(" + INHOUSE_USE + ")</span>";
716                             }
717
718                             title += " "
719                                   + "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber="
720                                   + oObj.biblionumber
721                                   + "&itemnumber="
722                                   + oObj.itemnumber
723                                   + "#"
724                                   + oObj.itemnumber
725                                   + "'>"
726                                   + (oObj.barcode ? oObj.barcode.escapeHtml() : "")
727                                   + "</a>"
728                                   + onsite_checkout;
729
730                             return title;
731                         },
732                         "sType": "anti-the"
733                     },
734                     {
735                         "mDataProp": function ( oObj ) {
736                             return oObj.recordtype_description.escapeHtml();
737                         }
738                     },
739                     {
740                         "mDataProp": function ( oObj ) {
741                             return oObj.itemtype_description.escapeHtml();
742                         }
743                     },
744                     {
745                         "mDataProp": function ( oObj ) {
746                             return ( oObj.collection ? oObj.collection.escapeHtml() : '' );
747                         }
748                     },
749                     {
750                         "mDataProp": function ( oObj ) {
751                             return ( oObj.location ? oObj.location.escapeHtml() : '' );
752                         }
753                     },
754                     {
755                         "mDataProp": "issuedate",
756                         "bVisible": false,
757                     },
758                     {
759                         "iDataSort": 7, // Sort on hidden unformatted issuedate column
760                         "mDataProp": "issuedate_formatted",
761                     },
762                     {
763                         "mDataProp": function ( oObj ) {
764                             return oObj.branchname.escapeHtml();
765                         }
766                     },
767                     {
768                         "mDataProp": function ( oObj ) {
769                             return ( oObj.itemcallnumber ? oObj.itemcallnumber.escapeHtml() : '' );
770                         }
771                     },
772                     {
773                         "mDataProp": function ( oObj ) {
774                             if ( ! oObj.charge ) oObj.charge = 0;
775                             return parseFloat(oObj.charge).toFixed(2);
776                         }
777                     },
778                     {
779                         "mDataProp": function ( oObj ) {
780                             if ( ! oObj.fine ) oObj.fine = 0;
781                             return parseFloat(oObj.fine).toFixed(2);
782                         }
783                     },
784                     {
785                         "mDataProp": function ( oObj ) {
786                             if ( ! oObj.price ) oObj.price = 0;
787                             return parseFloat(oObj.price).toFixed(2);
788                         }
789                     },
790                     {
791                         "mDataProp": function( oObj ) {
792                             return "<a href='/cgi-bin/koha/members/moremember.pl?borrowernumber=" + oObj.borrowernumber + "'>"
793                                 + oObj.borrower.firstname.escapeHtml()
794                                 + " " +
795                                 oObj.borrower.surname.escapeHtml()
796                                 + " (" + oObj.borrower.cardnumber.escapeHtml() + ")</a>"
797                         }
798                     },
799                 ],
800                 "bPaginate": false,
801                 "bProcessing": true,
802                 "bServerSide": false,
803                 "sAjaxSource": '/cgi-bin/koha/svc/checkouts',
804                 "fnServerData": function ( sSource, aoData, fnCallback ) {
805                     $.each(relatives_borrowernumbers, function( index, value ) {
806                         aoData.push( { "name": "borrowernumber", "value": value } );
807                     });
808
809                     $.getJSON( sSource, aoData, function (json) {
810                         fnCallback(json)
811                     } );
812                 },
813             }));
814         }
815     });
816
817     if ( $("#relatives-issues-table").length ) {
818         $("#relatives-issues-table_processing").position({
819             of: $( "#relatives-issues-table" ),
820             collision: "none"
821         });
822     }
823
824     if ( AllowRenewalLimitOverride || AllowRenewalOnHoldOverride ) {
825         $( '#override_limit' ).click( function () {
826             if ( this.checked ) {
827                 if ( AllowRenewalLimitOverride ) {
828                     $( '.renewals-allowed' ).show();
829                     $( '.renewals-disabled' ).hide();
830                 }
831                 if ( AllowRenewalOnHoldOverride ) {
832                     $( '.renewals-allowed-on_reserve' ).show();
833                 }
834             } else {
835                 $( '.renewals-allowed' ).hide();
836                 $( '.renewals-allowed-on_reserve' ).hide();
837                 $( '.renewals-disabled' ).show();
838             }
839         } ).prop('checked', false);
840     }
841
842     // Handle return claims
843     $(document).on("click", '.claim-returned-btn', function(e){
844         e.preventDefault();
845         itemnumber = $(this).data('itemnumber');
846
847         $('#claims-returned-itemnumber').val(itemnumber);
848         $('#claims-returned-notes').val("");
849         $('#claims-returned-charge-lost-fee').attr('checked', false)
850         $('#claims-returned-modal').modal()
851     });
852     $(document).on("click", '#claims-returned-modal-btn-submit', function(e){
853         let itemnumber = $('#claims-returned-itemnumber').val();
854         let notes = $('#claims-returned-notes').val();
855         let fee = $('#claims-returned-charge-lost-fee').attr('checked') ? true : false;
856
857         $('#claims-returned-modal').modal('hide')
858
859         $(`.claim-returned-btn[data-itemnumber='${itemnumber}']`).replaceWith(`<img id='return_claim_spinner_${itemnumber}' src='${interface}/${theme}/img/spinner-small.gif' />`);
860
861         params = {
862             item_id: itemnumber,
863             notes: notes,
864             charge_lost_fee: fee,
865             created_by: logged_in_user_borrowernumber,
866         };
867
868         $.post( '/api/v1/return_claims', JSON.stringify(params), function( data ) {
869
870             id = "#return_claim_spinner_" + data.item_id;
871
872             let created_on = new Date(data.created_on);
873
874             let content = "";
875             if ( data.claim_id ) {
876                 content = `<span class="badge">${created_on.toLocaleDateString()}</span>`;
877                 $(id).parent().parent().addClass('ok');
878             } else {
879                 content = RETURN_CLAIMED_FAILURE;
880                 $(id).parent().parent().addClass('warn');
881             }
882
883             $(id).replaceWith( content );
884
885             refreshReturnClaimsTable();
886         }, "json")
887
888     });
889
890
891     // Don't load return claims table unless it is clicked on
892     var returnClaimsTable;
893     $("#return-claims-tab").click( function() {
894         refreshReturnClaimsTable();
895     });
896
897     function refreshReturnClaimsTable(){
898         loadReturnClaimsTable();
899         $("#return-claims-table").DataTable().ajax.reload();
900     }
901     function loadReturnClaimsTable() {
902         if ( ! returnClaimsTable ) {
903             returnClaimsTable = $("#return-claims-table").dataTable({
904                 "bAutoWidth": false,
905                 "sDom": "rt",
906                 "aaSorting": [],
907                 "aoColumns": [
908                     {
909                         "mDataProp": "id",
910                         "bVisible": false,
911                     },
912                     {
913                         "mDataProp": function ( oObj ) {
914                               let title = `<a class="return-claim-title strong" href="/cgi-bin/koha/circ/request-rcticle.pl?biblionumber=[% rc.checkout.item.biblionumber | html %]">
915                                   ${oObj.title}
916                                   ${oObj.enumchron || ""}
917                               </a>`;
918                               if ( oObj.author ) {
919                                 title += `by ${oObj.author}`;
920                               }
921                               title += `<a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=${oObj.biblionumber}&itemnumber=${oObj.itemnumber}">${oObj.barcode}</a>`;
922
923                               return title;
924                         }
925                     },
926                     {
927                         "sClass": "return-claim-notes-td",
928                         "mDataProp": function ( oObj ) {
929                             return `
930                                 <span id="return-claim-notes-static-${oObj.id}" class="return-claim-notes" data-return-claim-id="${oObj.id}">${oObj.notes}</span>
931                                 <i style="float:right" class="fa fa-pencil-square-o" title="Double click to edit"></i>
932                             `;
933                         }
934                     },
935                     {
936                         "mDataProp": function ( oObj ) {
937                             let created_on = new Date( oObj.created_on );
938                             return created_on.toLocaleDateString();
939                         }
940                     },
941                     {
942                         "mDataProp": function ( oObj ) {
943                             let updated_on = new Date( oObj.updated_on );
944                             return updated_on.toLocaleDateString();
945                         }
946                     },
947                     {
948                         "mDataProp": function ( oObj ) {
949                             if ( ! oObj.resolution ) return "";
950
951                             let desc = `<strong>${oObj.resolution_data.lib}</strong> on <i>${oObj.resolved_on}</i>`;
952                             if (oObj.resolved_by_data) desc += ` by <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=${oObj.resolved_by_data.borrowernumber}">${oObj.resolved_by_data.firstname || ""} ${oObj.resolved_by_data.surname || ""}</a>`;
953                             return desc;
954                         }
955                     },
956                     {
957                         "mDataProp": function ( oObj ) {
958                             let delete_html = oObj.resolved_on
959                                 ? `<li><a href="#" class="return-claim-tools-delete" data-return-claim-id="${oObj.id}"><i class="fa fa-trash"></i> Delete</a></li>`
960                                 : "";
961                             let resolve_html = ! oObj.resolution
962                                 ? `<li><a href="#" class="return-claim-tools-resolve" data-return-claim-id="${oObj.id}"><i class="fa fa-check-square"></i> Resolve</a></li>`
963                                 : "";
964
965                             return `
966                                 <div class="btn-group">
967                                   <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
968                                     Actions <span class="caret"></span>
969                                   </button>
970                                   <ul class="dropdown-menu">
971                                     <li><a href="#" class="return-claim-tools-editnotes" data-return-claim-id="${oObj.id}"><i class="fa fa-edit"></i> Edit notes</a></li>
972                                     ${resolve_html}
973                                     ${delete_html}
974                                   </ul>
975                                 </div>
976                             `;
977                         }
978                     },
979                 ],
980                 "bPaginate": false,
981                 "bProcessing": true,
982                 "bServerSide": false,
983                 "sAjaxSource": '/cgi-bin/koha/svc/return_claims',
984                 "fnServerData": function ( sSource, aoData, fnCallback ) {
985                     aoData.push( { "name": "borrowernumber", "value": borrowernumber } );
986
987                     $.getJSON( sSource, aoData, function (json) {
988                         let resolved = json.resolved;
989                         let unresolved = json.unresolved;
990
991                         $('#return-claims-count-resolved').text(resolved);
992                         $('#return-claims-count-unresolved').text(unresolved);
993
994                         fnCallback(json)
995                     } );
996                 },
997             });
998         }
999     }
1000
1001     $('body').on('click', '.return-claim-tools-editnotes', function() {
1002         let id = $(this).data('return-claim-id');
1003         $(`#return-claim-notes-static-${id}`).parent().dblclick();
1004     });
1005     $('body').on('dblclick', '.return-claim-notes-td', function() {
1006         let elt = $(this).children('.return-claim-notes');
1007         let id = elt.data('return-claim-id');
1008         if ( $(`#return-claim-notes-editor-textarea-${id}`).length == 0 ) {
1009             let note = elt.text();
1010             let editor = `
1011                 <span id="return-claim-notes-editor-${id}">
1012                     <textarea id="return-claim-notes-editor-textarea-${id}">${note}</textarea>
1013                     <br/>
1014                     <a class="btn btn-default btn-xs claim-returned-notes-editor-submit" data-return-claim-id="${id}"><i class="fa fa-save"></i> Update</a>
1015                     <a class="claim-returned-notes-editor-cancel" data-return-claim-id="${id}" href="#">Cancel</a>
1016                 </span>
1017             `;
1018             elt.hide();
1019             $(editor).insertAfter( elt );
1020         }
1021     });
1022
1023     $('body').on('click', '.claim-returned-notes-editor-submit', function(){
1024         let id = $(this).data('return-claim-id');
1025         let notes = $(`#return-claim-notes-editor-textarea-${id}`).val();
1026
1027         let params = {
1028             notes: notes,
1029             updated_by: logged_in_user_borrowernumber
1030         };
1031
1032         $(this).parent().remove();
1033
1034         $.ajax({
1035             url: `/api/v1/return_claims/${id}/notes`,
1036             type: 'PUT',
1037             data: JSON.stringify(params),
1038             success: function( data ) {
1039                 let notes = $(`#return-claim-notes-static-${id}`);
1040                 notes.text(data.notes);
1041                 notes.show();
1042             },
1043             contentType: "json"
1044         });
1045     });
1046
1047     $('body').on('click', '.claim-returned-notes-editor-cancel', function(){
1048         let id = $(this).data('return-claim-id');
1049         $(this).parent().remove();
1050         $(`#return-claim-notes-static-${id}`).show();
1051     });
1052
1053     // Hanld return claim deletion
1054     $('body').on('click', '.return-claim-tools-delete', function() {
1055         let confirmed = confirm(CONFIRM_DELETE_RETURN_CLAIM);
1056         if ( confirmed ) {
1057             let id = $(this).data('return-claim-id');
1058
1059             $.ajax({
1060                 url: `/api/v1/return_claims/${id}`,
1061                 type: 'DELETE',
1062                 success: function( data ) {
1063                     refreshReturnClaimsTable();
1064                 }
1065             });
1066         }
1067     });
1068
1069     // Handle return claim resolution
1070     $('body').on('click', '.return-claim-tools-resolve', function() {
1071         let id = $(this).data('return-claim-id');
1072
1073         $('#claims-returned-resolved-modal-id').val(id);
1074         $('#claims-returned-resolved-modal').modal()
1075     });
1076
1077     $(document).on('click', '#claims-returned-resolved-modal-btn-submit', function(e) {
1078         let resolution = $('#claims-returned-resolved-modal-resolved-code').val();
1079         let id = $('#claims-returned-resolved-modal-id').val();
1080
1081         $('#claims-returned-resolved-modal-btn-submit-spinner').show();
1082         $('#claims-returned-resolved-modal-btn-submit-icon').hide();
1083
1084         params = {
1085           resolution: resolution,
1086           updated_by: logged_in_user_borrowernumber
1087         };
1088
1089         $.ajax({
1090             url: `/api/v1/return_claims/${id}/resolve`,
1091             type: 'PUT',
1092             data: JSON.stringify(params),
1093             success: function( data ) {
1094                 $('#claims-returned-resolved-modal-btn-submit-spinner').hide();
1095                 $('#claims-returned-resolved-modal-btn-submit-icon').show();
1096                 $('#claims-returned-resolved-modal').modal('hide')
1097
1098                 refreshReturnClaimsTable();
1099             },
1100             contentType: "json"
1101         });
1102
1103     });
1104
1105  });