Odoo – Authorize.Net Auto Reconciliation
LasLabs has released a new Odoo plugin that allows transactions to be generated for Authorize.Net payments, subsequently reconciling the invoice that the payment is associated to.
The Problem ∞
Odoo’s normal credit card workflow requires that a Payment Transaction be created before a customer can actually pay for the invoice. That screen then needs to be monitored in order to determine when you are paid (or you can monitor directly within your Authorize.Net account).
After the Authorize.Net transaction has been realized, a payment has to be created on the correct invoice, making sure to correctly reference the Authorize.Net transaction record to the invoice payment record.
Our Solution ∞
We find this workflow to be clunky, and our customers often do not correctly link the actual transaction to the transaction that is being created in the Invoices screen (yes, they’re different records).
Our new plugin eliminates this manual interaction in favor of a more automatic approach. As soon as the customer is redirected back to your website after payment, a transaction record is created.
If the payment was approved, it is added automatically to the correct invoice with the correct references. After that, either a partial or full reconciliation will take place on the invoice depending on whether the full amount was received.
Sample Workflow ∞
Here’s a sample of the workflow:
-
Customer clicks Authorize.Net button in invoice or sale
-
If the payment is approved, you are paid and customer is redirected back to their account. If they paid the full invoice, they can see that it is paid. Otherwise, they can see/pay the remaining balance.
-
While looking at the invoice in the employee portal, you can see the new payments that were generated in the
Payments
tab
The Downside ∞
As with everything, there is always a downside.
If a transaction is declined after the initial approval, you will have to manually intervene by reversing a lot of accounting transactions.
This can be a pretty complicated process, but we have found it to be the lesser of two evils due to the rarity of this occurrence. If you are in a high risk market, this may be more of an issue.
We are open to community suggestions when it comes to improving this module (see Open Source) below. If you have an idea on how to circumvent this issue, please drop a comment or open an issue.
The process of reversing the reconciliation can be read here.
Download ∞
As always – LasLabs is fully committed to Open Source, so the code is available from the following sources:
Features/Assistance ∞
If you come across a bug, or require assistance/features – drop us a line in the comments, or on our Facebook!
Technical ∞
The Error ∞
Below is the error that this plugin is meant to solve:
2015-09-29 05:05:17,979 24363 INFO laslabs werkzeug: 127.0.0.1 - - [29/Sep/2015 05:05:17] "POST /payment/authorize/return/ HTTP/1.0" 500 - 2015-09-29 05:05:17,989 24363 ERROR laslabs werkzeug: Error on request: Traceback (most recent call last): File "/usr/local/lib/python2.7/dist-packages/werkzeug/serving.py", line 180, in run_wsgi execute(self.server.app) File "/usr/local/lib/python2.7/dist-packages/werkzeug/serving.py", line 168, in execute application_iter = app(environ, start_response) File "/usr/lib/python2.7/dist-packages/openerp/service/server.py", line 290, in app return self.app(e, s) File "/usr/lib/python2.7/dist-packages/openerp/service/wsgi_server.py", line 216, in application return application_unproxied(environ, start_response) File "/usr/lib/python2.7/dist-packages/openerp/service/wsgi_server.py", line 202, in application_unproxied result = handler(environ, start_response) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 1290, in __call__ return self.dispatch(environ, start_response) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 1264, in __call__ return self.app(environ, start_wrapped) File "/usr/local/lib/python2.7/dist-packages/werkzeug/wsgi.py", line 591, in __call__ return self.app(environ, start_response) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 1264, in __call__ return self.app(environ, start_wrapped) File "/usr/local/lib/python2.7/dist-packages/werkzeug/wsgi.py", line 591, in __call__ return self.app(environ, start_response) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 1435, in dispatch result = ir_http._dispatch() File "/usr/lib/python2.7/dist-packages/openerp/addons/crm/ir_http.py", line 13, in _dispatch response = super(ir_http, self)._dispatch() File "/usr/lib/python2.7/dist-packages/openerp/addons/website/models/ir_http.py", line 148, in _dispatch resp = super(ir_http, self)._dispatch() File "/usr/lib/python2.7/dist-packages/openerp/addons/base/ir/ir_http.py", line 177, in _dispatch return self._handle_exception(e) File "/usr/lib/python2.7/dist-packages/openerp/addons/website/models/ir_http.py", line 196, in _handle_exception return super(ir_http, self)._handle_exception(exception) File "/usr/lib/python2.7/dist-packages/openerp/addons/base/ir/ir_http.py", line 147, in _handle_exception return request._handle_exception(exception) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 666, in _handle_exception return super(HttpRequest, self)._handle_exception(exception) File "/usr/lib/python2.7/dist-packages/openerp/addons/base/ir/ir_http.py", line 173, in _dispatch result = request.dispatch() File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 684, in dispatch r = self._call_function(**self.params) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 310, in _call_function return checked_call(self.db, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/openerp/service/model.py", line 113, in wrapper return f(dbname, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 307, in checked_call return self.endpoint(*a, **kw) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 803, in __call__ return self.method(*args, **kw) File "/usr/lib/python2.7/dist-packages/openerp/http.py", line 403, in response_wrap response = f(*args, **kw) File "/usr/lib/python2.7/dist-packages/openerp/addons/payment_authorize/controllers/main.py", line 24, in authorize_form_feedback request.env['payment.transaction'].sudo().form_feedback(post, 'authorize') File "/usr/lib/python2.7/dist-packages/openerp/api.py", line 239, in wrapper return new_api(self, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/openerp/api.py", line 463, in new_api result = method(self._model, cr, uid, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/openerp/addons/payment/models/payment_acquirer.py", line 440, in form_feedback tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context) File "/usr/lib/python2.7/dist-packages/openerp/api.py", line 241, in wrapper return old_api(self, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/openerp/api.py", line 336, in old_api result = method(recs, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/openerp/addons/payment_authorize/models/authorize.py", line 115, in _authorize_form_get_tx_from_data raise ValidationError(error_msg) ValidationError: Authorize: received data for reference SALE/2015/1234; no order found0