[IMP] payment: payment methods improvements

closes odoo/odoo#136525

Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
Valeriya(vchu)
2023-10-13 21:11:53 +00:00
parent 25423820ed
commit 997aa444cb
15 changed files with 209 additions and 37 deletions
@@ -79,7 +79,7 @@
</div>
</xpath>
<xpath expr="//div[@id='invoice_content']//div[hasclass('o_portal_html_view')]" position="before">
<div t-if="invoice.transaction_ids and invoice.amount_total and not success and not error" class="o_account_payment_tx_status" t-att-data-invoice-id="invoice.id">
<div t-if="invoice.get_portal_last_transaction() and invoice.amount_total and not success and not error" class="o_account_payment_tx_status" t-att-data-invoice-id="invoice.id">
<t t-call="payment.transaction_status">
<t t-set="tx" t-value="invoice.get_portal_last_transaction()"/>
</t>
+2 -2
View File
@@ -19,8 +19,8 @@
'views/portal_templates.xml',
# Model views.
'views/payment_method_views.xml',
'views/payment_provider_views.xml', # Depends on `action_payment_method`.
'views/payment_provider_views.xml',
'views/payment_method_views.xml', # Depends on `action_payment_provider`.
'views/payment_transaction_views.xml',
'views/payment_token_views.xml', # Depends on `action_payment_transaction_linked_to_token`.
'views/res_partner_views.xml',
+22 -1
View File
@@ -26,7 +26,7 @@
</record>
<record id="payment_method_unknown" model="payment.method">
<field name="name">Unknown</field>
<field name="name">Payment method</field>
<field name="code">unknown</field>
<field name="sequence">1000</field>
<field name="active">False</field>
@@ -3625,6 +3625,27 @@
/>
</record>
<record id="payment_method_boleto" model="payment.method">
<field name="name">Boleto</field>
<field name="code">boleto</field>
<field name="sequence">1000</field>
<field name="active">False</field>
<field name="image" type="base64" file="payment/static/img/cash_app_pay.png"/>
<field name="support_tokenization">False</field>
<field name="support_express_checkout">False</field>
<field name="support_refund"></field>
<field name="supported_country_ids"
eval="[Command.set([
ref('base.br'),
])]"
/>
<field name="supported_currency_ids"
eval="[Command.set([
ref('base.BRL'),
])]"
/>
</record>
<!-- === PAYMENT METHOD BRANDS === -->
<record id="payment_method_visa" model="payment.method">
@@ -380,6 +380,7 @@
ref('payment.payment_method_bancontact'),
ref('payment.payment_method_becs_direct_debit'),
ref('payment.payment_method_blik'),
ref('payment.payment_method_boleto'),
ref('payment.payment_method_card'),
ref('payment.payment_method_cash_app_pay'),
ref('payment.payment_method_clearpay'),
+21 -4
View File
@@ -53,17 +53,34 @@ class PaymentMethod(models.Model):
)
# Feature support fields.
support_tokenization = fields.Boolean(string="Tokenization Supported")
support_express_checkout = fields.Boolean(string="Express Checkout Supported")
support_tokenization = fields.Boolean(
string="Tokenization Supported",
help="Tokenization is the process of saving the payment details as a token that can later"
" be reused without having to enter the payment details again.",
)
support_express_checkout = fields.Boolean(
string="Express Checkout Supported",
help="Express checkout allows customers to pay faster by using a payment method that"
" provides all required billing and shipping information, thus allowing to skip the"
" checkout process.",
)
support_refund = fields.Selection(
string="Type of Refund Supported",
selection=[('full_only', "Full Only"), ('partial', "Partial")],
help="Refund is a feature allowing to refund customers directly from the payment in Odoo.",
)
supported_country_ids = fields.Many2many(
string="Supported Countries", comodel_name='res.country'
string="Supported Countries",
comodel_name='res.country',
help="The list of countries in which this payment method can be used (if the provider"
" allows it). In other countries, this payment method is not available to customers."
)
supported_currency_ids = fields.Many2many(
string="Supported Currencies", comodel_name='res.currency'
string="Supported Currencies",
comodel_name='res.currency',
help="The list of currencies for that are supported by this payment method (if the provider"
" allows it). When paying with another currency, this payment method is not available "
"to customers.",
)
#=== COMPUTE METHODS ===#
@@ -71,7 +71,7 @@
class="d-flex gap-3 align-items-center mt-4 mb-3 fs-6 small text-uppercase
fw-bolder"
>
<span>Saved payment methods</span>
<span>Your payment methods</span>
<hr class="flex-grow-1 m-0"/>
</h4>
<!-- === Body === -->
@@ -213,7 +213,7 @@
</div>
<!-- === Provider name (only for desktop and tablet) === -->
<div class="col d-none d-md-block">
<p t-att-class="'mb-0 small text-600'
<p name="o_payment_secured_by_desktop" t-att-class="'mb-0 small text-600'
+ (' ms-4 ms-md-0' if allow_token_selection else '')"
>
<span><i class="fa fa-lock"/> Secured by</span>
@@ -239,7 +239,9 @@
<t t-call="{{inline_form_xml_id}}"/>
</div>
<!-- === Provider name (only for mobile) === -->
<p class="align-self-end d-block d-md-none mb-0 small text-600">
<p name="o_payment_secured_by_mobile"
class="align-self-end d-block d-md-none mb-0 small text-600"
>
<span><i class="fa fa-lock"/> Secured by</span>
<span t-out="dict(provider_sudo._fields['code']._description_selection(
provider_sudo.env
@@ -348,7 +350,7 @@
</label>
</div>
<!-- === Provider name === -->
<p class="align-self-end mb-0 ms-auto small text-600">
<p name="o_payment_secured_by" class="align-self-end mb-0 ms-auto small text-600">
<span><i class="fa fa-lock"/> Secured by</span>
<span t-out="dict(provider_sudo._fields['code']._description_selection(
provider_sudo.env
+96 -11
View File
@@ -13,21 +13,72 @@
<h1><field name="name" placeholder="Name"/></h1>
</div>
<group>
<field name="code" groups="base.group_no_one"/>
<field name="code" readonly="id" groups="base.group_no_one"/>
<field name="primary_payment_method_id" invisible="is_primary"/>
<field name="active" widget="boolean_toggle"/>
<label for="supported_country_ids"/>
<div>
<field name="supported_country_ids"
class="oe_inline"
widget="many2many_tags"
readonly="1"
/>
<span class="oe_inline text-muted" invisible="supported_country_ids">
All countries are supported.
</span>
</div>
<label for="supported_currency_ids"/>
<div>
<field name="supported_currency_ids"
class="oe_inline"
widget="many2many_tags"
readonly="1"
/>
<span class="oe_inline text-muted" invisible="supported_currency_ids">
All currencies are supported.
</span>
</div>
</group>
<notebook>
<page string="Brands" name="brands" invisible="not is_primary">
<field name="brand_ids"/>
</page>
<page string="Supported By" name="providers" groups="base.group_no_one">
<field name="provider_ids">
<tree decoration-muted="state == 'disabled'">
<page string="Providers" name="providers">
<field name="provider_ids" readonly="1">
<tree decoration-muted="state == 'disabled'" editable="bottom">
<field name="name"/>
<field name="state"/>
</tree>
</field>
</page>
<page string="Brands" name="brands" invisible="not is_primary">
<field name="brand_ids"/>
</page>
<page string="Configuration"
name="configuration"
groups="base.group_no_one"
>
<div class="alert alert-warning" role="alert">
<i class="fa fa-exclamation-triangle"/> These properties are set to
match the behavior of providers and that of their integration with
Odoo regarding this payment method. Any change may result in errors
and should be tested on a test database first.
</div>
<group>
<field name="support_tokenization"/>
<field name="support_express_checkout"/>
<field name="support_refund" />
<field name="supported_country_ids"
widget="many2many_tags"
placeholder="Select countries. Leave empty to allow any."
/>
<field name="supported_currency_ids"
widget="many2many_tags"
placeholder="Select currencies. Leave empty to allow any."
/>
<field name="provider_ids"
string="Supported by"
widget="many2many_tags"
/>
</group>
</page>
</notebook>
</sheet>
</form>
@@ -41,13 +92,38 @@
<tree decoration-muted="not active">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="supported_country_ids" optional="hidden" widget="many2many_tags"/>
<field name="supported_currency_ids" optional="hidden" widget="many2many_tags"/>
<field name="active" widget="boolean_toggle"/>
</tree>
</field>
</record>
<record id="payment_method_kanban" model="ir.ui.view">
<field name="name">payment.method.kanban</field>
<field name="model">payment.method</field>
<field name="priority">1</field>
<field name="arch" type="xml">
<kanban>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_content oe_kanban_global_click">
<div class="row">
<div class="col-6">
<strong><field name="name"/></strong>
</div>
<div class="col-6">
<span class="float-end">
<field name="image" widget="image" class="oe_avatar"/>
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="payment_method_search" model="ir.ui.view">
<field name="name">payment.method.search</field>
<field name="model">payment.method</field>
@@ -62,11 +138,20 @@
<field name="name">Payment Methods</field>
<field name="res_model">payment.method</field>
<field name="view_mode">tree,kanban,form</field>
<field name="domain">[('is_primary', '=', True)]</field>
<field name="domain">[
('is_primary', '=', True),
('provider_ids.state', '!=', 'disabled')
]
</field>
<field name="context">{'active_test': False}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a payment method
No payment methods found for your payment providers.
</p>
<p>
<a type="action" class="text-primary" name="%(payment.action_payment_provider)d">
<i class="oi oi-arrow-right me-1"/> Configure a payment provider
</a>
</p>
</field>
</record>
@@ -78,6 +78,7 @@
<group name="provider_config">
<group string="Payment Form" name="payment_form">
<field name="payment_method_ids"
string="Payment Methods"
domain="[('is_primary', '=', True)]"
readonly="True"
widget="many2many_tags"
@@ -215,7 +216,15 @@
<field name="arch" type="xml">
<search>
<field name="name" string="provider" filter_domain="[('name', 'ilike', self)]"/>
<field name="code"/>
<field name="payment_method_ids"
string="payment method"
context="{'active_test': False}"
filter_domain="[
'|',
('payment_method_ids.name', 'ilike', self),
('payment_method_ids.code', 'ilike', self),
]"
/>
<filter name="provider_installed" string="Installed" domain="[('module_state', '=', 'installed')]"/>
<group expand="0" string="Group By">
<filter string="Provider" name="code" context="{'group_by': 'code'}"/>
+12 -7
View File
@@ -114,7 +114,7 @@
<!-- Portal breadcrumb -->
<t t-call="payment.portal_breadcrumb"/>
<!-- Payment methods page -->
<div class="row">
<div class="row justify-content-center">
<div class="col-lg-7">
<div t-if="not payment_methods_sudo and not tokens_sudo"
class="alert alert-warning"
@@ -160,6 +160,7 @@
<t t-call="portal.frontend_layout">
<t t-set="page_title" t-value="'Payment Confirmation'"/>
<t t-set="additional_title"><t t-esc="page_title"/></t>
<t t-set="show_pm" t-value="tx.payment_method_code != 'unknown'"/>
<div class="wrap">
<div class="container">
<!-- Portal breadcrumb -->
@@ -172,7 +173,9 @@
<t t-call="payment.transaction_status"/>
</div>
</div>
<div class="o_cc o_cc2 row row-cols-1 row-cols-md-4 mx-0 mb-3 py-2 rounded">
<div t-att-class="'o_cc o_cc2 row row-cols-1 mx-0 mb-3 py-2 rounded'
+ (' row-cols-md-4' if show_pm else ' row-cols-md-3')"
>
<t t-call="payment.summary_item">
<t t-set="name" t-value="'amount'"/>
<t t-set="label">Amount</t>
@@ -187,11 +190,13 @@
<t t-set="value" t-value="tx.reference"/>
<t t-set="include_separator" t-value="True"/>
</t>
<t t-call="payment.summary_item">
<t t-set="name" t-value="'method'"/>
<t t-set="label">Payment Method</t>
<t t-set="value" t-value="tx.payment_method_id.name"/>
<t t-set="include_separator" t-value="True"/>
<t t-if="tx.payment_method_code != 'unknown'">
<t t-call="payment.summary_item">
<t t-set="name" t-value="'method'"/>
<t t-set="label">Payment Method</t>
<t t-set="value" t-value="tx.payment_method_id.name"/>
<t t-set="include_separator" t-value="True"/>
</t>
</t>
<t t-call="payment.summary_item">
<t t-set="name" t-value="'provider'"/>
@@ -1,6 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="payment_custom.token_form" inherit_id="payment.token_form">
<xpath expr="//p[@name='o_payment_secured_by_desktop']" position="attributes">
<attribute name="t-att-class"/>
<attribute name="class">d-none</attribute>
</xpath>
<xpath expr="//p[@name='o_payment_secured_by_mobile']" position="attributes">
<attribute name="class" separator=" " add="d-none"/>
</xpath>
</template>
<template id="payment_custom.payment_method_form" inherit_id="payment.method_form">
<xpath expr="//p[@name='o_payment_secured_by']" position="attributes">
<attribute name="class" separator=" " add="d-none"/>
</xpath>
</template>
<template id="redirect_form">
<form t-att-action="api_url" method="post">
<input type="hidden" name="reference" t-att-value="reference"/>
@@ -1,6 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="payment_demo.token_form" inherit_id="payment.token_form">
<xpath expr="//p[@name='o_payment_secured_by_desktop']" position="attributes">
<attribute name="t-att-class"/>
<attribute name="class">d-none</attribute>
</xpath>
<xpath expr="//p[@name='o_payment_secured_by_mobile']" position="attributes">
<attribute name="class" separator=" " add="d-none"/>
</xpath>
</template>
<template id="payment_demo.payment_method_form" inherit_id="payment.method_form">
<xpath expr="//p[@name='o_payment_secured_by']" position="attributes">
<attribute name="class" separator=" " add="d-none"/>
</xpath>
</template>
<template id="inline_form">
<div t-attf-id="demo-container-{{provider_id}}">
<t t-call="payment_demo.payment_details"/>
+3 -3
View File
@@ -292,7 +292,7 @@
If you believe that it is an error, please contact the website administrator.
</div>
<div t-else="" id="payment_method" class="text-start">
<h3 class="mb24">Pay with</h3>
<h3>Pay with</h3>
<t t-call="payment.form">
<!-- Inject the order ID to allow Stripe to check if tokenization is required. -->
<t t-set="sale_order_id" t-value="sale_order.id"/>
@@ -348,7 +348,7 @@
Your order is not in a state to be rejected.
</div>
<t t-if="sale_order.transaction_ids">
<t t-if="sale_order.get_portal_last_transaction()">
<t t-call="payment.transaction_status">
<t t-set="tx" t-value="sale_order.get_portal_last_transaction()"/>
</t>
@@ -417,7 +417,7 @@
<div t-attf-class="#{'card-body' if report_type == 'html' else ''}">
<!-- Informations -->
<div id="informations">
<div t-if="sale_order.transaction_ids and not invoices and sale_order.state in ('sent', 'sale') and portal_confirmation == 'pay' and not success and not error" t-att-data-order-id="sale_order.id">
<div t-if="sale_order.get_portal_last_transaction() and not invoices and sale_order.state in ('sent', 'sale') and portal_confirmation == 'pay' and not success and not error" t-att-data-order-id="sale_order.id">
<t t-if="sale_order.transaction_ids">
<t t-call="payment.transaction_status">
<t t-set="tx" t-value="sale_order.get_portal_last_transaction()"/>
@@ -2,7 +2,7 @@
<odoo>
<record id="payment_provider_onsite" model="payment.provider">
<field name="name">Pay in store when picking the product</field>
<field name="name">Pay in Store</field>
<field name="code">custom</field>
<field name="state">enabled</field>
<field name="custom_mode">onsite</field>
@@ -3,7 +3,7 @@
<record id="onsite_delivery_product" model="product.product">
<field name="name">On site picking</field>
<field name="description">Pay in store when picking the product</field>
<field name="description">Pay in Store</field>
<field name="type">service</field>
<field name="list_price">0</field>
<field name="purchase_ok">false</field>
@@ -25,7 +25,7 @@ registry.category("web_tour.tours").add('onsite_payment_tour', {
wsTourUtils.goToCart({quantity: 2}),
wsTourUtils.goToCheckout(),
...wsTourUtils.fillAdressForm(),
wTourUtils.clickOnElement('"Pay in store when picking the product"', '.o_delivery_carrier_select:contains("Example shipping On Site")'),
wTourUtils.clickOnElement('"Pay in Store"', '.o_delivery_carrier_select:contains("Example shipping On Site")'),
wTourUtils.clickOnElement('Pay button', 'button[name="o_payment_submit_button"]:visible:not(:disabled)'),
{
content: "Check if the payment is successful",