'Amount of time after which no negative balances (refunds) are allowed on bills for lost/long overdue materials. The "Prohibit negative balance on bills for lost materials" setting must also be set to "true".',
'coust', 'description'),
'interval', null)
+,( 'ui.circ.billing.amount_limit', 'gui',
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_limit',
+ 'Maximum payment amount allowed.',
+ 'coust', 'label'),
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_limit',
+ 'The payment amount in the Patron Bills interface may not exceed the value of this setting.',
+ 'coust', 'description'),
+ 'currency',null)
+,( 'ui.circ.billing.amount_warn', 'gui',
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_warn',
+ 'Payment amount threshold for Are You Sure? dialog.',
+ 'coust', 'label'),
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_warn',
+ 'In the Patron Bills interface, a payment attempt will warn if the amount exceeds the value of this setting.',
+ 'coust', 'description'),
+ 'currency', null)
;
UPDATE config.org_unit_setting_type
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype )
+ VALUES (
+ 'ui.circ.billing.amount_limit', 'gui',
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_limit',
+ 'Maximum payment amount allowed.',
+ 'coust', 'label'),
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_limit',
+ 'The payment amount in the Patron Bills interface may not exceed the value of this setting.',
+ 'coust', 'description'),
+ 'currency'
+ );
+
+INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype )
+ VALUES (
+ 'ui.circ.billing.amount_warn', 'gui',
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_warn',
+ 'Payment amount threshold for Are You Sure? dialog.',
+ 'coust', 'label'),
+ oils_i18n_gettext(
+ 'ui.circ.billing.amount_warn',
+ 'In the Patron Bills interface, a payment attempt will warn if the amount exceeds the value of this setting.',
+ 'coust', 'description'),
+ 'currency'
+ );
+
+COMMIT;
s.CHECK_IN_CONFIRM = "[% l('Check In Items?') %]";
s.REG_INVALID_FIELDS =
"[% l('Please enter valid values for all required fields.') %]"
+ s.PAYMENT_WARN_AMOUNT = "[% l('Are you sure you want to apply a payment of $[_1]?', '{{payment_amount}}') %]";
+ s.PAYMENT_WARN_AMOUNT_TITLE = "[% l('Verify Payment Amount') %]";
+ s.PAYMENT_OVER_MAX = "[% l('Payments over $[_1] are denied by policy.', '{{max_amount}}') %]";
}]);
</script>
service.fetchBillSettings = function() {
if (service.settings) return $q.when(service.settings);
return egCore.org.settings(
- ['ui.circ.billing.uncheck_bills_and_unfocus_payment_box']
+ ['ui.circ.billing.uncheck_bills_and_unfocus_payment_box','ui.circ.billing.amount_warn','ui.circ.billing.amount_limit']
).then(function(s) {return service.settings = s});
}
*/
.controller('PatronBillsCtrl',
['$scope','$q','$routeParams','egCore','egConfirmDialog','$location',
- 'egGridDataProvider','billSvc','patronSvc','egPromptDialog', 'egBilling',
+ 'egGridDataProvider','billSvc','patronSvc','egPromptDialog', 'egAlertDialog',
+ 'egBilling',
function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
- egGridDataProvider , billSvc , patronSvc , egPromptDialog, egBilling) {
+ egGridDataProvider , billSvc , patronSvc , egPromptDialog, egAlertDialog,
+ egBilling) {
$scope.initTab('bills', $routeParams.id);
billSvc.userId = $routeParams.id;
$scope.annotate_payment = false;
$scope.receipt_count = 1;
$scope.receipt_on_pay = false;
+ $scope.warn_amount = 1000;
+ $scope.max_amount = 100000;
+ $scope.amount_verified = false;
// pre-define list-returning funcs in case we access them
// before the grid instantiates
// arrive, manually de-select everything.
$scope.gridControls.selectItems([]);
}
+ if (s['ui.circ.billing.amount_warn']) {
+ $scope.warn_amount = Number(s['ui.circ.billing.amount_warn']);
+ }
+ if (s['ui.circ.billing.amount_limit']) {
+ $scope.max_amount = Number(s['ui.circ.billing.amount_limit']);
+ }
});
$scope.gridControls.allItemsRetrieved = function() {
}
$scope.applyPayment = function() {
+
+ if ($scope.payment_amount > $scope.max_amount ) {
+ egAlertDialog.open(
+ egCore.strings.PAYMENT_OVER_MAX,
+ { max_amount : ''+$scope.max_amount,
+ ok : function() {
+ $scope.payment_amount = 0;
+ }
+ }
+ );
+ return;
+ }
+
+ if (($scope.payment_amount > $scope.warn_amount) && ($scope.amount_verified == false)) {
+ egConfirmDialog.open(
+ egCore.strings.PAYMENT_WARN_AMOUNT_TITLE, egCore.strings.PAYMENT_WARN_AMOUNT,
+ { payment_amount : ''+$scope.payment_amount,
+ ok : function() {
+ $scope.amount_verfied = true;
+ $scope.applyPayment();
+ },
+ cancel : function() {
+ $scope.payment_amount = 0;
+ }
+ }
+ );
+ return;
+ }
+
+ $scope.amount_verfied = false;
+
if ($scope.annotate_payment) {
egPromptDialog.open(
egCore.strings.ANNOTATE_PAYMENT_MSG, '',
staff.patron.bills.pay.refund_exceeds_desk_payment=%1$s\n\nAnother way to "zero" this transaction is to use Add Billing and add a miscellaneous bill to counter the negative balance.
staff.patron.bills.pay.invalid_user_xact_id=%1$s\n\nThis patron data is stale. Refreshing patron data. You should re-attempt the payment.
staff.patron.bills.pay.payment_failed=Bill payment likely failed
+staff.patron.bills.pay.over_warn_limit.title=Verify Payment Amount
+staff.patron.bills.pay.over_warn_limit=Are you sure you want to apply a payment of $%1$s?
+staff.patron.bills.pay.over_limit=Payments over $%1$s are denied by policy.
staff.patron.bills.info_box.label_value.reservation=Reservation
# 1 - Resource Barcode 2 - Resource Type Name
staff.patron.bills.info_box.value_format.reservation=%1$s : %2$s
'keypress',
function(ev) {
if (! (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) ) { return; }
+ if (!verify_amount()) { return; }
distribute_payment();
$('apply_payment_btn').focus();
},
function(ev) {
try {
$('apply_payment_btn').disabled = true;
- apply_payment();
- tally_all();
+ if (verify_amount()) {
+ apply_payment();
+ tally_all();
+ }
$('apply_payment_btn').disabled = false;
} catch(E) {
alert('Error in bill2.js, apply_payment_btn: ' + E);
}
}
+function verify_amount() {
+
+ try {
+ var amt_warn = Number(g.data.hash.aous['ui.circ.billing.amount_warn']) || 1000;
+ var amt_limit = Number(g.data.hash.aous['ui.circ.billing.amount_limit']) || 100000;
+ var box = $('payment');
+ var amt = Number(box.value);
+
+ if (amt <= amt_warn) { return true;}
+
+ if (amt > amt_limit) {
+ alert($("patronStrings").getFormattedString('staff.patron.bills.pay.over_limit', [amt_limit]));
+ } else {
+ var r = g.error.yns_alert(
+ $('patronStrings').getFormattedString('staff.patron.bills.pay.over_warn_limit', [amt]),
+ $('patronStrings').getString('staff.patron.bills.pay.over_warn_limit.title'),
+ $('commonStrings').getString('common.yes'),
+ $('commonStrings').getString('common.no'),
+ null
+ );
+ if (r == 0) { return true; }
+ }
+
+ box.value = ''; box.select(); box.focus();
+ distribute_payment();
+ return false;
+
+ } catch (e) {
+ return false;
+ }
+
+}
+
+
function apply_payment() {
try {
var payment_blob = {};
--- /dev/null
+Set Per-OU Limits on Allowed Payment Amounts
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Two new OU Settings have been added to prevent clerks
+from accidentally clearing all patron bills by scanning
+a barcode into the Payment Amount field, or accidentally
+entering the amount without a decimal point (such as you
+would when using a cash register).
+
+The first setting is the amount above which staff will
+be asked if they're sure they want to apply the payment,
+the second is the maximum amount of money that can be
+accepted through the staff client.
+
+These settings only effect the staff client, not credit
+cards accepted through the OPAC, or direct API calls
+from third party tools.