From 01207cf7ad4439b9424ee55476cee944ec3d097c Mon Sep 17 00:00:00 2001 From: Julian Maurice <julian.maurice@biblibre.com> Date: Wed, 18 Nov 2015 15:52:14 +0100 Subject: [PATCH] Bug 15219: Server-side processing and pagination on checkouts tables Some libraries have patrons with more than 1000 checkouts. Loading all of them at once can be very long, and sometimes can cause timeout errors. This patch prevent that by enabling server-side processing and pagination on checkouts tables. This affects patron's checkouts and patron's relatives' checkouts tables on pages circ/circulation.pl and members/moremember.pl. As server-side processing can be useless and cumbersome with small sets of data, a new system preference is introduced to control this behaviour (server-side processing is disabled by default). Additionally, this patch: - adds a switch to turn off and on row grouping (today's checkouts vs previous checkouts) (on by default) - adds "column settings" for relatives' checkouts tables - factorize some code that was duplicated across the two DataTables configurations (mDataProp) Test plan: 1. Find (or create) a patron that have at least 10 checkouts and where the relatives checkouts table contain at least 10 checkouts. 2. Go to the patron's detail page and check everything works fine in both tables (sorting, pagination, the data itself, ...) 3. Do the same on circulation page (circulation.pl) 4. Enable system preference ServerSideCheckoutsTable 5. Repeat steps 2 and 3 Signed-off-by: Eric Gosselin <eric.gosselin@inlibro.com> --- admin/columns_settings.yml | 60 +- installer/data/mysql/atomicupdate/bug_15219.sql | 2 + installer/data/mysql/sysprefs.sql | 1 + .../plugins/jquery.dataTables.rowGrouping.js | 10 +- .../prog/en/includes/checkouts-table-footer.inc | 2 +- .../prog/en/includes/checkouts-table.inc | 1 - .../prog/en/includes/checkouts.js.inc | 24 + .../prog/en/includes/columns_settings.inc | 2 +- .../intranet-tmpl/prog/en/includes/strings.inc | 1 + .../en/modules/admin/preferences/circulation.pref | 6 + .../prog/en/modules/circ/circulation.tt | 13 +- .../prog/en/modules/members/moremember.tt | 12 +- koha-tmpl/intranet-tmpl/prog/js/checkouts.js | 709 +++++++++++---------- svc/checkouts | 108 ++-- 14 files changed, 503 insertions(+), 448 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/bug_15219.sql create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/checkouts.js.inc diff --git a/admin/columns_settings.yml b/admin/columns_settings.yml index e212edd..ab3c777 100644 --- a/admin/columns_settings.yml +++ b/admin/columns_settings.yml @@ -208,7 +208,7 @@ modules: moremember: issues-table: - - columnname: sort_order + columnname: group_order cannot_be_toggled: 1 cannot_be_modified: 1 is_hidden: 1 @@ -218,11 +218,6 @@ modules: cannot_be_modified: 1 is_hidden: 1 - - columnname: due_date_unformatted - cannot_be_toggled: 1 - cannot_be_modified: 1 - is_hidden: 1 - - columnname: due_date - columnname: title @@ -256,6 +251,29 @@ modules: columnname: export cannot_be_toggled: 1 cannot_be_modified: 1 + relatives-issues-table: + - + columnname: due_date + - + columnname: title + - + columnname: item_type + - + columnname: location + - + columnname: checkout_on + - + columnname: checkout_from + - + columnname: callno + - + columnname: charge + - + columnname: fine + - + columnname: price + - + columnname: patron reports: lostitems: @@ -293,7 +311,7 @@ modules: circulation: issues-table: - - columnname: sort_order + columnname: group_order cannot_be_toggled: 1 cannot_be_modified: 1 is_hidden: 1 @@ -303,11 +321,6 @@ modules: cannot_be_modified: 1 is_hidden: 1 - - columnname: due_date_unformatted - cannot_be_toggled: 1 - cannot_be_modified: 1 - is_hidden: 1 - - columnname: due_date - columnname: title @@ -341,6 +354,29 @@ modules: columnname: export cannot_be_toggled: 1 cannot_be_modified: 1 + relatives-issues-table: + - + columnname: due_date + - + columnname: title + - + columnname: item_type + - + columnname: location + - + columnname: checkout_on + - + columnname: checkout_from + - + columnname: callno + - + columnname: charge + - + columnname: fine + - + columnname: price + - + columnname: patron returns: checkedintable: diff --git a/installer/data/mysql/atomicupdate/bug_15219.sql b/installer/data/mysql/atomicupdate/bug_15219.sql new file mode 100644 index 0000000..a186d45 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_15219.sql @@ -0,0 +1,2 @@ +INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) +VALUES ('ServerSideCheckoutsTables','0',NULL,'If enabled, use the "server-side processing" feature for checkouts tables. Can improve performance when there is a big number of checkouts to display.','YesNo'); diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 79229d7..f64a862 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -463,6 +463,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('SelfCheckTimeout','120','','Define the number of seconds before the Web-based Self Checkout times out a patron','Integer'), ('SeparateHoldings','0',NULL,'Separate current branch holdings from other holdings','YesNo'), ('SeparateHoldingsBranch','homebranch','homebranch|holdingbranch','Branch used to separate holdings','Choice'), +('ServerSideCheckoutsTables','0',NULL,'If enabled, use the "server-side processing" feature for checkouts tables. Can improve performance when there is a big number of checkouts per patron.','YesNo'), ('SessionRestrictionByIP','1','Check for change in remote IP address for session security. Disable only when remote IP address changes frequently.','','YesNo'), ('SessionStorage','mysql','mysql|Pg|tmp','Use database or a temporary file for storing session data','Choice'), ('ShelfBrowserUsesCcode','1','0','Use the item collection code when finding items for the shelf browser.','YesNo'), diff --git a/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.dataTables.rowGrouping.js b/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.dataTables.rowGrouping.js index 126ff63..12a88e4 100644 --- a/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.dataTables.rowGrouping.js +++ b/koha-tmpl/intranet-tmpl/lib/jquery/plugins/jquery.dataTables.rowGrouping.js @@ -608,15 +608,15 @@ } - oTable.fnSetColumnVis(properties.iGroupingColumnIndex, !properties.bHideGroupingColumn); + oTable.fnSetColumnVis(properties.iGroupingColumnIndex, !properties.bHideGroupingColumn, false); if (properties.bCustomColumnOrdering) { - oTable.fnSetColumnVis(properties.iGroupingOrderByColumnIndex, !properties.bHideGroupingOrderByColumn); + oTable.fnSetColumnVis(properties.iGroupingOrderByColumnIndex, !properties.bHideGroupingOrderByColumn, false); } if (properties.iGroupingColumnIndex2 != -1) { - oTable.fnSetColumnVis(properties.iGroupingColumnIndex2, !properties.bHideGroupingColumn2); + oTable.fnSetColumnVis(properties.iGroupingColumnIndex2, !properties.bHideGroupingColumn2, false); } if (properties.bCustomColumnOrdering2) { - oTable.fnSetColumnVis(properties.iGroupingOrderByColumnIndex2, !properties.bHideGroupingOrderByColumn2); + oTable.fnSetColumnVis(properties.iGroupingOrderByColumnIndex2, !properties.bHideGroupingOrderByColumn2, false); } oTable.fnSettings().aoDrawCallback.push({ "fn": _fnDrawCallBackWithGrouping, @@ -687,4 +687,4 @@ }); }; -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc index 3adbfa3..a8b906b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc @@ -1,6 +1,6 @@ <tfoot> <tr> - <td colspan="11" style="text-align: right; font-weight:bold;">Totals:</td> + <td colspan="10" style="text-align: right; font-weight:bold;">Totals:</td> <td id="totaldue" style="text-align: right;">[% totaldue %]</td> <td id="totalfine" style="text-align: right;">[% finetotal %]</td> <td id="totalprice" style="text-align: right;">[% totalprice %]</td> diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc index 1d103a0..66c1ba0 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc @@ -13,7 +13,6 @@ <th scope="col"> </th> <th scope="col"> </th> <th scope="col">Due date</th> - <th scope="col">Due date</th> <th scope="col">Title</th> <th scope="col">Item type</th> <th scope="col">Location</th> diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts.js.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts.js.inc new file mode 100644 index 0000000..732c5ba --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts.js.inc @@ -0,0 +1,24 @@ +[% USE Koha %] + +<script> + [% IF Koha.Preference('ServerSideCheckoutsTables') %] + var ServerSideCheckoutsTables = true; + [% ELSE %] + var ServerSideCheckoutsTables = false; + [% END %] + var AllowCirculate = [% (CAN_user_circulate_circulate_remaining_permissions)? 1 : 0 %]; + var AllowRenewalLimitOverride = [% (CAN_user_circulate_override_renewals && Koha.Preference('AllowRenewalLimitOverride'))? 1 : 0 %]; + + var MSG_DT_LOADING_RECORDS = _("Loading... you may continue scanning."); + + var borrowernumber = "[% borrowernumber %]"; + var relatives_borrowernumbers = new Array(); + [% FOREACH b IN relatives_borrowernumbers %] + relatives_borrowernumbers.push("[% b %]"); + [% END %] + + var columns_settings = [% ColumnsSettings.GetColumns( 'circ', 'circulation', 'issues-table', 'json' ) %]; + var relatives_columns_settings = [% ColumnsSettings.GetColumns( 'circ', 'circulation', 'relatives-issues-table', 'json' ) %]; +</script> + +<script type="text/javascript" src="[% interface %]/[% theme %]/js/checkouts.js"></script> diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc index b065a35..74d632c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc @@ -28,7 +28,7 @@ function KohaTable(selector, dt_parameters, columns_settings) { var table = $(selector).dataTable($.extend(true, {}, dataTablesDefaults, dt_parameters)); $(hidden_ids).each(function(index, value) { - table.fnSetColumnVis( value, false ); + table.fnSetColumnVis( value, false, false ); }); return table; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc index 7483ccf..3b58d4e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc @@ -41,5 +41,6 @@ var SUSPEND_HOLD_ERROR_NOT_FOUND = _("Unable to suspend hold, hold not found"); var RESUME_HOLD_ERROR_NOT_FOUND = _("Unable to resume, hold not found"); var CURRENT = _(" (current) "); + var GROUP_TODAYS_CHECKOUTS = _("Group today's checkouts"); //]]> </script> diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index 81b671c..b48313e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -46,6 +46,12 @@ Circulation: desc: latest to earliest - due date. - + - pref: ServerSideCheckoutsTables + choices: + yes: Enable + no: Do not enable + - server-side processing on checkouts tables. It can improve performance when there is a big number of checkouts to display. + - - pref: SpecifyDueDate choices: yes: Allow diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index 114f2e7..d354bb3 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -28,30 +28,20 @@ [% INCLUDE 'timepicker.inc' %] <script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery.dataTables.rowGrouping.js"></script> <script type="text/javascript" src="[% interface %]/[% theme %]/js/pages/circulation.js"></script> -<script type="text/javascript" src="[% interface %]/[% theme %]/js/checkouts.js"></script> +[% INCLUDE 'checkouts.js.inc' %] <script type="text/javascript" src="[% interface %]/[% theme %]/js/holds.js"></script> <script type="text/javascript"> //<![CDATA[ /* Set some variable needed in circulation.js */ -var MSG_DT_LOADING_RECORDS = _("Loading... you may continue scanning."); var interface = "[% interface %]"; var theme = "[% theme %]"; -var borrowernumber = "[% borrowernumber %]"; var branchcode = "[% branch %]"; var exports_enabled = "[% exports_enabled %]"; -var AllowRenewalLimitOverride = [% (CAN_user_circulate_override_renewals && Koha.Preference('AllowRenewalLimitOverride') )? 1: 0 %]; -var AllowCirculate = [% (CAN_user_circulate_circulate_remaining_permissions)? 1 : 0 %]; var script = "circulation"; -var relatives_borrowernumbers = new Array(); -[% FOREACH b IN relatives_borrowernumbers %] - relatives_borrowernumbers.push("[% b %]"); -[% END %] var MSG_EXPORT_SELECT_CHECKOUTS = _("You must select checkout(s) to export"); var MSG_CONFIRM_DELETE_MESSAGE = _("Are you sure you want to delete this message? This cannot be undone."); -columns_settings = [% ColumnsSettings.GetColumns( 'circ', 'circulation', 'issues-table', 'json' ) %] - [% IF borrowernumber and borrower %] if($.cookie("holdfor") != [% borrowernumber %]){ $.removeCookie("holdfor", { path: '/' }); @@ -921,7 +911,6 @@ No patron matched <span class="ex">[% message | html %]</span> <table id="relatives-issues-table"> <thead> <tr> - <th scope="col">Due date (unformatted, hidden)</th> <th scope="col">Due date</th> <th scope="col">Title</th> <th scope="col">Item type</th> diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt index 271b30b..62e1fa1 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -25,28 +25,19 @@ [% INCLUDE 'timepicker.inc' %] <script type="text/javascript" src="[% interface %]/lib/jquery/plugins/jquery.dataTables.rowGrouping.js"></script> <script type="text/javascript" src="[% interface %]/[% theme %]/js/pages/circulation.js"></script> -<script type="text/javascript" src="[% interface %]/[% theme %]/js/checkouts.js"></script> +[% INCLUDE 'checkouts.js.inc' %] <script type="text/javascript" src="[% interface %]/[% theme %]/js/holds.js"></script> <script type="text/JavaScript"> //<![CDATA[ /* Set some variable needed in circulation.js */ var interface = "[% interface %]"; var theme = "[% theme %]"; -var borrowernumber = "[% borrowernumber %]"; var branchcode = "[% Branches.GetLoggedInBranchcode() %]"; var exports_enabled = "[% exports_enabled %]"; -var AllowCirculate = [% (CAN_user_circulate_circulate_remaining_permissions)? 1 : 0 %] -var AllowRenewalLimitOverride = [% (CAN_user_circulate_override_renewals && Koha.Preference('AllowRenewalLimitOverride') )? 1: 0 %]; var script = "moremember"; -var relatives_borrowernumbers = new Array(); -[% FOREACH b IN relatives_borrowernumbers %] - relatives_borrowernumbers.push("[% b %]"); -[% END %] var MSG_EXPORT_SELECT_CHECKOUTS = _("You must select checkout(s) to export"); -columns_settings = [% ColumnsSettings.GetColumns( 'members', 'moremember', 'issues-table', 'json' ) %] - $(document).ready(function() { $('#finesholdsissues').tabs({ // Correct table sizing for tables hidden in tabs @@ -528,7 +519,6 @@ function validate1(date) { <table id="relatives-issues-table" style="width: 100% !Important;"> <thead> <tr> - <th scope="col">Due date hidden not formatted</th> <th scope="col">Due date</th> <th scope="col">Title</th> <th scope="col">Item type</th> diff --git a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js index 1ef2375..20d229c 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js +++ b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js @@ -1,3 +1,250 @@ +(function($) { + +var ymd = $.datepicker.formatDate('yy-mm-dd', new Date()); + +function enableGrouping(datatable) { + datatable.rowGrouping({ + iGroupingColumnIndex: 1, + iGroupingOrderByColumnIndex: 0, + sGroupingColumnSortDirection: "desc" + }); +} + +function disableGrouping(datatable) { + var oSettings = datatable.fnSettings(); + for (f = 0; f < oSettings.aoDrawCallback.length; f++) { + if (oSettings.aoDrawCallback[f].sName == 'fnRowGrouping') { + oSettings.aoDrawCallback.splice(f, 1); + oSettings.aaSortingFixed = null; + datatable.fnDraw(); + break; + } + } +} + +var mRenders = { + groupOrder: function (oData, sType, oObj) { + if ( oObj.issued_today ) { + return "1"; + } else { + return "0"; + } + }, + groupName: function (oData, sType, oObj) { + if (oObj.issued_today) { + return "<strong>" + TODAYS_CHECKOUTS + "</strong>"; + } else { + return "<strong>" + PREVIOUS_CHECKOUTS + "</strong>"; + } + }, + dateDueFormatted: function (oData, sType, oObj) { + var due = oObj.date_due_formatted; + + if ( oObj.date_due_overdue ) { + due = "<span class='overdue'>" + due + "</span>"; + } + + due = "<span id='date_due_" + oObj.itemnumber + "' class='date_due'>" + due + "</span>"; + + if ( oObj.lost ) { + due += "<span class='lost'>" + oObj.lost + "</span>"; + } + + if ( oObj.damaged ) { + due += "<span class='dmg'>" + oObj.damaged + "</span>"; + } + + return due; + }, + title: function (oData, sType, oObj) { + var title = "<span class='strong'><a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber=" + + oObj.biblionumber + + "'>" + + oObj.title; + + $.each(oObj.subtitle, function( index, value ) { + title += " " + value.subfield; + }); + + if ( oObj.enumchron ) { + title += " (" + oObj.enumchron + ")"; + } + + title += "</a></span>"; + + if ( oObj.author ) { + title += " " + BY.replace( "_AUTHOR_", " " + oObj.author ); + } + + if ( oObj.itemnotes ) { + var span_class = "text-muted"; + title += " - <span class='" + span_class + "'>" + oObj.itemnotes + "</span>" + } + + if ( oObj.itemnotes_nonpublic ) { + var span_class = "text-danger"; + if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) { + span_class = "circ-hlt"; + } + title += " - <span class='" + span_class + "'>" + oObj.itemnotes_nonpublic + "</span>" + } + + var onsite_checkout = ''; + if ( oObj.onsite_checkout == 1 ) { + onsite_checkout += " <span class='onsite_checkout'>(" + INHOUSE_USE + ")</span>"; + } + + title += " " + + "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=" + + oObj.biblionumber + + "&itemnumber=" + + oObj.itemnumber + + "#" + + oObj.itemnumber + + "'>" + + oObj.barcode + + "</a>" + + onsite_checkout; + + return title; + }, + itemtype: function (oData, sType, oObj) { + return oObj.itemtype_description; + }, + issuedate: function (oData, sType, oObj) { + return oObj.issuedate_formatted; + }, + branch: function (oData, sType, oObj) { + return oObj.branchname; + }, + charge: function (oData, sType, oObj) { + if (!oObj.charge) oObj.charge = 0; + return '<span style="text-align: right; display: block;">' + parseFloat(oObj.charge).toFixed(2) + '<span>'; + }, + fine: function (oData, sType, oObj) { + if (!oObj.fine) oObj.fine = 0; + return '<span style="text-align: right; display: block;">' + parseFloat(oObj.fine).toFixed(2) + '<span>'; + }, + price: function (oData, sType, oObj) { + if (!oObj.price) oObj.price = 0; + return parseFloat(oObj.price).toFixed(2); + }, + renew: function (oData, sType, oObj) { + var content = ""; + var span_style = ""; + var span_class = ""; + + content += "<span>"; + content += "<span style='padding: 0 1em;'>" + oObj.renewals_count + "</span>"; + + if ( oObj.can_renew ) { + // Do nothing + } else if ( oObj.can_renew_error == "on_reserve" ) { + content += "<span class='renewals-disabled-no-override'>" + + "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" + oObj.biblionumber + "'>" + ON_HOLD + "</a>" + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "too_many" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "restriction" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE_RESTRICTION + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "overdue" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE_OVERDUE + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "too_soon" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE_TOO_SOON.format( oObj.can_renew_date ) + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "auto_too_soon" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE_AUTO_TOO_SOON + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "auto_too_late" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE_AUTO_TOO_LATE + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "auto_renew" ) { + content += "<span class='renewals-disabled'>" + + NOT_RENEWABLE_AUTO_RENEW + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "onsite_checkout" ) { + // Don't display something if it's an onsite checkout + } else { + content += "<span class='renewals-disabled'>" + + oObj.can_renew_error + + "</span>"; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } + + var can_force_renew = ( oObj.onsite_checkout == 0 ) && ( oObj.can_renew_error != "on_reserve" ); + var can_renew = ( oObj.renewals_remaining > 0 && !oObj.can_renew_error ); + if ( can_renew || can_force_renew ) { + content += "<span class='" + span_class + "' style='" + span_style + "'>" + + "<input type='checkbox' "; + if ( oObj.date_due_overdue && can_renew ) { + content += "checked='checked' "; + } + content += "class='renew' id='renew_" + oObj.itemnumber + "' name='renew' value='" + oObj.itemnumber +"'/>" + + "</span>"; + + content += "<span class='renewals'>(" + + RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed ) + + ")</span>"; + } + + content += "</span>"; + + return content; + }, + checkin: function (oData, sType, oObj) { + if (oObj.can_renew_error == "on_reserve") { + return "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" + oObj.biblionumber + "'>" + ON_HOLD + "</a>"; + } else { + return "<input type='checkbox' class='checkin' id='checkin_" + oObj.itemnumber + "' name='checkin' value='" + oObj.itemnumber +"'></input>"; + } + }, + export: function (oData, sType, oObj) { + var s = "<input type='checkbox' name='itemnumbers' value='" + oObj.itemnumber + "' style='visibility:hidden;' />"; + + s += "<input type='checkbox' class='export' id='export_" + oObj.biblionumber + "' name='biblionumbers' value='" + oObj.biblionumber + "' />"; + return s; + }, + patron: function (oData, sType, oObj) { + return "<a href='/cgi-bin/koha/members/moremember.pl?borrowernumber=" + oObj.borrowernumber + "'>" + + oObj.borrower.firstname + " " + oObj.borrower.surname + " (" + oObj.borrower.cardnumber + ")</a>" + } +}; + $(document).ready(function() { $.ajaxSetup ({ cache: false }); @@ -139,8 +386,6 @@ $(document).ready(function() { return false; }); - var ymd = $.datepicker.formatDate('yy-mm-dd', new Date()); - $('#issues-table').hide(); $('#issues-table-actions').hide(); $('#issues-table-load-immediately').change(function(){ @@ -174,16 +419,24 @@ $(document).ready(function() { "sProcessing": MSG_DT_LOADING_RECORDS, }, "bAutoWidth": false, - "dom": 'B<"clearfix">rt', + "bProcessing": true, + "bServerSide": ServerSideCheckoutsTables, + "sAjaxSource": "/cgi-bin/koha/svc/checkouts", + "fnServerParams": function (aoData) { + aoData.push({ "name": "borrowernumber", "value": borrowernumber }); + }, + "bPaginate": true, + "sPaginationType": "four_button", + "dom": 'B<"clearfix"><"top pager"ilp>rt', + "aaSorting": [], "aoColumns": [ { - "mDataProp": function( oObj ) { - return oObj.sort_order; - } + "mData": "issued_today", + "mRender": mRenders.groupOrder }, { - "mDataProp": function( oObj ) { - if ( oObj.issued_today ) { + "mData": function (oObj) { + if (oObj.issued_today) { return "<strong>" + TODAYS_CHECKOUTS + "</strong>"; } else { return "<strong>" + PREVIOUS_CHECKOUTS + "</strong>"; @@ -191,229 +444,57 @@ $(document).ready(function() { } }, { - "mDataProp": "date_due", - "bVisible": false, + "mData": "date_due", + "mRender": mRenders.dateDueFormatted }, { - "iDataSort": 2, // Sort on hidden unformatted date due column - "mDataProp": function( oObj ) { - var due = oObj.date_due_formatted; - - if ( oObj.date_due_overdue ) { - due = "<span class='overdue'>" + due + "</span>"; - } - - due = "<span id='date_due_" + oObj.itemnumber + "' class='date_due'>" + due + "</span>"; - - if ( oObj.lost ) { - due += "<span class='lost'>" + oObj.lost + "</span>"; - } - - if ( oObj.damaged ) { - due += "<span class='dmg'>" + oObj.damaged + "</span>"; - } - - - return due; - } + "mData": "title", + "mRender": mRenders.title }, { - "mDataProp": function ( oObj ) { - title = "<span class='strong'><a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber=" - + oObj.biblionumber - + "'>" - + oObj.title; - - $.each(oObj.subtitle, function( index, value ) { - title += " " + value.subfield; - }); - - if ( oObj.enumchron ) { - title += " (" + oObj.enumchron + ")"; - } - - title += "</a></span>"; - - if ( oObj.author ) { - title += " " + BY.replace( "_AUTHOR_", " " + oObj.author ); - } - - if ( oObj.itemnotes ) { - var span_class = "text-muted"; - title += " - <span class='" + span_class + "'>" + oObj.itemnotes + "</span>" - } - - if ( oObj.itemnotes_nonpublic ) { - var span_class = "text-danger"; - title += " - <span class='" + span_class + "'>" + oObj.itemnotes_nonpublic + "</span>" - } - - var onsite_checkout = ''; - if ( oObj.onsite_checkout == 1 ) { - onsite_checkout += " <span class='onsite_checkout'>(" + INHOUSE_USE + ")</span>"; - } - - title += " " - + "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=" - + oObj.biblionumber - + "&itemnumber=" - + oObj.itemnumber - + "#" - + oObj.itemnumber - + "'>" - + oObj.barcode - + "</a>" - + onsite_checkout; - - return title; - }, - "sType": "anti-the" + "mData": "itemtype", + "mRender": mRenders.itemtype }, - { "mDataProp": "itemtype_description" }, - { "mDataProp": "location" }, - { "mDataProp": "homebranch" }, - { "mDataProp": "issuedate_formatted" }, - { "mDataProp": "branchname" }, - { "mDataProp": "itemcallnumber" }, + { "mData": "location" }, + { "mData": "homebranch" }, { - "mDataProp": function ( oObj ) { - if ( ! oObj.charge ) oObj.charge = 0; - return '<span style="text-align: right; display: block;">' + parseFloat(oObj.charge).toFixed(2) + '<span>'; - } + "mData": "issuedate", + "mRender": mRenders.issuedate }, { - "mDataProp": function ( oObj ) { - if ( ! oObj.fine ) oObj.fine = 0; - return '<span style="text-align: right; display: block;">' + parseFloat(oObj.fine).toFixed(2) + '<span>'; - } + "mData": "branchcode", + "mRender": mRenders.branch }, + { "mData": "itemcallnumber" }, { - "mDataProp": function ( oObj ) { - if ( ! oObj.price ) oObj.price = 0; - return '<span style="text-align: right; display: block;">' + parseFloat(oObj.price).toFixed(2) + '<span>'; - } + "mData": "charge", + "mRender": mRenders.charge, + "bSortable": ServerSideCheckoutsTables ? false : true + }, + { + "mData": "fine", + "mRender": mRenders.fine, + "bSortable": ServerSideCheckoutsTables ? false : true + }, + { + "mData": "replacementprice", + "mRender": mRenders.price, + "bSortable": ServerSideCheckoutsTables ? false : true }, { - "bSortable": false, "bVisible": AllowCirculate ? true : false, - "mDataProp": function ( oObj ) { - var content = ""; - var span_style = ""; - var span_class = ""; - - content += "<span>"; - content += "<span style='padding: 0 1em;'>" + oObj.renewals_count + "</span>"; - - if ( oObj.can_renew ) { - // Do nothing - } else if ( oObj.can_renew_error == "on_reserve" ) { - content += "<span class='renewals-disabled-no-override'>" - + "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" + oObj.biblionumber + "'>" + ON_HOLD + "</a>" - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "too_many" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "restriction" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE_RESTRICTION - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "overdue" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE_OVERDUE - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "too_soon" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE_TOO_SOON.format( oObj.can_renew_date ) - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "auto_too_soon" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE_AUTO_TOO_SOON - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "auto_too_late" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE_AUTO_TOO_LATE - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "auto_renew" ) { - content += "<span class='renewals-disabled'>" - + NOT_RENEWABLE_AUTO_RENEW - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "onsite_checkout" ) { - // Don't display something if it's an onsite checkout - } else { - content += "<span class='renewals-disabled'>" - + oObj.can_renew_error - + "</span>"; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } - - var can_force_renew = ( oObj.onsite_checkout == 0 ) && ( oObj.can_renew_error != "on_reserve" ); - var can_renew = ( oObj.renewals_remaining > 0 && !oObj.can_renew_error ); - if ( can_renew || can_force_renew ) { - content += "<span class='" + span_class + "' style='" + span_style + "'>" - + "<input type='checkbox' "; - if ( oObj.date_due_overdue && can_renew ) { - content += "checked='checked' "; - } - content += "class='renew' id='renew_" + oObj.itemnumber + "' name='renew' value='" + oObj.itemnumber +"'/>" - + "</span>"; - - content += "<span class='renewals'>(" - + RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed ) - + ")</span>"; - } - - content += "</span>"; - - return content; - } + "mRender": mRenders.renew, + "bSortable": false }, { - "bSortable": false, "bVisible": AllowCirculate ? true : false, - "mDataProp": function ( oObj ) { - if ( oObj.can_renew_error == "on_reserve" ) { - return "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber=" + oObj.biblionumber + "'>" + ON_HOLD + "</a>"; - } else { - return "<input type='checkbox' class='checkin' id='checkin_" + oObj.itemnumber + "' name='checkin' value='" + oObj.itemnumber +"'></input>"; - } - } + "mRender": mRenders.checkin, + "bSortable": false }, { "bVisible": exports_enabled ? true : false, "bSortable": false, - "mDataProp": function ( oObj ) { - var s = "<input type='checkbox' name='itemnumbers' value='" + oObj.itemnumber + "' style='visibility:hidden;' />"; - - s += "<input type='checkbox' class='export' id='export_" + oObj.biblionumber + "' name='biblionumbers' value='" + oObj.biblionumber + "' />"; - return s; - } + "mRender": mRenders.export } ], "fnFooterCallback": function ( nRow, aaData, iStart, iEnd, aiDisplay ) { @@ -428,39 +509,30 @@ $(document).ready(function() { $("#totaldue").html(total_charge.toFixed(2)); $("#totalfine").html(total_fine.toFixed(2)); $("#totalprice").html(total_price.toFixed(2)); - }, - "bPaginate": false, - "bProcessing": true, - "bServerSide": false, - "sAjaxSource": '/cgi-bin/koha/svc/checkouts', - "fnServerData": function ( sSource, aoData, fnCallback ) { - aoData.push( { "name": "borrowernumber", "value": borrowernumber } ); - - $.getJSON( sSource, aoData, function (json) { - fnCallback(json) - } ); - }, - "fnInitComplete": function(oSettings) { - // Disable rowGrouping plugin after first use - // so any sorting on the table doesn't use it - var oSettings = issuesTable.fnSettings(); - - for (f = 0; f < oSettings.aoDrawCallback.length; f++) { - if (oSettings.aoDrawCallback[f].sName == 'fnRowGrouping') { - oSettings.aoDrawCallback.splice(f, 1); - break; - } - } - - oSettings.aaSortingFixed = null; - }, - }, columns_settings).rowGrouping( - { - iGroupingColumnIndex: 1, - iGroupingOrderByColumnIndex: 0, - sGroupingColumnSortDirection: "asc" } - ); + }, columns_settings); + + enableGrouping(issuesTable); + + var groupingSwitch = $('<input type="checkbox">') + .attr('id', 'issues-table-row-grouping-switch') + .attr('checked', 'checked') + .css('vertical-align', 'middle') + .change(function() { + if ($(this).is(':checked')) { + enableGrouping(issuesTable); + } else { + disableGrouping(issuesTable); + } + }); + $('<div></div>') + .css('float', 'left') + .css('padding', '0.3em 0.5em') + .css('line-height', '1.9em') + .append(groupingSwitch) + .append(' ') + .append('<label for="issues-table-row-grouping-switch">' + GROUP_TODAYS_CHECKOUTS + '</label>') + .appendTo('#issues-table_wrapper .top.pager'); if ( $("#issues-table").length ) { $("#issues-table_processing").position({ @@ -474,129 +546,60 @@ $(document).ready(function() { var relativesIssuesTable; $("#relatives-issues-tab").click( function() { if ( ! relativesIssuesTable ) { - relativesIssuesTable = $("#relatives-issues-table").dataTable({ + relativesIssuesTable = KohaTable("#relatives-issues-table", { "bAutoWidth": false, - "sDom": "rt", - "aaSorting": [], + "sDom": 'C<"top pager"ilp>rt', + "aaSorting": [[4, "desc"]], + "bProcessing": true, + "bServerSide": ServerSideCheckoutsTables, + "sAjaxSource": "/cgi-bin/koha/svc/checkouts", + "fnServerParams": function (aoData) { + for (i in relatives_borrowernumbers) { + var borrowernumber = relatives_borrowernumbers[i]; + aoData.push({ "name": "borrowernumber", "value": borrowernumber }); + } + }, + "bPaginate": true, + "sPaginationType": "four_button", "aoColumns": [ { - "mDataProp": "date_due", - "bVisible": false, + "mData": "date_due", + "mRender": mRenders.dateDueFormatted }, { - "iDataSort": 1, // Sort on hidden unformatted date due column - "mDataProp": function( oObj ) { - var today = new Date(); - var due = new Date( oObj.date_due ); - if ( today > due ) { - return "<span class='overdue'>" + oObj.date_due_formatted + "</span>"; - } else { - return oObj.date_due_formatted; - } - } + "mData": "title", + "mRender": mRenders.title, }, + { "mData": "itemtype" }, + { "mData": "location" }, { - "mDataProp": function ( oObj ) { - title = "<span class='strong'><a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber=" - + oObj.biblionumber - + "'>" - + oObj.title; - - $.each(oObj.subtitle, function( index, value ) { - title += " " + value.subfield; - }); - - if ( oObj.enumchron ) { - title += " (" + oObj.enumchron + ")"; - } - - title += "</a></span>"; - - if ( oObj.author ) { - title += " " + BY.replace( "_AUTHOR_", " " + oObj.author ); - } - - if ( oObj.itemnotes ) { - var span_class = ""; - if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) { - span_class = "circ-hlt"; - } - title += " - <span class='" + span_class + "'>" + oObj.itemnotes + "</span>" - } - - if ( oObj.itemnotes_nonpublic ) { - var span_class = ""; - if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) { - span_class = "circ-hlt"; - } - title += " - <span class='" + span_class + "'>" + oObj.itemnotes_nonpublic + "</span>" - } - - var onsite_checkout = ''; - if ( oObj.onsite_checkout == 1 ) { - onsite_checkout += " <span class='onsite_checkout'>(" + INHOUSE_USE + ")</span>"; - } - - title += " " - + "<a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=" - + oObj.biblionumber - + "&itemnumber=" - + oObj.itemnumber - + "#" - + oObj.itemnumber - + "'>" - + oObj.barcode - + "</a>" - + onsite_checkout; - - return title; - }, - "sType": "anti-the" + "mData": "issuedate", + "mRender": mRenders.issuedate }, - { "mDataProp": "itemtype" }, - { "mDataProp": "location" }, - { "mDataProp": "issuedate_formatted" }, - { "mDataProp": "branchname" }, - { "mDataProp": "itemcallnumber" }, + { "mData": "branchname" }, + { "mData": "itemcallnumber" }, { - "mDataProp": function ( oObj ) { - if ( ! oObj.charge ) oObj.charge = 0; - return parseFloat(oObj.charge).toFixed(2); - } + "mData": "charge", + "mRender": mRenders.charge, + "bSortable": ServerSideCheckoutsTables ? false : true }, { - "mDataProp": function ( oObj ) { - if ( ! oObj.fine ) oObj.fine = 0; - return parseFloat(oObj.fine).toFixed(2); - } + "mData": "fine", + "mRender": mRenders.fine, + "bSortable": ServerSideCheckoutsTables ? false : true }, { - "mDataProp": function ( oObj ) { - if ( ! oObj.price ) oObj.price = 0; - return parseFloat(oObj.price).toFixed(2); - } + "mData": "replacementprice", + "mRender": mRenders.price, + "bSortable": ServerSideCheckoutsTables ? false : true }, { - "mDataProp": function( oObj ) { - return "<a href='/cgi-bin/koha/members/moremember.pl?borrowernumber=" + oObj.borrowernumber + "'>" - + oObj.borrower.firstname + " " + oObj.borrower.surname + " (" + oObj.borrower.cardnumber + ")</a>" - } - }, - ], - "bPaginate": false, - "bProcessing": true, - "bServerSide": false, - "sAjaxSource": '/cgi-bin/koha/svc/checkouts', - "fnServerData": function ( sSource, aoData, fnCallback ) { - $.each(relatives_borrowernumbers, function( index, value ) { - aoData.push( { "name": "borrowernumber", "value": value } ); - }); - - $.getJSON( sSource, aoData, function (json) { - fnCallback(json) - } ); - }, - }); + "mData": "borrowernumber", + "mRender": mRenders.patron, + "bSortable": ServerSideCheckoutsTables ? false : true + } + ] + }, relatives_columns_settings); } }); @@ -616,4 +619,6 @@ $(document).ready(function() { } } ).prop('checked', false); } - }); +}); + +})(jQuery); diff --git a/svc/checkouts b/svc/checkouts index 0b0d329..c7c8a6a 100755 --- a/svc/checkouts +++ b/svc/checkouts @@ -17,8 +17,7 @@ # with Koha; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use strict; -use warnings; +use Modern::Perl; use CGI; use JSON qw(to_json); @@ -28,6 +27,7 @@ use C4::Biblio qw(GetMarcBiblio GetFrameworkCode GetRecordValue ); use C4::Circulation qw(GetIssuingCharges CanBookBeRenewed GetRenewCount GetSoonestRenewDate); use C4::Overdues qw(GetFine); use C4::Context; +use C4::Utils::DataTables; use Koha::AuthorisedValues; use Koha::DateUtils; @@ -45,26 +45,33 @@ unless (haspermission($userid, { circulate => 'circulate_remaining_permissions' exit 0; } -my @sort_columns = qw/date_due title itype issuedate branchcode itemcallnumber/; - my @borrowernumber = $input->multi_param('borrowernumber'); -my $offset = $input->param('iDisplayStart'); -my $results_per_page = $input->param('iDisplayLength') || -1; - -my $sorting_column = $input->param('iSortCol_0') || q{}; -$sorting_column = ( $sorting_column && $sort_columns[$sorting_column] ) ? $sort_columns[$sorting_column] : 'issuedate'; - -my $sorting_direction = $input->param('sSortDir_0') || q{}; -$sorting_direction = $sorting_direction eq 'asc' ? 'asc' : 'desc'; - -$results_per_page = undef if ( $results_per_page == -1 ); - -binmode STDOUT, ":encoding(UTF-8)"; -print $input->header( -type => 'text/plain', -charset => 'UTF-8' ); +my $offset = $input->param('iDisplayStart'); +my $count = $input->param('iDisplayLength'); + +my %params = dt_get_params($input); +my $orderby = dt_build_orderby(\%params); + +my $todaysIssuesDefaultSortOrder = + C4::Context->preference('todaysIssuesDefaultSortOrder'); +my $previousIssuesDefaultSortOrder = + C4::Context->preference('previousIssuesDefaultSortOrder'); + +# Default sort when user hasn't clicked on a column header yet. +# When row grouping is enabled, DataTables ask to sort on issued_today column, +# so we need to treat this case the same as the "no sorting" case +if (!$orderby || $orderby =~ m/ORDER BY issued_today desc\s*$/) { + $orderby = qq{ + ORDER BY + issued_today DESC, + CASE WHEN issued_today = 0 THEN date_due END $previousIssuesDefaultSortOrder, + CASE WHEN issued_today != 0 THEN date_due END $todaysIssuesDefaultSortOrder + }; +} my @parameters; my $sql = ' - SELECT + SELECT SQL_CALC_FOUND_ROWS issuedate, date_due, date_due < now() as date_due_overdue, @@ -100,7 +107,7 @@ my $sql = ' location, items.enumchron, - DATEDIFF( issuedate, CURRENT_DATE() ) AS not_issued_today + DATEDIFF( issuedate, CURRENT_DATE() ) = 0 AS issued_today FROM issues LEFT JOIN items USING ( itemnumber ) LEFT JOIN biblio USING ( biblionumber ) @@ -108,27 +115,33 @@ my $sql = ' LEFT JOIN borrowers USING ( borrowernumber ) LEFT JOIN branches ON ( issues.branchcode = branches.branchcode ) LEFT JOIN branches branches2 ON ( items.homebranch = branches2.branchcode ) - WHERE borrowernumber + WHERE '; +my $borrowernumber_sql = 'borrowernumber'; if ( @borrowernumber == 1 ) { - $sql .= '= ?'; -} -else { - $sql .= ' IN (' . join( ',', ('?') x @borrowernumber ) . ') '; + $borrowernumber_sql .= ' = ?'; +} else { + $borrowernumber_sql .= ' IN (' . join( ',', ('?') x @borrowernumber ) . ') '; } +$sql .= $borrowernumber_sql; push( @parameters, @borrowernumber ); -$sql .= " ORDER BY $sorting_column $sorting_direction "; +$sql .= " $orderby"; + +if ($count && $count > 0) { + $sql .= " LIMIT ?,?"; + push @parameters, $offset, $count; +} my $dbh = C4::Context->dbh(); my $sth = $dbh->prepare($sql); $sth->execute(@parameters); +my ($iTotalDisplayRecords) = $dbh->selectrow_array('SELECT FOUND_ROWS()'); my $item_level_itypes = C4::Context->preference('item-level_itypes'); -my @checkouts_today; -my @checkouts_previous; +my @checkouts; while ( my $c = $sth->fetchrow_hashref() ) { my ($charge) = GetIssuingCharges( $c->{itemnumber}, $c->{borrowernumber} ); my $fine = GetFine( $c->{itemnumber}, $c->{borrowernumber} ); @@ -217,36 +230,25 @@ while ( my $c = $sth->fetchrow_hashref() ) { firstname => $c->{firstname}, cardnumber => $c->{cardnumber}, }, - issued_today => !$c->{not_issued_today}, + issued_today => $c->{issued_today} ? JSON::true : JSON::false, }; - if ( $c->{not_issued_today} ) { - push( @checkouts_previous, $checkout ); - } - else { - push( @checkouts_today, $checkout ); - } + push @checkouts, $checkout; } +binmode STDOUT, ":encoding(UTF-8)"; +print $input->header( -type => 'application/json', -charset => 'UTF-8' ); -@checkouts_today = sort { $a->{timestamp} cmp $b->{timestamp} } @checkouts_today; -@checkouts_today = reverse(@checkouts_today) - unless ( C4::Context->preference('todaysIssuesDefaultSortOrder') eq 'desc' ); - -@checkouts_previous = sort { $a->{date_due} cmp $b->{date_due} } @checkouts_previous; -@checkouts_previous = reverse(@checkouts_previous) - if ( C4::Context->preference('previousIssuesDefaultSortOrder') eq 'desc' ); - -my @checkouts = ( @checkouts_today, @checkouts_previous ); - -my $i = 1; -map { $_->{sort_order} = $i++ } @checkouts; - +my $sEcho = $input->param('sEcho'); +my ($iTotalRecords) = $dbh->selectrow_array( + "SELECT COUNT(*) FROM issues WHERE $borrowernumber_sql", undef, + @borrowernumber); -my $data; -$data->{'iTotalRecords'} = scalar @checkouts; -$data->{'iTotalDisplayRecords'} = scalar @checkouts; -$data->{'sEcho'} = $input->param('sEcho') || undef; -$data->{'aaData'} = \@checkouts; +my $response = { + iTotalRecords => $iTotalRecords, + iTotalDisplayRecords => $iTotalDisplayRecords, + sEcho => $sEcho, + aaData => \@checkouts, +}; -print to_json($data); +print to_json($response); -- 2.7.4