[MOV] payment_transfer: rename to payment_custom

The module `payment_transfer` was originally meant to implement a
payment with Wire Transfer flow, which it does not exactly do since all
it does it making transactions follow the payment flow until their
`pending_msg` field's content is shown to the customer. Because of that,
other modules (`website_delivery_ups`, `website_sale_picking`) started
duplicating the base acquirer Wire Transfer to create new payment modes
such as Cash on Delivery and Pay in Store.

To better prepare for a proper dinstinction of the custom modes enabled
by other modules, this commit renames the module `payment_transfer` to
`payment_custom`.

The module `payment_transfer`'s `auto-install` key is also set to
`False` since we no longer want Wire Transfer to be the default payment
acquirer for new databases.

task-2853489

Part-of: odoo/odoo#99400
This commit is contained in:
Victor Feyens
2022-09-06 12:01:37 +02:00
parent 852c2a13ed
commit 5f41f15e6b
93 changed files with 123 additions and 96 deletions
@@ -245,8 +245,8 @@
<field name="sequence">30</field> <field name="sequence">30</field>
<field name="image_128" <field name="image_128"
type="base64" type="base64"
file="payment_transfer/static/description/icon.png"/> file="payment_custom/static/description/icon.png"/>
<field name="module_id" ref="base.module_payment_transfer"/> <field name="module_id" ref="base.module_payment_custom"/>
</record> </record>
</odoo> </odoo>
@@ -42,7 +42,7 @@ class PaymentWizard(models.TransientModel):
def _get_manual_payment_acquirer(self, env=None): def _get_manual_payment_acquirer(self, env=None):
if env is None: if env is None:
env = self.env env = self.env
module_id = env.ref('base.module_payment_transfer').id module_id = env.ref('base.module_payment_custom').id
return env['payment.acquirer'].search([('module_id', '=', module_id), return env['payment.acquirer'].search([('module_id', '=', module_id),
('company_id', '=', env.company.id)], limit=1) ('company_id', '=', env.company.id)], limit=1)
@@ -7,4 +7,4 @@ from odoo.addons.payment import reset_payment_acquirer
def uninstall_hook(cr, registry): def uninstall_hook(cr, registry):
reset_payment_acquirer(cr, registry, 'transfer') reset_payment_acquirer(cr, registry, 'custom')
@@ -1,18 +1,16 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
{ {
'name': 'Wire Transfer Payment Acquirer', 'name': 'Custom Payment Modes',
'version': '2.0', 'version': '2.0',
'category': 'Accounting/Payment Acquirers', 'category': 'Accounting/Payment Acquirers',
'summary': 'Payment Acquirer: Wire Transfer Implementation', 'summary': 'Payment Acquirer: Custom payment modes',
'description': """Wire Transfer Payment Acquirer""",
'depends': ['payment'], 'depends': ['payment'],
'data': [ 'data': [
'views/payment_custom_templates.xml',
'views/payment_views.xml', 'views/payment_views.xml',
'views/payment_transfer_templates.xml',
'data/payment_acquirer_data.xml', 'data/payment_acquirer_data.xml',
], ],
'auto_install': True,
'assets': { 'assets': {
'web.assets_frontend': [ 'web.assets_frontend': [
'payment_transfer/static/src/js/post_processing.js', 'payment_transfer/static/src/js/post_processing.js',
+18
View File
@@ -0,0 +1,18 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import pprint
from odoo.http import Controller, request, route
_logger = logging.getLogger(__name__)
class CustomController(Controller):
_process_url = '/payment/custom/process'
@route(_process_url, type='http', auth='public', methods=['POST'], csrf=False)
def custom_process_transaction(self, **post):
_logger.info("Handling custom processing with data:\n%s", pprint.pformat(post))
request.env['payment.transaction'].sudo()._handle_notification_data('custom', post)
return request.redirect('/payment/status')
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="payment.payment_acquirer_transfer" model="payment.acquirer">
<field name="provider">custom</field>
<field name="redirect_form_view_id" ref="redirect_form"/>
<!-- Clear the default value before recomputing the pending_msg -->
<field name="pending_msg" eval="False"/>
</record>
<function model="payment.acquirer"
name="_transfer_ensure_pending_msg_is_set"
eval="[[ref('payment.payment_acquirer_transfer')]]"/>
</odoo>
@@ -7,8 +7,8 @@ class PaymentAcquirer(models.Model):
_inherit = 'payment.acquirer' _inherit = 'payment.acquirer'
provider = fields.Selection( provider = fields.Selection(
selection_add=[('transfer', "Wire Transfer")], default='transfer', selection_add=[('custom', "Custom")],
ondelete={'transfer': 'set default'}) ondelete={'custom': 'set default'})
qr_code = fields.Boolean( qr_code = fields.Boolean(
string="Enable QR Codes", help="Enable the use of QR-codes when paying by wire transfer.") string="Enable QR Codes", help="Enable the use of QR-codes when paying by wire transfer.")
@@ -19,7 +19,7 @@ class PaymentAcquirer(models.Model):
:return: None :return: None
""" """
super()._compute_view_configuration_fields() super()._compute_view_configuration_fields()
self.filtered(lambda acq: acq.provider == 'transfer').update({ self.filtered(lambda acq: acq.provider == 'custom').update({
'show_credentials_page': False, 'show_credentials_page': False,
'show_payment_icon_ids': False, 'show_payment_icon_ids': False,
'show_pre_msg': False, 'show_pre_msg': False,
@@ -27,23 +27,8 @@ class PaymentAcquirer(models.Model):
'show_cancel_msg': False, 'show_cancel_msg': False,
}) })
@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): def _transfer_ensure_pending_msg_is_set(self):
for acquirer in self.filtered(lambda a: a.provider == 'transfer' and not a.pending_msg): for acquirer in self.filtered(lambda a: a.provider == 'custom' and not a.pending_msg):
company_id = acquirer.company_id.id company_id = acquirer.company_id.id
# filter only bank accounts marked as visible # filter only bank accounts marked as visible
accounts = self.env['account.journal'].search([ accounts = self.env['account.journal'].search([
@@ -5,7 +5,7 @@ import logging
from odoo import _, api, models from odoo import _, api, models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from odoo.addons.payment_transfer.controllers.main import TransferController from odoo.addons.payment_custom.controllers.main import CustomController
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -14,7 +14,7 @@ class PaymentTransaction(models.Model):
_inherit = 'payment.transaction' _inherit = 'payment.transaction'
def _get_specific_rendering_values(self, processing_values): def _get_specific_rendering_values(self, processing_values):
""" Override of payment to return Transfer-specific rendering values. """ Override of payment to return custom-specific rendering values.
Note: self.ensure_one() from `_get_processing_values` Note: self.ensure_one() from `_get_processing_values`
@@ -23,16 +23,16 @@ class PaymentTransaction(models.Model):
:rtype: dict :rtype: dict
""" """
res = super()._get_specific_rendering_values(processing_values) res = super()._get_specific_rendering_values(processing_values)
if self.provider != 'transfer': if self.provider != 'custom':
return res return res
return { return {
'api_url': TransferController._accept_url, 'api_url': CustomController._process_url,
'reference': self.reference, 'reference': self.reference,
} }
def _get_tx_from_notification_data(self, provider, notification_data): def _get_tx_from_notification_data(self, provider, notification_data):
""" Override of payment to find the transaction based on transfer data. """ Override of payment to find the transaction based on custom data.
:param str provider: The provider of the acquirer that handled the transaction :param str provider: The provider of the acquirer that handled the transaction
:param dict notification_data: The notification feedback data :param dict notification_data: The notification feedback data
@@ -41,11 +41,11 @@ class PaymentTransaction(models.Model):
:raise: ValidationError if the data match no transaction :raise: ValidationError if the data match no transaction
""" """
tx = super()._get_tx_from_notification_data(provider, notification_data) tx = super()._get_tx_from_notification_data(provider, notification_data)
if provider != 'transfer' or len(tx) == 1: if provider != 'custom' or len(tx) == 1:
return tx return tx
reference = notification_data.get('reference') reference = notification_data.get('reference')
tx = self.search([('reference', '=', reference), ('provider', '=', 'transfer')]) tx = self.search([('reference', '=', reference), ('provider', '=', 'custom')])
if not tx: if not tx:
raise ValidationError( raise ValidationError(
"Wire Transfer: " + _("No transaction found matching reference %s.", reference) "Wire Transfer: " + _("No transaction found matching reference %s.", reference)
@@ -53,29 +53,29 @@ class PaymentTransaction(models.Model):
return tx return tx
def _process_notification_data(self, notification_data): def _process_notification_data(self, notification_data):
""" Override of payment to process the transaction based on transfer data. """ Override of payment to process the transaction based on custom data.
Note: self.ensure_one() Note: self.ensure_one()
:param dict notification_data: The transfer data :param dict notification_data: The custom data
:return: None :return: None
""" """
super()._process_notification_data(notification_data) super()._process_notification_data(notification_data)
if self.provider != 'transfer': if self.provider != 'custom':
return return
_logger.info( _logger.info(
"validated transfer payment for transaction with reference %s: set as pending", "validated custom payment for transaction with reference %s: set as pending",
self.reference self.reference
) )
self._set_pending() self._set_pending()
def _log_received_message(self): def _log_received_message(self):
""" Override of payment to remove transfer acquirer from the recordset. """ Override of `payment` to remove custom acquirers from the recordset.
:return: None :return: None
""" """
other_provider_txs = self.filtered(lambda t: t.provider != 'transfer') other_provider_txs = self.filtered(lambda t: t.provider != 'custom')
super(PaymentTransaction, other_provider_txs)._log_received_message() super(PaymentTransaction, other_provider_txs)._log_received_message()
def _get_sent_message(self): def _get_sent_message(self):
@@ -85,7 +85,7 @@ class PaymentTransaction(models.Model):
:rtype: str :rtype: str
""" """
message = super()._get_sent_message() message = super()._get_sent_message()
if self.provider == 'transfer': if self.provider == 'custom':
message = _( message = _(
"The customer has selected %(acq_name)s to make the payment.", "The customer has selected %(acq_name)s to make the payment.",
acq_name=self.acquirer_id.name acq_name=self.acquirer_id.name

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -1,4 +1,4 @@
odoo.define('payment_transfer.post_processing', require => { odoo.define('payment_custom.post_processing', require => {
'use strict'; 'use strict';
const paymentPostProcessing = require('payment.post_processing'); const paymentPostProcessing = require('payment.post_processing');
@@ -6,7 +6,7 @@ odoo.define('payment_transfer.post_processing', require => {
paymentPostProcessing.include({ paymentPostProcessing.include({
/** /**
* Don't wait for the transaction to be confirmed before redirecting customers to the * Don't wait for the transaction to be confirmed before redirecting customers to the
* landing route because Wire Transfer transactions remain in the state 'pending' forever. * landing route because custom transactions remain in the state 'pending' forever.
* *
* @override method from `payment.post_processing` * @override method from `payment.post_processing`
* @param {Object} display_values_list - The post-processing values of the transactions * @param {Object} display_values_list - The post-processing values of the transactions
@@ -15,7 +15,7 @@ odoo.define('payment_transfer.post_processing', require => {
// In almost every case, there will be a single transaction to display. If there are // In almost every case, there will be a single transaction to display. If there are
// more than one transaction, the last one will most likely be the one that counts. We // more than one transaction, the last one will most likely be the one that counts. We
// use that one to redirect the user to the landing page. // use that one to redirect the user to the landing page.
if (display_values_list.length > 0 && display_values_list[0].provider === 'transfer') { if (display_values_list.length > 0 && display_values_list[0].provider === 'custom') {
window.location = display_values_list[0].landing_route; window.location = display_values_list[0].landing_route;
} else { } else {
return this._super(...arguments); return this._super(...arguments);
@@ -7,9 +7,9 @@
</form> </form>
</template> </template>
<template id="transfer_transaction_status" inherit_id="payment.transaction_status"> <template id="custom_transaction_status" inherit_id="payment.transaction_status">
<xpath expr="//div[@id='o_payment_status_alert']" position="inside"> <xpath expr="//div[@id='o_payment_status_alert']" position="inside">
<t t-if="tx.acquirer_id.sudo().provider == 'transfer'"> <t t-if="tx.acquirer_id.sudo().provider == 'custom'">
<div t-if="tx.reference"> <div t-if="tx.reference">
<strong>Communication: </strong><span t-esc="tx.reference"/> <strong>Communication: </strong><span t-esc="tx.reference"/>
</div> </div>
@@ -2,18 +2,18 @@
<odoo> <odoo>
<record id="payment_acquirer_form" model="ir.ui.view"> <record id="payment_acquirer_form" model="ir.ui.view">
<field name="name">Wire Transfer Acquirer Form</field> <field name="name">Custom Acquirer Form</field>
<field name="model">payment.acquirer</field> <field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.payment_acquirer_form"/> <field name="inherit_id" ref="payment.payment_acquirer_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="capture_manually" position="after"> <field name="capture_manually" position="after">
<field name="qr_code" attrs="{'invisible': [('provider', '!=', 'transfer')]}" /> <field name="qr_code" attrs="{'invisible': [('provider', '!=', 'custom')]}" />
</field> </field>
<xpath expr="//group[@name='payment_followup']" position="attributes"> <group name="payment_followup" position="attributes">
<attribute name="attrs"> <attribute name="attrs">
{'invisible': [('provider', '=', 'transfer')]} {'invisible': [('provider', '=', 'custom')]}
</attribute> </attribute>
</xpath> </group>
</field> </field>
</record> </record>
@@ -1,19 +0,0 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import pprint
from odoo import http
from odoo.http import request
_logger = logging.getLogger(__name__)
class TransferController(http.Controller):
_accept_url = '/payment/transfer/feedback'
@http.route(_accept_url, type='http', auth='public', methods=['POST'], csrf=False)
def transfer_form_feedback(self, **post):
_logger.info("handling redirection from Transfer with data:\n%s", pprint.pformat(post))
request.env['payment.transaction'].sudo()._handle_notification_data('transfer', post)
return request.redirect('/payment/status')
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="payment.payment_acquirer_transfer" model="payment.acquirer">
<field name="provider">transfer</field>
<field name="state">enabled</field>
<field name="is_published">True</field>
<field name="redirect_form_view_id" ref="redirect_form"/>
<!-- Clear the default value to trigger the computation of the message -->
<field name="pending_msg" eval="False"/>
</record>
</odoo>
+1 -1
View File
@@ -39,7 +39,7 @@ class PaymentTransaction(models.Model):
sales_orders = record.sale_order_ids.filtered(lambda so: so.state in ['draft', 'sent']) sales_orders = record.sale_order_ids.filtered(lambda so: so.state in ['draft', 'sent'])
sales_orders.filtered(lambda so: so.state == 'draft').with_context(tracking_disable=True).write({'state': 'sent'}) sales_orders.filtered(lambda so: so.state == 'draft').with_context(tracking_disable=True).write({'state': 'sent'})
if record.acquirer_id.provider == 'transfer': if record.acquirer_id.provider == 'custom':
for so in record.sale_order_ids: for so in record.sale_order_ids:
so.reference = record._compute_sale_order_reference(so) so.reference = record._compute_sale_order_reference(so)
# send order confirmation mail # send order confirmation mail
@@ -9,6 +9,14 @@ from odoo.tests import tagged
class TestWEventBoothExhibitorCommon(HttpCaseWithUserDemo, HttpCaseWithUserPortal): class TestWEventBoothExhibitorCommon(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
def test_register(self): def test_register(self):
if self.env['ir.module.module']._get('payment_custom').state != 'installed':
self.skipTest("Transfer acquirer is not installed")
self.env.ref('payment.payment_acquirer_transfer').write({
'state': 'enabled',
'is_published': True,
})
self.browser_js( self.browser_js(
'/event', '/event',
'odoo.__DEBUG__.services["web_tour.tour"].run("webooth_exhibitor_register")', 'odoo.__DEBUG__.services["web_tour.tour"].run("webooth_exhibitor_register")',
@@ -11,15 +11,20 @@ odoo.define("website_event_booth_sale_exhibitor.tour", function (require) {
trigger: '.btn-primary[href="/shop/confirm_order"]', trigger: '.btn-primary[href="/shop/confirm_order"]',
run: 'click', run: 'click',
}, { }, {
content: 'Pay your order', content: "Select `Wire Transfer` payment method",
trigger: '.btn-primary[name="o_payment_submit_button"]', trigger: '#payment_method label:contains("Wire Transfer")',
run: 'click',
}, { }, {
trigger: 'h3:contains("Please use the following transfer details")', content: "Pay",
run: function () {}, //Either there are multiple payment methods, and one is checked, either there is only one, and therefore there are no radio inputs
}] // extra_trigger: '#payment_method input:checked,#payment_method:not(:has("input:radio:visible"))',
trigger: 'button[name="o_payment_submit_button"]:visible:not(:disabled)',
}, {
content: "Last step",
trigger: '.oe_website_sale_tx_status:contains("Please use the following transfer details")',
timeout: 30000,
}];
} }
}); });
}); });
@@ -14,6 +14,15 @@ class TestUi(HttpCaseWithUserDemo):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
if self.env['ir.module.module']._get('payment_custom').state != 'installed':
self.skipTest("Transfer acquirer is not installed")
self.env.ref('payment.payment_acquirer_transfer').write({
'state': 'enabled',
'is_published': True,
})
self.event_2 = self.env['event.event'].create({ self.event_2 = self.env['event.event'].create({
'name': 'Conference for Architects TEST', 'name': 'Conference for Architects TEST',
'user_id': self.env.ref('base.user_admin').id, 'user_id': self.env.ref('base.user_admin').id,
@@ -54,7 +63,6 @@ class TestUi(HttpCaseWithUserDemo):
'seats_max': 2, 'seats_max': 2,
}]) }])
# flush event to ensure having tickets available in the tests # flush event to ensure having tickets available in the tests
self.env.flush_all() self.env.flush_all()
@@ -67,12 +67,33 @@ class TestUi(HttpCaseWithUserDemo):
self.start_tour(self.env['website'].get_client_action_url('/shop'), 'shop', login='admin') self.start_tour(self.env['website'].get_client_action_url('/shop'), 'shop', login='admin')
def test_02_admin_checkout(self): def test_02_admin_checkout(self):
if self.env['ir.module.module']._get('payment_custom').state != 'installed':
self.skipTest("Transfer acquirer is not installed")
self.env.ref('payment.payment_acquirer_transfer').write({
'state': 'enabled',
'is_published': True,
})
self.start_tour("/", 'shop_buy_product', login="admin") self.start_tour("/", 'shop_buy_product', login="admin")
def test_03_demo_checkout(self): def test_03_demo_checkout(self):
if self.env['ir.module.module']._get('payment_custom').state != 'installed':
self.skipTest("Transfer acquirer is not installed")
self.env.ref('payment.payment_acquirer_transfer').write({
'state': 'enabled',
'is_published': True,
})
self.start_tour("/", 'shop_buy_product', login="demo") self.start_tour("/", 'shop_buy_product', login="demo")
def test_04_admin_website_sale_tour(self): def test_04_admin_website_sale_tour(self):
if self.env['ir.module.module']._get('payment_custom').state != 'installed':
self.skipTest("Transfer acquirer is not installed")
self.env.ref('payment.payment_acquirer_transfer').write({
'state': 'enabled',
'is_published': True,
})
tax_group = self.env['account.tax.group'].create({'name': 'Tax 15%'}) tax_group = self.env['account.tax.group'].create({'name': 'Tax 15%'})
tax = self.env['account.tax'].create({ tax = self.env['account.tax'].create({
'name': 'Tax 15%', 'name': 'Tax 15%',
+1 -1
View File
@@ -8,7 +8,7 @@
'description': """ 'description': """
Allows customers to pay for their orders at a shop, instead of paying online. Allows customers to pay for their orders at a shop, instead of paying online.
""", """,
'depends': ['website_sale_delivery', 'payment_transfer'], 'depends': ['website_sale_delivery', 'payment_custom'],
'data': [ 'data': [
'data/website_sale_picking_data.xml', 'data/website_sale_picking_data.xml',
'views/res_config_settings_views.xml', 'views/res_config_settings_views.xml',
@@ -3,10 +3,10 @@
<record id="payment_acquirer_onsite" model="payment.acquirer"> <record id="payment_acquirer_onsite" model="payment.acquirer">
<field name="name">Pay in store when picking the product</field> <field name="name">Pay in store when picking the product</field>
<field name="provider">transfer</field> <field name="provider">custom</field>
<field name="state">enabled</field> <field name="state">enabled</field>
<field name="is_onsite_acquirer">true</field> <field name="is_onsite_acquirer">true</field>
<field name="redirect_form_view_id" ref="payment_transfer.redirect_form"/> <field name="redirect_form_view_id" ref="payment_custom.redirect_form"/>
<field name="pending_msg" type="html"> <field name="pending_msg" type="html">
<p> <p>
<i>Your order has been saved.</i> Please come to the store to pay for your products <i>Your order has been saved.</i> Please come to the store to pay for your products
@@ -23,6 +23,7 @@ class PaymentAcquirer(models.Model):
if not onsite_carriers: if not onsite_carriers:
compatible_acquirers -= self.env.ref('website_sale_picking.payment_acquirer_onsite') compatible_acquirers -= self.env.ref('website_sale_picking.payment_acquirer_onsite')
# FIXME MULTI COMPANY !!!
# Show onsite payment only if it is a physical product # Show onsite payment only if it is a physical product
order = self.env['sale.order'].browse(sale_order_id).exists() order = self.env['sale.order'].browse(sale_order_id).exists()
if not any(product.type in ['consu', 'product'] for product in order.order_line.product_id): if not any(product.type in ['consu', 'product'] for product in order.order_line.product_id):