From: erickson Date: Wed, 4 Aug 2010 20:19:14 +0000 (+0000) Subject: require latest last_xact_id for payments X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=c5d36d2a99e45b9a02d00a1e795b66a38c271b43;p=evergreen%2Fbjwebb.git require latest last_xact_id for payments To prevent accidental multiple payments on the transaction or payments against stale information, require the latest user last_xact_id to be passed into the payment API call. Update staff client (thanks jason) and self-check payment interfaces to match. git-svn-id: svn://svn.open-ils.org/ILS/trunk@17079 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm index a4edcf84b..4ae466ba5 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm @@ -66,6 +66,10 @@ __PACKAGE__->register_method( ], }/, type => 'hash' }, + { + desc => q/Last user transaction ID. This is the actor.usr.last_xact_id value/, + type => 'string' + } ], "return" => { "desc" => @@ -73,6 +77,11 @@ __PACKAGE__->register_method( BAD_PARAMS Bad parameters were given to this API method itself. See note field. + INVALID_USER_XACT_ID + The last user transaction ID does not match the ID in the database. This means + the user object has been updated since the last retrieval. The client should + be instructed to reload the user object and related transactions before attempting + another payment REFUND_EXCEEDS_BALANCE REFUND_EXCEEDS_DESK_PAYMENTS CREDIT_PROCESSOR_NOT_SPECIFIED @@ -117,7 +126,7 @@ __PACKAGE__->register_method( } ); sub make_payments { - my($self, $client, $auth, $payments) = @_; + my($self, $client, $auth, $payments, $last_xact_id) = @_; my $e = new_editor(authtoken => $auth, xact => 1); return $e->die_event unless $e->checkauth; @@ -138,6 +147,11 @@ sub make_payments { my $patron = $e->retrieve_actor_user($user_id) or return $e->die_event; + if($patron->last_xact_id ne $last_xact_id) { + $e->rollback; + return OpenILS::Event->new('INVALID_USER_XACT_ID'); + } + # A user is allowed to make credit card payments on his/her own behalf # All other scenarious require permission unless($type eq 'credit_card_payment' and $user_id == $e->requestor->id) { @@ -364,8 +378,12 @@ sub make_payments { } } + # update the user to create a new last_xact_id + $e->update_actor_user($patron) or return $e->die_event; + $patron = $e->retrieve_actor_user($patron) or return $e->die_event; + $e->commit; - return \@payment_ids; + return {last_xact_id => $patron->last_xact_id, payments => \@payment_ids}; } sub _recording_failure { diff --git a/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js b/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js index 5691731c5..b8e422553 100644 --- a/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js +++ b/Open-ILS/web/js/dojo/openils/circ/nls/selfcheck.js @@ -36,6 +36,7 @@ "FAIL_PART_no_ultimate_items": "The system could not find any items to match this hold request", "FAIL_PART_no_matchpoint": "System rules do not define how to handle this item", "FAIL_PART_no_user": "The system could not find this patron", - "FAIL_PART_transit_range": "The item cannot transit this far" + "FAIL_PART_transit_range": "The item cannot transit this far", + "PAYMENT_INVALID_USER_XACT_ID" : "We cannot proceed with the payment, because your account was updated from another location. Please refresh the interface or log out and back in to retrieve the latest account information" } diff --git a/Open-ILS/web/js/ui/default/circ/selfcheck/payment.js b/Open-ILS/web/js/ui/default/circ/selfcheck/payment.js index b3a59fc6b..1871bba1f 100644 --- a/Open-ILS/web/js/ui/default/circ/selfcheck/payment.js +++ b/Open-ILS/web/js/ui/default/circ/selfcheck/payment.js @@ -102,7 +102,7 @@ proto.sendCCPayment = function(patron, xacts, onPaymentSubmit) { var resp = fieldmapper.standardRequest( ['open-ils.circ', 'open-ils.circ.money.payment'], - {params : [this.authtoken, args]} + {params : [this.authtoken, args, patron.last_xact_id()]} ); if (typeof(progressDialog) != "undefined") diff --git a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js index a9f2ec6a2..8ae71862d 100644 --- a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js +++ b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js @@ -114,9 +114,13 @@ SelfCheckManager.prototype.init = function() { var message = evt + ''; if(evt.textcode == 'CREDIT_PROCESSOR_DECLINED_TRANSACTION' && evt.payload) message += '\n' + evt.payload.error_message; + if(evt.textcode == 'INVALID_USER_XACT_ID') + message += '\n' + localeStrings.PAYMENT_INVALID_USER_XACT_ID; self.handleAlert(message, true, 'payment-failure'); return; } + + self.patron.last_xact_id(resp.last_xact_id); // update to match latest from server self.printPaymentReceipt( resp, function() { @@ -1356,7 +1360,7 @@ SelfCheckManager.prototype.printHoldsReceipt = function(callback) { } -SelfCheckManager.prototype.printPaymentReceipt = function(paymentIds, callback) { +SelfCheckManager.prototype.printPaymentReceipt = function(response, callback) { var self = this; progressDialog.show(true); @@ -1365,7 +1369,7 @@ SelfCheckManager.prototype.printPaymentReceipt = function(paymentIds, callback) ['open-ils.circ', 'open-ils.circ.money.payment_receipt.print'], { async : true, - params : [this.authtoken, paymentIds], + params : [this.authtoken, response.payments], oncomplete : function(r) { var resp = openils.Util.readResponse(r); var output = resp.template_output(); diff --git a/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties b/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties index 49afcfd70..20f8b0f15 100644 --- a/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties +++ b/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties @@ -54,6 +54,7 @@ staff.patron.bills.apply_payment.nothing_applied=No payments or patron credit ap staff.patron.bills.pay.annotate_payment=Please annotate this payment: staff.patron.bills.pay.annotate_payment.title=Annotate Payment 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.info_box.label_value.reservation=Reservation # 1 - Resource Barcode 2 - Resource Type Name diff --git a/Open-ILS/xul/staff_client/server/patron/bill2.js b/Open-ILS/xul/staff_client/server/patron/bill2.js index 34c4e9665..96f3e8bcf 100644 --- a/Open-ILS/xul/staff_client/server/patron/bill2.js +++ b/Open-ILS/xul/staff_client/server/patron/bill2.js @@ -42,19 +42,7 @@ function my_init() { $('credit_forward').setAttribute('value','???'); if (!g.patron) { - g.network.simple_request( - 'FM_AU_FLESHED_RETRIEVE_VIA_ID.authoritative', - [ ses(), g.patron_id ], - function(req) { - try { - g.patron = req.getResultObject(); - if (typeof g.patron.ilsevent != 'undefined') throw(g.patron); - $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() )); - } catch(E) { - alert('Error in bill2.js, retrieve patron callback: ' + E); - } - } - ); + refresh_patron(); } else { $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() )); } @@ -757,14 +745,7 @@ function apply_payment() { if (typeof window.xulG == 'object' && typeof window.xulG.refresh == 'function') window.xulG.refresh(); if (typeof window.xulG == 'object' && typeof window.xulG.on_money_change == 'function') window.xulG.on_money_change(); if ( $('payment_type').value == 'credit_payment' || $('convert_change_to_credit').checked ) { - JSAN.use('patron.util'); - patron.util.retrieve_au_via_id(ses(),g.patron_id, function(req) { - var au_obj = req.getResultObject(); - if (typeof au_obj.ilsevent == 'undefined') { - g.patron = au_obj; - $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() )); - } - }); + refresh_patron(); } try { if ( ! $('receipt_upon_payment').hasAttribute('checked') ) { return; } // Skip print attempt @@ -836,11 +817,14 @@ function pay(payment_blob) { payment_type : $('payment_type').getAttribute('label'), note : payment_blob.note } - var robj = g.network.simple_request( 'BILL_PAY', [ ses(), payment_blob ]); + var robj = g.network.simple_request( 'BILL_PAY', [ ses(), payment_blob, g.patron.last_xact_id() ]); if (typeof robj.ilsevent != 'undefined') { - switch(Number(robj.ilsevent)) { - case 0 /* SUCCESS */ : return true; break; - case 1226 /* REFUND_EXCEEDS_DESK_PAYMENTS */ : alert($("patronStrings").getFormattedString('staff.patron.bills.pay.refund_exceeds_desk_payment', [robj.desc])); return false; break; + switch(robj.textcode) { + case 'SUCCESS' : return true; break; + case 'REFUND_EXCEEDS_DESK_PAYMENTS' : alert($("patronStrings").getFormattedString('staff.patron.bills.pay.refund_exceeds_desk_payment', [robj.desc])); return false; break; + case 'INVALID_USER_XACT_ID' : + refresh(); default_focus(); + alert($("patronStrings").getFormattedString('staff.patron.bills.pay.invalid_user_xact_id', [robj.desc])); return false; break; default: throw(robj); break; } } @@ -856,6 +840,7 @@ function refresh(params) { if (params && params.clear_voided_summary) { g.data.voided_billings = []; g.data.stash('voided_billings'); } + refresh_patron(); g.bill_list.clear(); retrieve_mbts_for_list(); tally_voided(); @@ -908,3 +893,13 @@ function void_all_billings(mobts_id) { } } +function refresh_patron() { + JSAN.use('patron.util'); JSAN.use('util.money'); + patron.util.retrieve_au_via_id(ses(),g.patron_id, function(req) { + var au_obj = req.getResultObject(); + if (typeof au_obj.ilsevent == 'undefined') { + g.patron = au_obj; + $('credit_forward').setAttribute('value',util.money.sanitize( g.patron.credit_forward_balance() )); + } + }); +}