[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:
committed by
Antoine Vandevenne (anv)
parent
71c5278d9a
commit
aa514acf13
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: </li>
|
||||
<li>Account Number:</li>
|
||||
<li>Account Holder: </li>
|
||||
</ul>]]>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user