From ae659d6001a7a5e0caf31d52abc662cdd6b852ab Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Thu, 10 Jul 2014 10:26:57 -0400 Subject: [PATCH] Bug 12550 - Add ability to delay loading of patron's checkouts in circulation and patron details Now that a patron's checkouts are loaded asynchronously, we can further improve Koha's performance by not loading the checkouts table when it is not needed. For example, if a librarian is checking out 5 items to a patron, we really don't need to load the table during the scanning of items 1 to 4, just for item 5. Another example would be browsing to the patron details table in order to change a patron's password. I propose we add two separate system preferences to add a timer for loading the patron's checkouts table, one for circulation and one for patron details. That way a library can choose to have enough delay so that most librarians can scan items repeatedly without loading the checkouts table, and can have the patron details table load more quickly if they wish. In addition, setting one or both timeouts to 0 will cause the system to act as it currently does. Test Plan: 1) Apply this patch 2) Run updatedatabase.pl 3) Test circulation and patron details, no changes should be noted 4) Set CheckoutsTableSecondsToLoadCirculation and CheckoutsTableSecondsToLoadPatronDetails to some number of seconds. 5) Test again, note tables load after the set number of seconds 6) Test the "Load now" button, note it triggers the table to load immediately --- installer/data/mysql/sysprefs.sql | 2 + installer/data/mysql/updatedatabase.pl | 12 + koha-tmpl/intranet-tmpl/prog/en/js/checkouts.js | 417 +++++++++++--------- .../prog/en/modules/admin/preferences/patrons.pref | 8 + .../prog/en/modules/circ/circulation.tt | 13 +- .../prog/en/modules/members/moremember.tt | 67 ++-- 6 files changed, 300 insertions(+), 219 deletions(-) diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index ed427ea..6ece2b7 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -82,6 +82,8 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('CatalogModuleRelink','0',NULL,'If OFF the linker will never replace the authids that are set in the cataloging module.','YesNo'), ('CataloguingLog','1',NULL,'If ON, log edit/create/delete actions on bibliographic data. WARNING: this feature is very resource consuming.','YesNo'), ('checkdigit','none','none|katipo','If ON, enable checks on patron cardnumber: none or \"Katipo\" style checks','Choice'), +('CheckoutsTableSecondsToLoadCirculation', '0', NULL, 'Number of seconds to delay the loading of the checkouts table for circulation.', 'Integer'), +('CheckoutsTableSecondsToLoadPatronDetails', '0', NULL, 'Number of seconds to delay the loading of the checkouts table for patron details.', 'Integer'), ('CircAutocompl','1',NULL,'If ON, autocompletion is enabled for the Circulation input','YesNo'), ('CircAutoPrintQuickSlip','qslip',NULL,'Choose what should happen when an empty barcode field is submitted in circulation: Display a print quick slip window, Display a print slip window or Clear the screen.','Choice'), ('CircControl','ItemHomeLibrary','PickupLibrary|PatronLibrary|ItemHomeLibrary','Specify the agency that controls the circulation and fines policy','Choice'), diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 1030454..83248ef 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -8570,6 +8570,18 @@ if ( CheckVersion($DBversion) ) { SetVersion($DBversion); } +$DBversion = "3.17.00.XXX"; +if ( CheckVersion($DBversion) ) { + $dbh->do(q{ + INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES + ('CheckoutsTableSecondsToLoadCirculation', '0', NULL, 'Number of seconds to delay the loading of the checkouts table for circulation.', 'Integer'), + ('CheckoutsTableSecondsToLoadPatronDetails', '0', NULL, 'Number of seconds to delay the loading of the checkouts table for patron details.', 'Integer'); + }); + + print "Upgrade to $DBversion done (Bug 12550 - Add ability to delay loading of patron's checkouts in circulation and patron details)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/checkouts.js b/koha-tmpl/intranet-tmpl/prog/en/js/checkouts.js index 6efe9f9..cd12fae 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/js/checkouts.js +++ b/koha-tmpl/intranet-tmpl/prog/en/js/checkouts.js @@ -126,219 +126,254 @@ $(document).ready(function() { var ymd = $.datepicker.formatDate('yy-mm-dd', new Date()); - var issuesTable; - var drawn = 0; - issuesTable = $("#issues-table").dataTable({ - "oLanguage": { - "sEmptyTable" : MSG_DT_LOADING_RECORDS, - }, - "bAutoWidth": false, - "sDom": "<'row-fluid'<'span6'><'span6'>r>t<'row-fluid'>t", - "aoColumns": [ - { - "mDataProp": function( oObj ) { - if ( oObj.issued_today ) { - return "" + TODAYS_CHECKOUTS + ""; - } else { - return "" + PREVIOUS_CHECKOUTS + ""; - } - } - }, - { - "mDataProp": "date_due", - "bVisible": false, + // Don't bother loading table if there are no checkouts to show + if ( issues_count ) { + var issuesTable; + + if ( checkouts_table_seconds_to_load ) { + $('#issues-table').hide(); + $('#issues-table-actions').hide(); + $('#issues-table-load-now-button').click(function(){ + LoadIssuesTableNow(); + return false; + }); + var loadIssuesTableInterval = setInterval(function(){ CountdownIssuesTableLoading(); }, 1000); + } else { + $('#issues-table-loading-message').hide(); + LoadIssuesTable(); + } + } + + function CountdownIssuesTableLoading(){ + if ( checkouts_table_seconds_to_load ) { + $('#issues-table-loading-count').text( checkouts_table_seconds_to_load ); + checkouts_table_seconds_to_load--; + } else { + LoadIssuesTableNow(); + } + } + + function LoadIssuesTableNow() { + clearInterval(loadIssuesTableInterval); + $('#issues-table-loading-message').hide(); + $('#issues-table').show(); + $('#issues-table-actions').show(); + LoadIssuesTable(); + } + + function LoadIssuesTable() { + issuesTable = $("#issues-table").dataTable({ + "oLanguage": { + "sEmptyTable" : MSG_DT_LOADING_RECORDS, }, - { - "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 "" + oObj.date_due_formatted + ""; - } else { - return oObj.date_due_formatted; + "bAutoWidth": false, + "sDom": "<'row-fluid'<'span6'><'span6'>r>t<'row-fluid'>t", + "aoColumns": [ + { + "mDataProp": function( oObj ) { + if ( oObj.issued_today ) { + return "" + TODAYS_CHECKOUTS + ""; + } else { + return "" + PREVIOUS_CHECKOUTS + ""; + } } - } - }, - { - "mDataProp": function ( oObj ) { - title = "" - + oObj.title; - - $.each(oObj.subtitle, function( index, value ) { - title += " " + value.subfield; - }); + }, + { + "mDataProp": "date_due", + "bVisible": false, + }, + { + "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 "" + oObj.date_due_formatted + ""; + } else { + return oObj.date_due_formatted; + } + } + }, + { + "mDataProp": function ( oObj ) { + title = "" + + oObj.title; - title += ""; + $.each(oObj.subtitle, function( index, value ) { + title += " " + value.subfield; + }); - if ( oObj.author ) { - title += " " + BY.replace( "_AUTHOR_", " " + oObj.author ); - } + title += ""; - if ( oObj.itemnotes ) { - var span_class = ""; - if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) { - span_class = "circ-hlt"; + if ( oObj.author ) { + title += " " + BY.replace( "_AUTHOR_", " " + oObj.author ); } - title += " - " + oObj.itemnotes + "" - } - title += " " - + "" - + oObj.barcode - + ""; - - return title; - } - }, - { "mDataProp": "itemtype" }, - { "mDataProp": "issuedate_formatted" }, - { "mDataProp": "branchname" }, - { "mDataProp": "itemcallnumber" }, - { - "mDataProp": function ( oObj ) { - if ( ! oObj.charge ) oObj.charge = 0; - return parseFloat(oObj.charge).toFixed(2); - } - }, - { - "mDataProp": function ( oObj ) { - if ( ! oObj.price ) oObj.price = 0; - return parseFloat(oObj.price).toFixed(2); - } - }, - { - "bSortable": false, - "mDataProp": function ( oObj ) { - var content = ""; - var span_style = ""; - var span_class = ""; - - content += ""; - content += "" + oObj.renewals_count + ""; - - if ( oObj.can_renew ) { - // Do nothing - } else if ( oObj.can_renew_error == "on_reserve" ) { - content += "" - + "" + ON_HOLD + "" - + ""; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "too_many" ) { - content += "" - + NOT_RENEWABLE - + ""; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else if ( oObj.can_renew_error == "too_soon" ) { - content += "" - + NOT_RENEWABLE_TOO_SOON.format( oObj.can_renew_date ) - + ""; - - span_style = "display: none"; - span_class = "renewals-allowed"; - } else { - content += "" - + oObj.can_renew_error - + ""; + if ( oObj.itemnotes ) { + var span_class = ""; + if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) { + span_class = "circ-hlt"; + } + title += " - " + oObj.itemnotes + "" + } - span_style = "display: none"; - span_class = "renewals-allowed"; + title += " " + + "" + + oObj.barcode + + ""; + + return title; + } + }, + { "mDataProp": "itemtype" }, + { "mDataProp": "issuedate_formatted" }, + { "mDataProp": "branchname" }, + { "mDataProp": "itemcallnumber" }, + { + "mDataProp": function ( oObj ) { + if ( ! oObj.charge ) oObj.charge = 0; + return parseFloat(oObj.charge).toFixed(2); + } + }, + { + "mDataProp": function ( oObj ) { + if ( ! oObj.price ) oObj.price = 0; + return parseFloat(oObj.price).toFixed(2); } + }, + { + "bSortable": false, + "mDataProp": function ( oObj ) { + var content = ""; + var span_style = ""; + var span_class = ""; - content += "" - + "" - + ""; + content += ""; + content += "" + oObj.renewals_count + ""; + + if ( oObj.can_renew ) { + // Do nothing + } else if ( oObj.can_renew_error == "on_reserve" ) { + content += "" + + "" + ON_HOLD + "" + + ""; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "too_many" ) { + content += "" + + NOT_RENEWABLE + + ""; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else if ( oObj.can_renew_error == "too_soon" ) { + content += "" + + NOT_RENEWABLE_TOO_SOON.format( oObj.can_renew_date ) + + ""; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } else { + content += "" + + oObj.can_renew_error + + ""; + + span_style = "display: none"; + span_class = "renewals-allowed"; + } - if ( oObj.renewals_remaining ) { - content += "(" - + RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed ) - + ")"; - } + content += "" + + "" + + ""; - content += ""; + if ( oObj.renewals_remaining ) { + content += "(" + + RENEWALS_REMAINING.format( oObj.renewals_remaining, oObj.renewals_allowed ) + + ")"; + } + + content += ""; - return content; + return content; + } + }, + { + "bSortable": false, + "mDataProp": function ( oObj ) { + if ( oObj.can_renew_error == "on_reserve" ) { + return "" + ON_HOLD + ""; + } else { + return ""; + } + } + }, + { + "bVisible": exports_enabled ? true : false, + "bSortable": false, + "mDataProp": function ( oObj ) { + return ""; + } + } + ], + "fnFooterCallback": function ( nRow, aaData, iStart, iEnd, aiDisplay ) { + var total_charge = 0; + var total_price = 0; + for ( var i=0; i < aaData.length; i++ ) { + total_charge += aaData[i]['charge'] * 1; + total_price += aaData[i]['price'] * 1; } + var nCells = nRow.getElementsByTagName('td'); + nCells[1].innerHTML = total_charge.toFixed(2); + nCells[2].innerHTML = total_price.toFixed(2); }, - { - "bSortable": false, - "mDataProp": function ( oObj ) { - if ( oObj.can_renew_error == "on_reserve" ) { - return "" + ON_HOLD + ""; - } else { - return ""; + "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; }, + }).rowGrouping( { - "bVisible": exports_enabled ? true : false, - "bSortable": false, - "mDataProp": function ( oObj ) { - return ""; - } - } - ], - "fnFooterCallback": function ( nRow, aaData, iStart, iEnd, aiDisplay ) { - var total_charge = 0; - var total_price = 0; - for ( var i=0; i < aaData.length; i++ ) { - total_charge += aaData[i]['charge'] * 1; - total_price += aaData[i]['price'] * 1; - } - var nCells = nRow.getElementsByTagName('td'); - nCells[1].innerHTML = total_charge.toFixed(2); - nCells[2].innerHTML = 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; - } + iGroupingOrderByColumnIndex: 0, + sGroupingColumnSortDirection: "desc" } + ); - oSettings.aaSortingFixed = null; - }, - }).rowGrouping( - { - iGroupingOrderByColumnIndex: 0, - sGroupingColumnSortDirection: "desc" + if ( $("#issues-table").length ) { + $("#issues-table_processing").position({ + of: $( "#issues-table" ), + collision: "none" + }); } - ); - - if ( $("#issues-table").length ) { - $("#issues-table_processing").position({ - of: $( "#issues-table" ), - collision: "none" - }); } // Don't load relatives' issues table unless it is clicked on diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref index 2469431..d6a4204 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref @@ -138,3 +138,11 @@ Patrons: - pref: CardnumberLength - "characters long. The length can be a single number to specify an exact length, a range separated by a comma (i.e., 'Min,Max'), or a maximum with no minimum (i.e., ',Max')." - "If 'cardnumber' is included in the BorrowerMandatoryField list, the minimum length, if not specified here, defaults to one." + - + - Wait + - pref: CheckoutsTableSecondsToLoadCirculation + class: integer + - seconds before loading the patron's checkouts table when checking out items to a patron and + - pref: CheckoutsTableSecondsToLoadPatronDetails + class: integer + - seconds when viewing a patron's details. 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 d573ba4..a56f0cf 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -1,3 +1,4 @@ +[% USE Koha %] [% USE Branches %] [% USE KohaDates %] [% IF ( export_remove_fields OR export_with_csv_profile ) %] @@ -33,6 +34,9 @@ var borrowernumber = "[% borrowernumber %]"; var branchcode = "[% branch %]"; var exports_enabled = "[% exports_enabled %]"; var AllowRenewalLimitOverride = [% CAN_user_circulate_override_renewals && AllowRenewalLimitOverride %]; +var issues_count = [% issuecount %]; +[%- SET CheckoutsTableSecondsToLoad = Koha.Preference('CheckoutsTableSecondsToLoadCirculation') -%] +var checkouts_table_seconds_to_load = [% CheckoutsTableSecondsToLoad ? CheckoutsTableSecondsToLoad - 1 : 0 %]; var relatives_borrowernumbers = new Array(); [% FOREACH b IN relatives_borrowernumbers %] relatives_borrowernumbers.push("[% b %]"); @@ -680,6 +684,13 @@ No patron matched [% message %]
[% IF ( issuecount ) %] +
+

+ Load now + Loading in [% Koha.Preference('CheckoutsTableSecondsToLoadCirculation') %] seconds... +

+
+ @@ -701,7 +712,7 @@ No patron matched [% message %] [% INCLUDE 'checkouts-table-footer.inc' %]
-
+
[% IF ( CAN_user_circulate_override_renewals ) %] [% IF ( AllowRenewalLimitOverride ) %] 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 d6b95f8..465a719 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -1,3 +1,4 @@ +[% USE Koha %] [% USE KohaDates %] [% INCLUDE 'doc-head-open.inc' %] Koha › Patrons › @@ -27,6 +28,9 @@ var borrowernumber = "[% borrowernumber %]"; var branchcode = "[% branch %]"; var exports_enabled = "[% exports_enabled %]"; var AllowRenewalLimitOverride = [% CAN_user_circulate_override_renewals && AllowRenewalLimitOverride %]; +var issues_count = [% issuecount %]; +[%- SET CheckoutsTableSecondsToLoad = Koha.Preference('CheckoutsTableSecondsToLoadPatronDetails') -%] +var checkouts_table_seconds_to_load = [% CheckoutsTableSecondsToLoad ? CheckoutsTableSecondsToLoad - 1 : 0 %]; var relatives_borrowernumbers = new Array(); [% FOREACH b IN relatives_borrowernumbers %] relatives_borrowernumbers.push("[% b %]"); @@ -408,6 +412,13 @@ function validate1(date) { <div id="checkouts"> [% IF ( issuecount ) %] + <div id="issues-table-loading-message"> + <p> + <a id="issues-table-load-now-button" href="#" class="btn"><i class="icon-download"></i> Load now</a> + Loading in <span id="issues-table-loading-count">[% Koha.Preference('CheckoutsTableSecondsToLoadPatronDetails') %]</span> seconds... + </p> + </div> + <form name="issues" action="/cgi-bin/koha/tools/export.pl" method="post" class="checkboxed"> <table id="issues-table" style="width: 100% !Important;"> <thead> @@ -431,36 +442,38 @@ function validate1(date) { </table> [% IF ( issuecount ) %] - <fieldset class="action"> - [% IF ( CAN_user_circulate_override_renewals ) %] - [% IF ( AllowRenewalLimitOverride ) %] - <label for="override_limit">Override renewal limit:</label> - <input type="checkbox" name="override_limit" id="override_limit" value="1" /> - [% END %] - [% END %] - <button class="btn" id="RenewCheckinChecked"><i class="icon-check"></i> Renew or return checked items</button> - <button class="btn" id="RenewAll"><i class="icon-book"></i> Renew all</button> - </fieldset> - - [% IF ( exports_enabled ) %] - <fieldset> - <label for="export_formats"><b>Export checkouts using format:</b></label> - <select name="export_formats" id="export_formats"> - <option value="iso2709_995">ISO2709 with items</option> - <option value="iso2709">ISO2709 without items</option> - [% IF ( export_with_csv_profile ) %] - <option value="csv">CSV</option> + <div id="issues-table-actions"> + <fieldset class="action"> + [% IF ( CAN_user_circulate_override_renewals ) %] + [% IF ( AllowRenewalLimitOverride ) %] + <label for="override_limit">Override renewal limit:</label> + <input type="checkbox" name="override_limit" id="override_limit" value="1" /> [% END %] - </select> - - <label for="export_remove_fields">Don't export fields:</label> <input type="text" id="export_remove_fields" name="export_remove_fields" value="[% export_remove_fields %]" title="Use for iso2709 exports" /> - <input type="hidden" name="op" value="export" /> - <input type="hidden" id="export_format" name="format" value="iso2709" /> - <input type="hidden" id="dont_export_item" name="dont_export_item" value="0" /> - <input type="hidden" id="record_type" name="record_type" value="bibs" /> - <button class="btn btn-small" id="export_submit"><i class="icon-download-alt"></i> Export</button> + [% END %] + <button class="btn" id="RenewCheckinChecked"><i class="icon-check"></i> Renew or return checked items</button> + <button class="btn" id="RenewAll"><i class="icon-book"></i> Renew all</button> </fieldset> - [% END %] + + [% IF ( exports_enabled ) %] + <fieldset> + <label for="export_formats"><b>Export checkouts using format:</b></label> + <select name="export_formats" id="export_formats"> + <option value="iso2709_995">ISO2709 with items</option> + <option value="iso2709">ISO2709 without items</option> + [% IF ( export_with_csv_profile ) %] + <option value="csv">CSV</option> + [% END %] + </select> + + <label for="export_remove_fields">Don't export fields:</label> <input type="text" id="export_remove_fields" name="export_remove_fields" value="[% export_remove_fields %]" title="Use for iso2709 exports" /> + <input type="hidden" name="op" value="export" /> + <input type="hidden" id="export_format" name="format" value="iso2709" /> + <input type="hidden" id="dont_export_item" name="dont_export_item" value="0" /> + <input type="hidden" id="record_type" name="record_type" value="bibs" /> + <button class="btn btn-small" id="export_submit"><i class="icon-download-alt"></i> Export</button> + </fieldset> + [% END %] + </div> [% END %] </form> [% ELSE %] -- 1.7.2.5