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
);
--- /dev/null
+[%
+ WRAPPER "staff/base.tt2";
+ ctx.page_title = l("Work Log");
+ ctx.page_app = "egWorkLogApp";
+%]
+
+[% BLOCK APP_JS %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/grid.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/admin/workstation/log.js"></script>
+<link rel="stylesheet" href="[% ctx.base_path %]/staff/css/circ.css" />
+[% END %]
+
+<div ng-view></div>
+
+[% END %]
--- /dev/null
+<div class="container-fluid" style="text-align:center">
+ <div class="alert alert-info alert-less-pad strong-text-2">
+ <span>[% l('Work Log') %]</span>
+ </div>
+</div>
+
+<eg-grid
+ main-label="[% l('Most Recently Logged Staff Actions') %]"
+ id-field="id"
+ features="-sort,-multisort"
+ items-provider="grid_work_log_provider"
+ grid-controls="grid_controls"
+ persist-key="admin.workstation.work_log"
+>
+
+ <eg-grid-menu-item handler="refresh_ui"
+ label="[% l('Refresh') %]"></eg-grid-menu-item>
+
+ <eg-grid-menu-item handler="load_item"
+ label="[% l('Retrieve Item') %]"></eg-grid-menu-item>
+
+ <eg-grid-menu-item handler="load_patron"
+ label="[% l('Retrieve Patron') %]"></eg-grid-menu-item>
+
+ <eg-grid-field path='action' label="[% l('Code') %]" hidden></eg-grid-field>
+ <eg-grid-field path='msg' label="[% l('Message') %]"></eg-grid-field>
+ <eg-grid-field path='amount' label="[% l('Amount') %]" hidden></eg-grid-field>
+ <eg-grid-field path='user' label="[% l('Patron') %]"></eg-grid-field>
+ <eg-grid-field path='item' label="[% l('Item') %]"></eg-grid-field>
+ <eg-grid-field path='when' label="[% l('When') %]"></eg-grid-field>
+ <eg-grid-field path='actor' label="[% l('Staff') %]" hidden></eg-grid-field>
+</eg-grid>
+
+<hr/>
+
+<eg-grid
+ main-label="[% l('Most Recently Affected Patrons') %]"
+ id-field="id"
+ features="-sort,-multisort"
+ items-provider="grid_patron_log_provider"
+ grid-controls="grid_controls"
+ persist-key="admin.workstation.patron_log"
+>
+
+ <eg-grid-menu-item handler="load_item"
+ label="[% l('Retrieve Item') %]"></eg-grid-menu-item>
+
+ <eg-grid-menu-item handler="load_patron"
+ label="[% l('Retrieve Patron') %]"></eg-grid-menu-item>
+
+ <eg-grid-field path='action' label="[% l('Code') %]" hidden></eg-grid-field>
+ <eg-grid-field path='msg' label="[% l('Message') %]"></eg-grid-field>
+ <eg-grid-field path='amount' label="[% l('Amount') %]" hidden></eg-grid-field>
+ <eg-grid-field path='user' label="[% l('Patron') %]"></eg-grid-field>
+ <eg-grid-field path='item' label="[% l('Item') %]"></eg-grid-field>
+ <eg-grid-field path='when' label="[% l('When') %]"></eg-grid-field>
+ <eg-grid-field path='actor' label="[% l('Staff') %]" hidden></eg-grid-field>
+</eg-grid>
+
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') %]';
}]);
</script>
.eg-grid-primary-label {
font-weight: bold;
font-size: 120%;
+ margin-right: 2em;
}
/* odd/even row styling */
--- /dev/null
+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;
+ }
+
+}])
+
angular.module('egPatronApp')
.factory('billSvc',
- ['$q','egCore','patronSvc',
-function($q , egCore , patronSvc) {
+ ['$q','egCore','egWorkLog','patronSvc',
+function($q , egCore , egWorkLog , patronSvc) {
var service = {};
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);
.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
+ }
+ );
}
}
'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);
});
};
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;
}).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.
});
}
}])
-
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
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']);
});
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)
})
});
});
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)
})
});
});
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;
}
}
+ 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;
}
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;
});
}
};
}
])
+
+.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;
+}]);