diff --git a/addons/payment_transfer/__init__.py b/addons/payment_transfer/__init__.py index 1d52c9851d2..401b3fe0d4e 100644 --- a/addons/payment_transfer/__init__.py +++ b/addons/payment_transfer/__init__.py @@ -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') diff --git a/addons/payment_transfer/__manifest__.py b/addons/payment_transfer/__manifest__.py index 9f1f032bd70..d5a52cb7686 100644 --- a/addons/payment_transfer/__manifest__.py +++ b/addons/payment_transfer/__manifest__.py @@ -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', } diff --git a/addons/payment_transfer/controllers/__init__.py b/addons/payment_transfer/controllers/__init__.py index 65a8c12013d..80ee4da1c5e 100644 --- a/addons/payment_transfer/controllers/__init__.py +++ b/addons/payment_transfer/controllers/__init__.py @@ -1,3 +1,3 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. from . import main diff --git a/addons/payment_transfer/controllers/main.py b/addons/payment_transfer/controllers/main.py index 038299ccd44..175d24793ab 100644 --- a/addons/payment_transfer/controllers/main.py +++ b/addons/payment_transfer/controllers/main.py @@ -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') diff --git a/addons/payment_transfer/data/payment_acquirer_data.xml b/addons/payment_transfer/data/payment_acquirer_data.xml index a8d76fbfd5e..86eebe54d61 100644 --- a/addons/payment_transfer/data/payment_acquirer_data.xml +++ b/addons/payment_transfer/data/payment_acquirer_data.xml @@ -1,23 +1,15 @@ - - + + + + transfer + enabled + + False + False + False + + + - - Wire Transfer - - transfer - enabled - - - - Please make a payment to: -
    -
  • Bank: 
  • -
  • Account Number:
  • -
  • Account Holder:
  • -
]]> -
-
-
diff --git a/addons/payment_transfer/models/__init__.py b/addons/payment_transfer/models/__init__.py index ef12533682f..8e04237423a 100644 --- a/addons/payment_transfer/models/__init__.py +++ b/addons/payment_transfer/models/__init__.py @@ -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 diff --git a/addons/payment_transfer/models/payment.py b/addons/payment_transfer/models/payment.py deleted file mode 100644 index b748fa50349..00000000000 --- a/addons/payment_transfer/models/payment.py +++ /dev/null @@ -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(['']) - post_msg = _('''
-

Please use the following transfer details

-

%(bank_title)s

-%(bank_accounts)s -

Communication

-

Please use the order name as communication reference.

-
''') % { - '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 diff --git a/addons/payment_transfer/models/payment_acquirer.py b/addons/payment_transfer/models/payment_acquirer.py new file mode 100644 index 00000000000..1d7d51f4b16 --- /dev/null +++ b/addons/payment_transfer/models/payment_acquirer.py @@ -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'
' \ + f'

{_("Please use the following transfer details")}

' \ + f'

{_("Bank Account") if len(accounts) == 1 else _("Bank Accounts")}

' \ + f'' \ + f'

{_("Communication")}

' \ + f'

{_("Please use the order name as communication reference.")}

' \ + f'
' diff --git a/addons/payment_transfer/models/payment_transaction.py b/addons/payment_transfer/models/payment_transaction.py new file mode 100644 index 00000000000..470869c59c3 --- /dev/null +++ b/addons/payment_transfer/models/payment_transaction.py @@ -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 diff --git a/addons/payment_transfer/views/payment_transfer_templates.xml b/addons/payment_transfer/views/payment_transfer_templates.xml index a3af2f4e318..f352f8e2a0d 100644 --- a/addons/payment_transfer/views/payment_transfer_templates.xml +++ b/addons/payment_transfer/views/payment_transfer_templates.xml @@ -1,14 +1,27 @@ - - + diff --git a/addons/payment_transfer/views/payment_views.xml b/addons/payment_transfer/views/payment_views.xml index 659c1cbfe52..7667e8670d7 100644 --- a/addons/payment_transfer/views/payment_views.xml +++ b/addons/payment_transfer/views/payment_views.xml @@ -1,22 +1,15 @@ - - payment.acquirer.view.form.inherit.transfer + + + Wire Transfer Acquirer Form payment.acquirer - + - - {'invisible': [('provider', 'in', ['manual', 'transfer'])]} - - - {'invisible': [('provider', '=', 'transfer')]} - - - {'invisible': [('provider', '=', 'transfer')]} - - - {'invisible': [('provider', '=', 'transfer')]} + + +