[REF] payment_transfer: migrate Wire Transfer to the new payment API

See the merge commit for more details.

task-2333044
This commit is contained in:
Kevin Baptiste
2021-03-30 09:25:51 +02:00
committed by Antoine Vandevenne (anv)
parent 71c5278d9a
commit aa514acf13
11 changed files with 237 additions and 169 deletions
+6 -5
View File
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import controllers
from odoo.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
from odoo.addons.payment import reset_payment_provider
from . import models
from odoo.addons.payment import reset_payment_acquirer
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
def uninstall_hook(cr, registry):
reset_payment_provider(cr, registry, 'transfer')
reset_payment_acquirer(cr, registry, 'transfer')
+6 -7
View File
@@ -1,19 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Transfer Payment Acquirer',
'name': 'Wire Transfer Payment Acquirer',
'version': '2.0',
'category': 'Accounting/Payment Acquirers',
'summary': 'Payment Acquirer: Transfer Implementation',
'version': '1.0',
'description': """Transfer Payment Acquirer""",
'summary': 'Payment Acquirer: Wire Transfer Implementation',
'description': """Wire Transfer Payment Acquirer""",
'depends': ['payment'],
'data': [
'views/payment_views.xml',
'views/payment_transfer_templates.xml',
'data/payment_acquirer_data.xml',
],
'installable': True,
'auto_install': True,
'post_init_hook': 'create_missing_journal_for_acquirers',
'post_init_hook': 'create_missing_journals',
'uninstall_hook': 'uninstall_hook',
}
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main
+7 -7
View File
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import pprint
import werkzeug
from odoo import http
@@ -12,10 +14,8 @@ _logger = logging.getLogger(__name__)
class TransferController(http.Controller):
_accept_url = '/payment/transfer/feedback'
@http.route([
'/payment/transfer/feedback',
], type='http', auth='public', csrf=False)
@http.route(_accept_url, type='http', auth='public', methods=['POST'], csrf=False)
def transfer_form_feedback(self, **post):
_logger.info('Beginning form_feedback with post data %s', pprint.pformat(post)) # debug
request.env['payment.transaction'].sudo().form_feedback(post, 'transfer')
return werkzeug.utils.redirect('/payment/process')
_logger.info("beginning _handle_feedback_data with post data %s", pprint.pformat(post))
request.env['payment.transaction'].sudo()._handle_feedback_data('transfer', post)
return werkzeug.utils.redirect('/payment/status')
@@ -1,23 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<odoo noupdate="1">
<record id="payment.payment_acquirer_transfer" model="payment.acquirer">
<field name="provider">transfer</field>
<field name="state">enabled</field>
<field name="redirect_form_view_id" ref="redirect_form"/>
<field name="support_authorization">False</field>
<field name="support_fees_computation">False</field>
<field name="support_tokenization">False</field>
<!-- Clear the default value to trigger the computation of the message -->
<field name="pending_msg" eval="False"/>
</record>
<record id="payment.payment_acquirer_transfer" model="payment.acquirer">
<field name="name">Wire Transfer</field>
<field name="image_128" type="base64" file="payment_transfer/static/src/img/transfer_icon.png"/>
<field name="provider">transfer</field>
<field name="state">enabled</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="transfer_form"/>
<field name="pending_msg">
<![CDATA[
<h3>Please make a payment to: </h3>
<ul>
<li>Bank:&nbsp;</li>
<li>Account Number:</li>
<li>Account Holder: </li>
</ul>]]>
</field>
</record>
</data>
</odoo>
+3 -2
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import payment
from . import payment_acquirer
from . import payment_transaction
-103
View File
@@ -1,103 +0,0 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.tools.float_utils import float_compare
import logging
import pprint
_logger = logging.getLogger(__name__)
class TransferPaymentAcquirer(models.Model):
_inherit = 'payment.acquirer'
provider = fields.Selection(selection_add=[
('transfer', 'Manual Payment')
], default='transfer', ondelete={'transfer': 'set default'})
@api.model
def _create_missing_journal_for_acquirers(self, company=None):
# By default, the wire transfer method uses the default Bank journal.
company = company or self.env.company
acquirers = self.env['payment.acquirer'].search(
[('provider', '=', 'transfer'), ('journal_id', '=', False), ('company_id', '=', company.id)])
bank_journal = self.env['account.journal'].search(
[('type', '=', 'bank'), ('company_id', '=', company.id)], limit=1)
if bank_journal:
acquirers.write({'journal_id': bank_journal.id})
return super(TransferPaymentAcquirer, self)._create_missing_journal_for_acquirers(company=company)
def transfer_get_form_action_url(self):
return '/payment/transfer/feedback'
def _format_transfer_data(self):
company_id = self.env.company.id
# filter only bank accounts marked as visible
journals = self.env['account.journal'].search([('type', '=', 'bank'), ('company_id', '=', company_id)])
accounts = journals.mapped('bank_account_id').name_get()
bank_title = _('Bank Accounts') if len(accounts) > 1 else _('Bank Account')
bank_accounts = ''.join(['<ul>'] + ['<li>%s</li>' % name for id, name in accounts] + ['</ul>'])
post_msg = _('''<div>
<h3>Please use the following transfer details</h3>
<h4>%(bank_title)s</h4>
%(bank_accounts)s
<h4>Communication</h4>
<p>Please use the order name as communication reference.</p>
</div>''') % {
'bank_title': bank_title,
'bank_accounts': bank_accounts,
}
return post_msg
@api.model
def create(self, values):
""" Hook in create to create a default pending_msg. This is done in create
to have access to the name and other creation values. If no pending_msg
or a void pending_msg is given at creation, generate a default one. """
if values.get('provider') == 'transfer' and not values.get('pending_msg'):
values['pending_msg'] = self._format_transfer_data()
return super(TransferPaymentAcquirer, self).create(values)
def write(self, values):
""" Hook in write to create a default pending_msg. See create(). """
if not values.get('pending_msg', False) and all(not acquirer.pending_msg and acquirer.provider != 'transfer' for acquirer in self) and values.get('provider') == 'transfer':
values['pending_msg'] = self._format_transfer_data()
return super(TransferPaymentAcquirer, self).write(values)
class TransferPaymentTransaction(models.Model):
_inherit = 'payment.transaction'
@api.model
def _transfer_form_get_tx_from_data(self, data):
reference, amount, currency_name = data.get('reference'), data.get('amount'), data.get('currency_name')
tx = self.search([('reference', '=', reference)])
if not tx or len(tx) > 1:
error_msg = _('received data for reference %s') % (pprint.pformat(reference))
if not tx:
error_msg += _('; no order found')
else:
error_msg += _('; multiple order found')
_logger.info(error_msg)
raise ValidationError(error_msg)
return tx
def _transfer_form_get_invalid_parameters(self, data):
invalid_parameters = []
if float_compare(float(data.get('amount') or '0.0'), self.amount, 2) != 0:
invalid_parameters.append(('amount', data.get('amount'), '%.2f' % self.amount))
if data.get('currency') != self.currency_id.name:
invalid_parameters.append(('currency', data.get('currency'), self.currency_id.name))
return invalid_parameters
def _transfer_form_validate(self, data):
_logger.info('Validated transfer payment for tx %s: set as pending' % (self.reference))
self._set_transaction_pending()
return True
@@ -0,0 +1,79 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
class PaymentAcquirer(models.Model):
_inherit = 'payment.acquirer'
provider = fields.Selection(
selection_add=[('transfer', "Wire Transfer")], default='transfer',
ondelete={'transfer': 'set default'})
qr_code = fields.Boolean(
string="Enable QR Codes", help="Enable the use of QR-codes when paying by wire transfer.")
@api.depends('provider')
def _compute_view_configuration_fields(self):
""" Override of payment to hide the credentials page.
:return: None
"""
super()._compute_view_configuration_fields()
self.filtered(lambda acq: acq.provider == 'transfer').write({
'show_credentials_page': False,
'show_payment_icon_ids': False,
'show_pre_msg': False,
'show_done_msg': False,
'show_cancel_msg': False,
})
@api.model
def _create_missing_journals(self, company=None):
""" Override of payment to assign the default Bank journal instead or Electronic. """
# Search for transfer acquirers having no journal
company = company or self.env.company
transfer_acquirers = self.env['payment.acquirer'].search([
('provider', '=', 'transfer'),
('journal_id', '=', False),
('company_id', '=', company.id)
])
if transfer_acquirers:
# Pick the Bank journal
bank_journal = self.env['account.journal'].search(
[('type', '=', 'bank'), ('company_id', '=', company.id)], limit=1
)
if bank_journal:
transfer_acquirers.write({'journal_id': bank_journal.id})
return super()._create_missing_journals(company=company)
@api.model_create_multi
def create(self, values_list):
""" Make sure to have a pending_msg set. """
# This is done here and not in a default to have access to all required values.
acquirers = super().create(values_list)
acquirers._transfer_ensure_pending_msg_is_set()
return acquirers
def write(self, values):
""" Make sure to have a pending_msg set. """
# This is done here and not in a default to have access to all required values.
res = super().write(values)
self._transfer_ensure_pending_msg_is_set()
return res
def _transfer_ensure_pending_msg_is_set(self):
for acquirer in self.filtered(lambda a: a.provider == 'transfer' and not a.pending_msg):
company_id = acquirer.company_id.id
# filter only bank accounts marked as visible
accounts = self.env['account.journal'].search([
('type', '=', 'bank'), ('company_id', '=', company_id)
]).bank_account_id
acquirer.pending_msg = f'<div>' \
f'<h3>{_("Please use the following transfer details")}</h3>' \
f'<h4>{_("Bank Account") if len(accounts) == 1 else _("Bank Accounts")}</h4>' \
f'<ul>{"".join(f"<li>{account.display_name}</li>" for account in accounts)}</ul>' \
f'<h4>{_("Communication")}</h4>' \
f'<p>{_("Please use the order name as communication reference.")}</p>' \
f'</div>'
@@ -0,0 +1,93 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import _, api, models
from odoo.exceptions import ValidationError
from odoo.addons.payment_transfer.controllers.main import TransferController
_logger = logging.getLogger(__name__)
class PaymentTransaction(models.Model):
_inherit = 'payment.transaction'
def _get_specific_rendering_values(self, processing_values):
""" Override of payment to return Transfer-specific rendering values.
Note: self.ensure_one() from `_get_processing_values`
:param dict processing_values: The generic and specific processing values of the transaction
:return: The dict of acquirer-specific processing values
:rtype: dict
"""
res = super()._get_specific_rendering_values(processing_values)
if self.provider != 'transfer':
return res
return {
'api_url': TransferController._accept_url,
'reference': self.reference,
}
@api.model
def _get_tx_from_feedback_data(self, provider, data):
""" Override of payment to find the transaction based on transfer data.
:param str provider: The provider of the acquirer that handled the transaction
:param dict data: The transfer feedback data
:return: The transaction if found
:rtype: recordset of `payment.transaction`
:raise: ValidationError if the data match no transaction
"""
tx = super()._get_tx_from_feedback_data(provider, data)
if provider != 'transfer':
return tx
reference = data.get('reference')
tx = self.search([('reference', '=', reference), ('provider', '=', 'transfer')])
if not tx:
raise ValidationError(
"Wire Transfer: " + _("No transaction found matching reference %s.", reference)
)
return tx
def _process_feedback_data(self, data):
""" Override of payment to process the transaction based on transfer data.
Note: self.ensure_one()
:param dict data: The transfer feedback data
:return: None
"""
super()._process_feedback_data(data)
if self.provider != 'transfer':
return
_logger.info(
"validated transfer payment for tx with reference %s: set as pending", self.reference
)
self._set_pending()
def _log_received_message(self):
""" Override of payment to remove transfer acquirer from the recordset.
:return: None
"""
other_provider_txs = self.filtered(lambda t: t.provider != 'transfer')
super(PaymentTransaction, other_provider_txs)._log_received_message()
def _get_sent_message(self):
""" Override of payment to return a different message.
:return: The 'transaction sent' message
:rtype: str
"""
message = super()._get_sent_message()
if self.provider == 'transfer':
message = _(
"The customer has selected %(acq_name)s to make the payment.",
acq_name=self.acquirer_id.name
)
return message
@@ -1,14 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<template id="transfer_form">
<input type="hidden" name="data_set" t-att-data-action-url="tx_url" data-remove-me=""/>
<t t-if="return_url">
<input type="hidden" name='return_url' t-att-value='return_url'/>
<template id="redirect_form">
<form t-att-action="api_url" method="post">
<input type="hidden" name="reference" t-att-value="reference"/>
</form>
</template>
<template id="transfer_transaction_status" inherit_id="payment.transaction_status">
<xpath expr="//span[@t-raw='tx.acquirer_id.sudo().pending_msg']" position="after">
<t t-if="tx.acquirer_id.sudo().provider == 'transfer'">
<div t-if="tx.reference">
<strong>Communication: </strong><span t-esc="tx.reference"/>
</div>
<div t-if="tx.acquirer_id.sudo().qr_code">
<t t-set="qr_code" t-value="tx.acquirer_id.sudo().journal_id.bank_account_id.build_qr_code_url(tx.amount, tx.reference, None, tx.currency_id, tx.partner_id)"/>
<div t-if="qr_code" class="mt-2">
<h3>Or scan me with your banking app.</h3>
<img class="border border-dark rounded" t-att-src="qr_code"/>
</div>
</div>
</t>
<input type="hidden" name='reference' t-att-value='reference'/>
<input type="hidden" name='amount' t-att-value='amount'/>
<input type="hidden" name='currency' t-att-value='currency.name'/>
</template>
</data>
</xpath>
</template>
</odoo>
@@ -1,22 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="payment_acquirer_view_form_inherit_transfer" model="ir.ui.view">
<field name="name">payment.acquirer.view.form.inherit.transfer</field>
<record id="payment_acquirer_form" model="ir.ui.view">
<field name="name">Wire Transfer Acquirer Form</field>
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="inherit_id" ref="payment.payment_acquirer_form"/>
<field name="arch" type="xml">
<page name="acquirer_credentials" position="attributes">
<attribute name="attrs">{'invisible': [('provider', 'in', ['manual', 'transfer'])]}</attribute>
</page>
<field name="pre_msg" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</field>
<field name="done_msg" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</field>
<field name="cancel_msg" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
<field name="capture_manually" position="after">
<field name="qr_code" attrs="{'invisible': [('provider', '!=', 'transfer')]}" />
</field>
</field>
</record>
</odoo>