From 51fe14771d2f117f0f980047f53a64772f2c2ba1 Mon Sep 17 00:00:00 2001 From: Jason Etheridge Date: Fri, 4 Dec 2015 11:06:56 -0500 Subject: [PATCH] webstaff: egWorkLog service and Work Log UI under Administration -> Local Administration The original XUL feature starts here: 29d1b357eef061bb3698e4ce0506eb93b63421be Make sure egCore from the calling interface is pulling in these org unit settings: ui.admin.work_log.max_entries ui.admin.patron_log.max_entries Signed-off-by: Jason Etheridge Signed-off-by: Galen Charlton --- .../src/templates/opac/parts/place_hold_result.tt2 | 18 ++- .../src/templates/staff/admin/workstation/log.tt2 | 17 +++ .../templates/staff/admin/workstation/t_log.tt2 | 59 ++++++++ Open-ILS/src/templates/staff/base_js.tt2 | 13 ++ Open-ILS/src/templates/staff/css/style.css.tt2 | 1 + .../js/ui/default/staff/admin/workstation/log.js | 162 +++++++++++++++++++++ .../web/js/ui/default/staff/circ/patron/bills.js | 22 ++- .../web/js/ui/default/staff/circ/patron/holds.js | 14 +- .../web/js/ui/default/staff/circ/patron/regctl.js | 28 +++- .../web/js/ui/default/staff/circ/services/circ.js | 30 +++- .../web/js/ui/default/staff/services/eframe.js | 1 + Open-ILS/web/js/ui/default/staff/services/ui.js | 116 +++++++++++++++ 12 files changed, 456 insertions(+), 25 deletions(-) create mode 100644 Open-ILS/src/templates/staff/admin/workstation/log.tt2 create mode 100644 Open-ILS/src/templates/staff/admin/workstation/t_log.tt2 create mode 100644 Open-ILS/web/js/ui/default/staff/admin/workstation/log.js diff --git a/Open-ILS/src/templates/opac/parts/place_hold_result.tt2 b/Open-ILS/src/templates/opac/parts/place_hold_result.tt2 index 87fca56c72..889f4113da 100644 --- a/Open-ILS/src/templates/opac/parts/place_hold_result.tt2 +++ b/Open-ILS/src/templates/opac/parts/place_hold_result.tt2 @@ -59,13 +59,17 @@ window.addEventListener( 'load', function() { - try { - if (typeof xulG != 'undefined' && xulG.opac_hold_placed) { - xulG.opac_hold_placed([% hdata.hold_success %]); - } - } catch(E) { - alert('Error updating Work Log with hold placement: ' + E); - } + setTimeout( // we want this to run _after_ other onload handlers (such as from eframe.js) + function() { + try { + if (typeof xulG != 'undefined' && xulG.opac_hold_placed) { + xulG.opac_hold_placed([% hdata.hold_success %]); + } + } catch(E) { + alert('Error updating Work Log with hold placement: ' + E); + } + }, 0 + ); }, false ); diff --git a/Open-ILS/src/templates/staff/admin/workstation/log.tt2 b/Open-ILS/src/templates/staff/admin/workstation/log.tt2 new file mode 100644 index 0000000000..3e5cf0a8c5 --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/log.tt2 @@ -0,0 +1,17 @@ +[% + WRAPPER "staff/base.tt2"; + ctx.page_title = l("Work Log"); + ctx.page_app = "egWorkLogApp"; +%] + +[% BLOCK APP_JS %] + + + + + +[% END %] + +
+ +[% END %] diff --git a/Open-ILS/src/templates/staff/admin/workstation/t_log.tt2 b/Open-ILS/src/templates/staff/admin/workstation/t_log.tt2 new file mode 100644 index 0000000000..014d74d2dc --- /dev/null +++ b/Open-ILS/src/templates/staff/admin/workstation/t_log.tt2 @@ -0,0 +1,59 @@ +
+
+ [% l('Work Log') %] +
+
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/templates/staff/base_js.tt2 b/Open-ILS/src/templates/staff/base_js.tt2 index 54e6e7fcb2..980ec5b4a5 100644 --- a/Open-ILS/src/templates/staff/base_js.tt2 +++ b/Open-ILS/src/templates/staff/base_js.tt2 @@ -60,6 +60,19 @@ s.EG_UNLOAD_PAGE_PROMPT_MSG = '[% l('This page may have unsaved data.') %]'; s.EG_DATE_INPUT_CLOSE_TEXT = '[% l('Close') %]'; + s.EG_WORK_LOG_CHECKOUT = '[% l('Check Out') %]'; + s.EG_WORK_LOG_RENEW = '[% l('Renew') %]'; + s.EG_WORK_LOG_CHECKIN = '[% l('Check In') %]'; + s.EG_WORK_LOG_EDITED_PATRON = '[% l('Edited Patron') %]'; + s.EG_WORK_LOG_REGISTERED_PATRON = '[% l('Registered Patron') %]'; + s.EG_WORK_LOG_CASH_PAYMENT = '[% l('Cash Payment') %]'; + s.EG_WORK_LOG_CHECK_PAYMENT = '[% l('Check Payment') %]'; + s.EG_WORK_LOG_CREDIT_CARD_PAYMENT = '[% l('Credit Card Payment') %]'; + s.EG_WORK_LOG_CREDIT_PAYMENT = '[% l('Credit Payment') %]'; + s.EG_WORK_LOG_WORK_PAYMENT = '[% l('Work Payment') %]'; + s.EG_WORK_LOG_FORGIVE_PAYMENT = '[% l('Forgive Payment') %]'; + s.EG_WORK_LOG_GOODS_PAYMENT = '[% l('Goods Payment') %]'; + s.EG_WORK_LOG_REQUESTED_HOLD = '[% l('Hold Request') %]'; }]); diff --git a/Open-ILS/src/templates/staff/css/style.css.tt2 b/Open-ILS/src/templates/staff/css/style.css.tt2 index d4e4ae53c9..fb06c0eedd 100644 --- a/Open-ILS/src/templates/staff/css/style.css.tt2 +++ b/Open-ILS/src/templates/staff/css/style.css.tt2 @@ -220,6 +220,7 @@ table.list tr.selected td { /* deprecated? */ .eg-grid-primary-label { font-weight: bold; font-size: 120%; + margin-right: 2em; } /* odd/even row styling */ diff --git a/Open-ILS/web/js/ui/default/staff/admin/workstation/log.js b/Open-ILS/web/js/ui/default/staff/admin/workstation/log.js new file mode 100644 index 0000000000..b9fe1dbd91 --- /dev/null +++ b/Open-ILS/web/js/ui/default/staff/admin/workstation/log.js @@ -0,0 +1,162 @@ +angular.module('egWorkLogApp', + ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod']) + +.config(function($routeProvider, $locationProvider, $compileProvider) { + $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export + + var resolver = {delay : + ['egStartup', function(egStartup) {return egStartup.go()}]} + + $routeProvider.when('/admin/workstation/log', { + templateUrl: './admin/workstation/t_log', + controller: 'WorkLogCtrl', + resolve : resolver + }); + + $routeProvider.otherwise({redirectTo : '/admin/workstation/log'}); +}) + +.controller('WorkLogCtrl', + ['$scope','$q','$routeParams','$window','$location','$timeout','egCore','egGridDataProvider','egWorkLog', +function($scope , $q , $routeParams , $window , $location , $timeout , egCore , egGridDataProvider , egWorkLog ) { + + var work_log_entries = []; + var patron_log_entries = []; + + var work_log_provider = egGridDataProvider.instance({}); + var patron_log_provider = egGridDataProvider.instance({}); + $scope.grid_work_log_provider = work_log_provider; + $scope.grid_patron_log_provider = patron_log_provider; + + function load_item(log_entries) { + if (!log_entries) return; + if (!angular.isArray(log_entries)) log_entries = [log_entries]; + angular.forEach(log_entries, function(log_entry) { + $window.open( + $location.path( + '/cat/item/' + log_entry.item_id + ).absUrl(), + '_blank' + ).focus(); + }); + } + + $scope.load_item = function(action, data, entries) { + load_item(entries); + } + + function load_patron(log_entries) { + if (!log_entries) return; + if (!angular.isArray(log_entries)) log_entries = [log_entries]; + angular.forEach(log_entries, function(log_entry) { + $window.open( + $location.path( + '/circ/patron/' + log_entry.patron_id + '/checkout' + ).absUrl(), + '_blank' + ).focus(); + }); + } + + $scope.load_patron = function(action, data, entries) { + load_patron(entries); + } + + $scope.grid_controls = { + activateItem : load_patron + } + + function refresh_page() { + work_log_entries = []; + patron_log_entries = []; + provider.refresh(); + } + + function fetch_hold(deferred,entry) { + egCore.pcrud.search('ahr', + { 'id' : entry.data.hold_id }, { + 'flesh' : 2, + 'flesh_fields' : { + 'ahr' : ['usr','current_copy'], + }, + } + ).then( + deferred.resolve, null, + function(hold) { + entry.patron_id = hold.usr().id(); + entry.user = hold.usr().family_name(); + if (hold.current_copy()) { + entry.item = hold.current_copy().barcode(); + } + deferred.notify(entry); + } + ); + } + + function fetch_patron(deferred,entry) { + egCore.pcrud.search('au', + { 'id' : entry.data.patron_id }, {} + ).then( + deferred.resolve, null, + function(usr) { + entry.user = usr.family_name(); + deferred.notify(entry); + } + ); + } + + work_log_provider.get = function(offset, count) { + var log_entries = egWorkLog.retrieve_all(); + console.log(log_entries); + var deferred = $q.defer(); + + $timeout( function() { + log_entries.work_log.forEach( + function(el,idx) { + el.id = idx; + if (el.action == 'requested_hold') { + fetch_hold(deferred,el); + } else if (el.action == 'registered_patron') { + fetch_patron(deferred,el); + } else if (el.action == 'edited_patron') { + fetch_patron(deferred,el); + } else if (el.action == 'paid_bill') { + fetch_patron(deferred,el); + } else { + deferred.notify(el); + } + } + ); + }); + return deferred.promise; + } + + patron_log_provider.get = function(offset, count) { + var log_entries = egWorkLog.retrieve_all(); + console.log(log_entries); + var deferred = $q.defer(); + + $timeout( function() { + log_entries.patron_log.forEach( + function(el,idx) { + el.id = idx; + if (el.action == 'requested_hold') { + fetch_hold(deferred,el); + } else if (el.action == 'registered_patron') { + fetch_patron(deferred,el); + } else if (el.action == 'edited_patron') { + fetch_patron(deferred,el); + } else if (el.action == 'paid_bill') { + fetch_patron(deferred,el); + } else { + deferred.notify(el); + } + } + ); + }); + return deferred.promise; + } + +}]) + diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js b/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js index c9b6c44e91..1245a2bebe 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js +++ b/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js @@ -4,8 +4,8 @@ angular.module('egPatronApp') .factory('billSvc', - ['$q','egCore','patronSvc', -function($q , egCore , patronSvc) { + ['$q','egCore','egWorkLog','patronSvc', +function($q , egCore , egWorkLog , patronSvc) { var service = {}; @@ -39,6 +39,24 @@ function($q , egCore , patronSvc) { patronSvc.current.last_xact_id() ).then(function(resp) { console.debug('payments: ' + js2JSON(resp)); + var total = 0; angular.forEach(payments,function(p) { total += p[1]; }); + var msg; + switch(type) { + case 'cash_payment' : msg = egCore.strings.EG_WORK_LOG_CASH_PAYMENT; break; + case 'check_payment' : msg = egCore.strings.EG_WORK_LOG_CHECK_PAYMENT; break; + case 'credit_card_payment' : msg = egCore.strings.EG_WORK_LOG_CREDIT_CARD_PAYMENT; break; + case 'credit_payment' : msg = egCore.strings.EG_WORK_LOG_CREDIT_PAYMENT; break; + case 'work_payment' : msg = egCore.strings.EG_WORK_LOG_WORK_PAYMENT; break; + case 'forgive_payment' : msg = egCore.strings.EG_WORK_LOG_FORGIVE_PAYMENT; break; + case 'goods_payment' : msg = egCore.strings.EG_WORK_LOG_GOODS_PAYMENT; break; + } + egWorkLog.record( + msg,{ + 'action' : 'paid_bill', + 'patron_id' : service.userId, + 'total_amount' : total + } + ); if (evt = egCore.evt.parse(resp)) return alert(evt); diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js b/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js index 9c2101fb01..ddbf1d8692 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js +++ b/Open-ILS/web/js/ui/default/staff/circ/patron/holds.js @@ -137,13 +137,19 @@ function($scope, $q, $routeParams, egCore, egUser, patronSvc, .controller('PatronHoldsCreateCtrl', - ['$scope','$routeParams','$location','egCore','patronSvc', -function($scope , $routeParams , $location , egCore , patronSvc) { + ['$scope','$routeParams','$location','egCore','egWorkLog','patronSvc', +function($scope , $routeParams , $location , egCore , egWorkLog , patronSvc) { $scope.handlers = { - opac_hold_placed : function() { - // FIXME: this isn't getting called.. not sure why + opac_hold_placed : function(hold) { patronSvc.fetchUserStats(); // update hold counts + egWorkLog.record( + egCore.strings.EG_WORK_LOG_REQUESTED_HOLD,{ + 'action' : 'requested_hold', + 'patron_id' : patronSvc.current.id(), + 'hold_id' : hold + } + ); } } diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js b/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js index 0e6629ec03..d08d69971e 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js +++ b/Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js @@ -348,9 +348,15 @@ angular.module('egCoreMod') 'sms.enable', 'ui.patron.edit.aua.state.require', 'ui.patron.edit.aua.state.suggest', - 'ui.patron.edit.aua.state.show' + 'ui.patron.edit.aua.state.show', + 'ui.admin.work_log.max_entries', + 'ui.admin.patron_log.max_entries' ]).then(function(settings) { service.org_settings = settings; + if (egCore && egCore.env && !egCore.env.aous) { + egCore.env.aous = settings; + console.log('setting egCore.env.aous'); + } return service.process_org_settings(settings); }); }; @@ -1078,11 +1084,13 @@ angular.module('egCoreMod') return service; }]) -.controller('PatronRegCtrl', +.controller('PatronRegCtrl', ['$scope','$routeParams','$q','$uibModal','$window','egCore', 'patronSvc','patronRegSvc','egUnloadPrompt','egAlertDialog', -function($scope , $routeParams , $q , $uibModal , $window , egCore , - patronSvc , patronRegSvc , egUnloadPrompt, egAlertDialog) { + 'egWorkLog', +function($scope , $routeParams , $q , $uibModal , $window , egCore , + patronSvc , patronRegSvc , egUnloadPrompt, egAlertDialog , + egWorkLog) { $scope.page_data_loaded = false; $scope.clone_id = patronRegSvc.clone_id = $routeParams.clone_id; @@ -1801,6 +1809,17 @@ function($scope , $routeParams , $q , $uibModal , $window , egCore , }).then(function() { + if (updated_user) { + egWorkLog.record( + $scope.patron.isnew + ? egCore.strings.EG_WORK_LOG_REGISTERED_PATRON + : egCore.strings.EG_WORK_LOG_EDITED_PATRON, { + 'action' : $scope.patron.isnew ? 'registered_patron' : 'edited_patron', + 'patron_id' : updated_user.id() + } + ); + } + // reloading the page means potentially losing some information // (e.g. last patron search), but is the only way to ensure all // components are properly updated to reflect the modified patron. @@ -1821,4 +1840,3 @@ function($scope , $routeParams , $q , $uibModal , $window , egCore , }); } }]) - diff --git a/Open-ILS/web/js/ui/default/staff/circ/services/circ.js b/Open-ILS/web/js/ui/default/staff/circ/services/circ.js index e74cc78dd4..66d53352d2 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/services/circ.js +++ b/Open-ILS/web/js/ui/default/staff/circ/services/circ.js @@ -5,9 +5,10 @@ angular.module('egCoreMod') .factory('egCirc', - ['$uibModal','$q','egCore','egAlertDialog','egConfirmDialog', -function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog) { + 'egWorkLog', +function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog, + egWorkLog) { var service = { // auto-override these events after the first override @@ -17,7 +18,9 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog) { egCore.startup.go().finally(function() { egCore.org.settings([ - 'ui.staff.require_initials.patron_standing_penalty' + 'ui.staff.require_initials.patron_standing_penalty', + 'ui.admin.work_log.max_entries', + 'ui.admin.patron_log.max_entries' ]).then(function(set) { service.require_initials = Boolean(set['ui.staff.require_initials.patron_standing_penalty']); }); @@ -131,7 +134,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog) { return service.handle_checkout_resp(evt, params, options); }) .then(function(final_resp) { - return service.munge_resp_data(final_resp) + return service.munge_resp_data(final_resp,'checkout',method) }) }); }); @@ -171,7 +174,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog) { return service.handle_renew_resp(evt, params, options); }) .then(function(final_resp) { - return service.munge_resp_data(final_resp) + return service.munge_resp_data(final_resp,'renew',method) }) }); }); @@ -210,14 +213,14 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog) { return service.handle_checkin_resp(evt, params, options); }) .then(function(final_resp) { - return service.munge_resp_data(final_resp) + return service.munge_resp_data(final_resp,'checkin',method) }) }); }); } // provide consistent formatting of the final response data - service.munge_resp_data = function(final_resp) { + service.munge_resp_data = function(final_resp,worklog_action,worklog_method) { var data = final_resp.data = {}; if (!final_resp.evt[0]) return; @@ -256,6 +259,19 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog) { } } + egWorkLog.record( + worklog_action == 'checkout' + ? egCore.strings.EG_WORK_LOG_CHECKOUT + : (worklog_action == 'renew' + ? egCore.strings.EG_WORK_LOG_RENEW + : egCore.strings.EG_WORK_LOG_CHECKIN // worklog_action == 'checkin' + ),{ + 'action' : worklog_action, + 'method' : worklog_method, + 'response' : final_resp + } + ); + return final_resp; } diff --git a/Open-ILS/web/js/ui/default/staff/services/eframe.js b/Open-ILS/web/js/ui/default/staff/services/eframe.js index ac54bd5bd3..aa87c7b8bd 100644 --- a/Open-ILS/web/js/ui/default/staff/services/eframe.js +++ b/Open-ILS/web/js/ui/default/staff/services/eframe.js @@ -244,6 +244,7 @@ angular.module('egCoreMod') if ($scope.handlers) { $scope.handlers.reload = $scope.reload; angular.forEach($scope.handlers, function(val, key) { + console.log('eframe applying xulG handlers: ' + key); $scope.iframe.contentWindow.xulG[key] = val; }); } diff --git a/Open-ILS/web/js/ui/default/staff/services/ui.js b/Open-ILS/web/js/ui/default/staff/services/ui.js index e5eb40f710..b9b3a8ebd8 100644 --- a/Open-ILS/web/js/ui/default/staff/services/ui.js +++ b/Open-ILS/web/js/ui/default/staff/services/ui.js @@ -484,3 +484,119 @@ function($window , egStrings) { }; } ]) + +.factory('egWorkLog', ['egCore', function(egCore) { + var service = {}; + + service.retrieve_all = function() { + var workLog = egCore.hatch.getLocalItem('eg.work_log') || []; + var patronLog = egCore.hatch.getLocalItem('eg.patron_log') || []; + + return { 'work_log' : workLog, 'patron_log' : patronLog }; + } + + service.record = function(message,data) { + var max_entries; + var max_patrons; + if (typeof egCore != 'undefined') { + if (typeof egCore.env != 'undefined') { + if (typeof egCore.env.aous != 'undefined') { + max_entries = egCore.env.aous['ui.admin.work_log.max_entries']; + max_patrons = egCore.env.aous['ui.admin.patron_log.max_entries']; + } else { + console.log('worklog: missing egCore.env.aous'); + } + } else { + console.log('worklog: missing egCore.env'); + } + } else { + console.log('worklog: missing egCore'); + } + if (!max_entries) { + if (typeof egCore.org != 'undefined') { + if (typeof egCore.org.cachedSettings != 'undefined') { + max_entries = egCore.org.cachedSettings['ui.admin.work_log.max_entries']; + } else { + console.log('worklog: missing egCore.org.cachedSettings'); + } + } else { + console.log('worklog: missing egCore.org'); + } + } + if (!max_patrons) { + if (typeof egCore.org != 'undefined') { + if (typeof egCore.org.cachedSettings != 'undefined') { + max_patrons = egCore.org.cachedSettings['ui.admin.patron_log.max_entries']; + } else { + console.log('worklog: missing egCore.org.cachedSettings'); + } + } else { + console.log('worklog: missing egCore.org'); + } + } + if (!max_entries) { + max_entries = 20; + console.log('worklog: defaulting to max_entries = ' + max_entries); + } + if (!max_patrons) { + max_patrons = 10; + console.log('worklog: defaulting to max_patrons = ' + max_patrons); + } + + var workLog = egCore.hatch.getLocalItem('eg.work_log') || []; + var patronLog = egCore.hatch.getLocalItem('eg.patron_log') || []; + var entry = { + 'when' : new Date(), + 'msg' : message, + 'data' : data, + 'action' : data.action, + 'actor' : egCore.auth.user().usrname() + }; + if (data.action == 'checkin') { + entry['item'] = data.response.params.copy_barcode; + entry['user'] = data.response.data.au.family_name(); + entry['item_id'] = data.response.data.acp.id(); + entry['patron_id'] = data.response.data.au.id(); + } + if (data.action == 'checkout') { + entry['item'] = data.response.params.copy_barcode; + entry['user'] = data.response.data.au.family_name(); + entry['item_id'] = data.response.data.acp.id(); + entry['patron_id'] = data.response.data.au.id(); + } + if (data.action == 'renew') { + entry['item'] = data.response.params.copy_barcode; + entry['user'] = data.response.data.au.family_name(); + entry['item_id'] = data.response.data.acp.id(); + entry['patron_id'] = data.response.data.au.id(); + } + if (data.action == 'requested_hold' + || data.action == 'edited_patron' + || data.action == 'registered_patron' + || data.action == 'paid_bill') { + entry['patron_id'] = data.patron_id; + } + if (data.action == 'paid_bill') { + entry['amount'] = data.total_amount; + } + + workLog.push( entry ); + if (workLog.length > max_entries) workLog.shift(); + egCore.hatch.setLocalItem('eg.work_log',workLog); // hatch JSONifies the data, so should be okay re: memory leaks? + + if (entry['patron_id']) { + var temp = []; + for (var i = 0; i < patronLog.length; i++) { // filter out any matching patron + if (patronLog[i]['patron_id'] != entry['patron_id']) temp.push(patronLog[i]); + } + temp.push( entry ); + if (temp.length > max_patrons) temp.shift(); + patronLog = temp; + egCore.hatch.setLocalItem('eg.patron_log',patronLog); + } + + console.log('worklog',entry); + } + + return service; +}]); -- 2.11.0