[IMP] payment: UI/flow

This commit is contained in:
Victor Feyens
2019-08-12 08:44:25 +00:00
parent 718ca5269b
commit 611fde6c88
23 changed files with 77 additions and 86 deletions
+18 -14
View File
@@ -75,7 +75,19 @@ class PaymentAcquirer(models.Model):
default=lambda self: self.env.company.id, required=True)
view_template_id = fields.Many2one(
'ir.ui.view', 'Form Button Template',
default=_get_default_view_template_id)
default=_get_default_view_template_id,
help="This template renders the acquirer button with all necessary values.\n"
"It is rendered with qWeb with the following evaluation context:\n"
"tx_url: transaction URL to post the form\n"
"acquirer: payment.acquirer browse record\n"
"user: current user browse record\n"
"reference: the transaction reference number\n"
"currency: the transaction currency browse record\n"
"amount: the transaction amount, a float\n"
"partner: the buyer partner browse record, not necessarily set\n"
"partner_values: specific values about the buyer, for example coming from a shipping form\n"
"tx_values: transaction values\n"
"context: the current context dictionary")
registration_view_template_id = fields.Many2one(
'ir.ui.view', 'S2S Form Template', domain=[('type', '=', 'qweb')],
help="Template for method registration")
@@ -91,15 +103,7 @@ class PaymentAcquirer(models.Model):
help="Capture the amount from Odoo, when the delivery is completed.")
journal_id = fields.Many2one(
'account.journal', 'Payment Journal', domain=[('type', 'in', ['bank', 'cash'])],
help="""Payments will be registered into this journal. If you get paid straight on your bank account,
select your bank account. If you get paid in batch for several transactions, create a specific
payment journal for this payment acquirer to easily manage the bank reconciliation. You hold
the amount in a temporary transfer account of your books (created automatically when you create
the payment journal). Then when you get paid on your bank account by the payment acquirer, you
reconcile the bank statement line with this temporary transfer account. Use reconciliation
templates to do it in one-click.""")
specific_countries = fields.Boolean(string="Specific Countries",
help="If you leave it empty, the payment acquirer will be available for all the countries.")
help="""Journal where the successful transactions will be posted""")
check_validity = fields.Boolean(string="Verify Card Validity",
help="""Trigger a transaction of 1 currency unit and its refund to check the validity of new credit cards entered in the customer portal.
Without this check, the validity will be verified at the very first transaction.""")
@@ -116,15 +120,15 @@ class PaymentAcquirer(models.Model):
help='Message displayed after having done the payment process.')
pending_msg = fields.Html(
'Pending Message', translate=True,
default=lambda s: _('<i>Pending,</i> Your online payment has been successfully processed. But your order is not validated yet.'),
default=lambda s: _('Your payment has been successfully processed but is waiting for approval.'),
help='Message displayed, if order is in pending state after having done the payment process.')
done_msg = fields.Html(
'Done Message', translate=True,
default=lambda s: _('<i>Done,</i> Your online payment has been successfully processed. Thank you for your order.'),
default=lambda s: _('Your payment has been successfully processed. Thank you!'),
help='Message displayed, if order is done successfully after having done the payment process.')
cancel_msg = fields.Html(
'Cancel Message', translate=True,
default=lambda s: _('<i>Cancel,</i> Your payment has been cancelled.'),
default=lambda s: _('Your payment has been cancelled.'),
help='Message displayed, if order is cancel during the payment process.')
error_msg = fields.Html(
'Error Message', translate=True,
@@ -693,7 +697,7 @@ class PaymentTransaction(models.Model):
inv.message_post(body=post_message)
def _set_transaction_pending(self):
'''Move the transaction to the pending state(e.g. Wire Transfer).'''
'''Move the transaction to the pending state(e.g. Manual Payment).'''
if any(trans.state != 'draft' for trans in self):
raise ValidationError(_('Only draft transaction can be processed.'))
@@ -110,7 +110,7 @@ odoo.define('payment.processing', function (require) {
this.$el.find('.o_payment_processing_content').html(html);
},
displayLoading: function () {
var msg = _t("We are processing your payments, please wait ...");
var msg = _t("We are processing your payment, please wait ...");
$.blockUI({
'message': '<h2 class="text-white"><img src="/web/static/src/img/spin.png" class="fa-pulse"/>' +
' <br />' + msg +
@@ -42,6 +42,7 @@
.o_form_view {
.o_payment_acquirer_desc {
margin-top: 10px;
ul {
list-style-type: none;
padding: 0;
@@ -86,6 +86,7 @@
<t t-set="i" t-value="i+1"/>
</t>
</ul>
<div><t t-raw="acq.pre_msg"/></div>
</div>
<t t-if="acq.payment_flow == 'form'">
<div t-attf-id="o_payment_form_acq_{{acq.id}}"
+34 -42
View File
@@ -73,15 +73,44 @@
</div>
<group name="acquirer_base" col="2" attrs="{'invisible': ['&amp;', ('module_id', '!=', False), ('module_state', '!=', 'installed')]}">
<group name="acquirer_left">
<field name="provider" groups="base.group_no_one"/>
<field name="provider" groups="base.group_no_one" attrs="{'invisible': [('module_id', '!=', False), ('module_state', '!=', 'installed')]}"/>
<field name="company_id" groups="base.group_multi_company" options='{"no_open":True}'/>
</group>
</group>
<notebook attrs="{'invisible': ['&amp;', ('module_id', '!=', False), ('module_state', '!=', 'installed')]}">
<page string="Credentials" name="acquirer_credentials">
<page string="Credentials" name="acquirer_credentials" attrs="{'invisible': ['provider', '=', 'manual']}">
<group name="acquirer">
</group>
</page>
<page string="Configuration">
<group name="acquirer_config">
<group string="Payment Form" name="payment_form">
<field name="payment_icon_ids" widget="many2many_tags"/>
<field name="save_token" widget="radio" attrs="{'invisible': ['|', ('token_implemented', '=', False), ('payment_flow', '=', 's2s')]}"/>
<field name="capture_manually" attrs="{'invisible': [('authorize_implemented', '=', False)]}"/>
<field name="payment_flow" widget="radio" attrs="{'invisible': [('token_implemented', '=', False)]}"/>
<field name="view_template_id" groups="base.group_no_one"/>
<field name="registration_view_template_id" groups="base.group_no_one" attrs="{'invisible': [('payment_flow', '!=', 's2s')]}"/>
<field name="check_validity" attrs="{'invisible': [('payment_flow', '!=', 's2s')]}" groups="base.group_no_one"/>
<field name="qr_code" attrs="{'invisible': [('provider', '!=', 'transfer')]}"/>
</group>
<group string="Availability" name="availability">
<field name="country_ids" widget="many2many_tags" placeholder="Select countries. Leave empty to use everywhere."/>
</group>
<group string="Payment Followup" name="payment_followup">
<field name="journal_id" context="{'default_type': 'bank'}"/>
</group>
</group>
</page>
<page string="Fees" attrs="{'invisible': [('fees_implemented', '=', False)]}">
<group name="payment_fees">
<field name="fees_active"/>
<field name="fees_dom_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="fees_dom_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="fees_int_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="fees_int_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
</group>
</page>
<page string="Messages">
<group>
<field name="pre_msg" invisible="1"/>
@@ -92,45 +121,6 @@
<field name="error_msg"/>
</group>
</page>
<page string="Configuration">
<group name="acquirer_config">
<field name="journal_id" context="{'default_type': 'bank'}"/>
<field name="capture_manually" attrs="{'invisible': [('authorize_implemented', '=', False)]}"/>
<field name="save_token" widget="radio" attrs="{'invisible': ['|', ('token_implemented', '=', False), ('payment_flow', '=', 's2s')]}"/>
<field name="fees_active" attrs="{'invisible': [('fees_implemented', '=', False)]}"/>
<field name="fees_dom_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="fees_dom_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="fees_int_fixed" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="fees_int_var" attrs="{'invisible': [('fees_active', '=', False)]}"/>
<field name="specific_countries"/>
<field name="qr_code" attrs="{'invisible': [('provider', '!=', 'transfer')]}"/>
<field name="country_ids" widget="many2many_tags" attrs="{'invisible': [('specific_countries', '=', False)]}"/>
<label for="view_template_id" groups="base.group_no_one"/>
<div groups="base.group_no_one">
<field name="view_template_id" nolabel="1"/>
<div>
This template renders the acquirer button with all necessary values.
It is rendered with qWeb with the following evaluation context:
<ul>
<li>tx_url: transaction URL to post the form</li>
<li>acquirer: payment.acquirer browse record</li>
<li>user: current user browse record</li>
<li>reference: the transaction reference number</li>
<li>currency: the transaction currency browse record</li>
<li>amount: the transaction amount, a float</li>
<li>partner: the buyer partner browse record, not necessarily set</li>
<li>partner_values: specific values about the buyer, for example coming from a shipping form</li>
<li>tx_values: transaction values</li>
<li>context: the current context dictionary</li>
</ul>
</div>
</div>
<field name="registration_view_template_id" groups="base.group_no_one" attrs="{'invisible': [('payment_flow', '!=', 's2s')]}"/>
<field name="payment_icon_ids" widget="many2many_tags"/>
<field name="payment_flow" widget="radio" attrs="{'invisible': [('token_implemented', '=', False)]}"/>
<field name="check_validity" attrs="{'invisible': [('payment_flow', '!=', 's2s')]}" groups="base.group_no_one"/>
</group>
</page>
</notebook>
</sheet>
</form>
@@ -188,6 +178,7 @@
<field name="provider"/>
<field name="website_published"/>
<field name="environment"/>
<field name="country_ids" widget="many2many_tags" optional="hide"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
@@ -198,8 +189,9 @@
<field name="model">payment.acquirer</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="name" string="Acquirer" filter_domain="['|', ('name','ilike',self), ('description','ilike',self)]"/>
<field name="provider"/>
<filter name="acquirer_installed" string="Installed" domain="[('provider', '!=', 'manual')]"/>
<group expand="0" string="Group By">
<filter string="Provider" name="provider" domain="[]" context="{'group_by': 'provider'}"/>
<filter string="Environment" name="environment" domain="[]" context="{'group_by': 'environment'}"/>
+1 -1
View File
@@ -7,7 +7,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'adyen')]}">
<field name="adyen_merchant_account"/>
<field name="adyen_skin_code"/>
+1 -1
View File
@@ -5,7 +5,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'alipay')]}">
<field name="alipay_payment_method" widget="radio"/>
<field name="alipay_seller_email" attrs="{'invisible': [('alipay_payment_method', '=', 'standard_checkout')], 'required': [('provider', '=', 'alipay'), ('alipay_payment_method', '=', 'express_checkout')]}"/>
@@ -5,7 +5,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'authorize')]}">
<field name="authorize_login"/>
<field name="authorize_transaction_key" password="True"/>
@@ -7,7 +7,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'buckaroo')]}">
<field name="brq_websitekey"/>
<field name="brq_secretkey"/>
@@ -7,7 +7,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'ogone')]}">
<field name="ogone_pspid"/>
<field name="ogone_userid"/>
@@ -7,7 +7,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'paypal')]}">
<field name="paypal_email_account"/>
<field name="paypal_seller_account"/>
@@ -5,7 +5,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'payulatam')]}">
<field name="payulatam_account_id" options="{'no_create': True}"/>
<field name="payulatam_merchant_id"/>
@@ -5,7 +5,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'payumoney')]}">
<field name="payumoney_merchant_key"/>
<field name="payumoney_merchant_salt" password="True"/>
+1 -1
View File
@@ -7,7 +7,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'sips')]}">
<field name="sips_merchant_id"/>
<field name="sips_secret"/>
@@ -5,7 +5,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'>
<xpath expr='//group[@name="acquirer"]' position='inside'>
<group attrs="{'invisible': [('provider', '!=', 'stripe')]}">
<field name="stripe_secret_key" password="True"/>
<field name="stripe_publishable_key" password="True"/>
+1 -1
View File
@@ -9,7 +9,7 @@ from odoo.http import request
_logger = logging.getLogger(__name__)
class OgoneController(http.Controller):
class TransferController(http.Controller):
_accept_url = '/payment/transfer/feedback'
@http.route([
@@ -11,7 +11,7 @@
<field name="environment">test</field>
<field name="website_published" eval="True"/>
<field name="pre_msg"><![CDATA[
<p>Transfer information will be provided after choosing the payment aquirer.</p>]]>
<p>Transfer information will be provided when clicking 'Pay Now'.</p>]]>
</field>
</record>
</data>
+1 -1
View File
@@ -13,7 +13,7 @@ _logger = logging.getLogger(__name__)
class TransferPaymentAcquirer(models.Model):
_inherit = 'payment.acquirer'
provider = fields.Selection(selection_add=[('transfer', 'Wire Transfer')], default='transfer')
provider = fields.Selection(selection_add=[('transfer', 'Manual Payment')], default='transfer')
@api.model
def _create_missing_journal_for_acquirers(self, company=None):
+1 -1
View File
@@ -176,7 +176,7 @@ class CustomerPortal(CustomerPortal):
if order_sudo.has_to_be_paid():
domain = expression.AND([
['&', ('website_published', '=', True), ('company_id', '=', order_sudo.company_id.id)],
['|', ('specific_countries', '=', False), ('country_ids', 'in', [order_sudo.partner_id.country_id.id])]
['|', ('country_ids', '=', False), ('country_ids', 'in', [order_sudo.partner_id.country_id.id])]
])
acquirers = request.env['payment.acquirer'].sudo().search(domain)
+1 -1
View File
@@ -21,7 +21,7 @@ class ResCompany(models.Model):
('paypal', 'PayPal'),
('stripe', 'Stripe'),
('other', 'Pay with another payment acquirer'),
('manual', 'Wire Transfer'),
('manual', 'Manual Payment'),
], string="Sale onboarding selected payment method")
@api.model
+1 -1
View File
@@ -6,7 +6,7 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='acquirer_config']" position="inside">
<xpath expr="//field[@name='qr_code']" position="after">
<field name="so_reference_type" attrs="{'invisible': [('provider', '!=', 'transfer')]}"/>
</xpath>
</field>
+1 -1
View File
@@ -841,7 +841,7 @@ class WebsiteSale(http.Controller):
domain = expression.AND([
['&', ('website_published', '=', True), ('company_id', '=', order.company_id.id)],
['|', ('website_id', '=', False), ('website_id', '=', request.website.id)],
['|', ('specific_countries', '=', False), ('country_ids', 'in', [order.partner_id.country_id.id])]
['|', ('country_ids', '=', False), ('country_ids', 'in', [order.partner_id.country_id.id])]
])
acquirers = request.env['payment.acquirer'].search(domain)
+5 -12
View File
@@ -1599,8 +1599,8 @@
<t t-set="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
<div t-attf-class="card #{
(payment_tx_id.state == 'pending' and 'bg-info') or
(payment_tx_id.state == 'done' and 'bg-success') or
(payment_tx_id.state == 'authorized' and 'bg-success') or
(payment_tx_id.state == 'done' and 'alert-success') or
(payment_tx_id.state == 'authorized' and 'alert-success') or
'bg-danger'}">
<div class="card-header">
<a role="button" groups="base.group_system" class="btn btn-sm btn-link text-white float-right" target="_blank" aria-label="Edit" title="Edit"
@@ -1611,24 +1611,17 @@
<t t-raw="payment_tx_id.acquirer_id.pending_msg"/>
</t>
<t t-if="payment_tx_id.state == 'done'">
<t t-raw="payment_tx_id.acquirer_id.done_msg"/>
<span t-if='payment_tx_id.acquirer_id.done_msg' t-raw="payment_tx_id.acquirer_id.done_msg"/>
</t>
<t t-if="payment_tx_id.state == 'cancel'">
<t t-raw="payment_tx_id.acquirer_id.cancel_msg"/>
</t>
<t t-if="payment_tx_id.state == 'error'">
<span t-if="payment_tx_id.acquirer_id.error_msg" t-raw="payment_tx_id.acquirer_id.error_msg"/>
<span t-else="">An error occured while processing your payment. Please try again or contact your administrator.</span>
</t>
<t t-if="payment_tx_id.state == 'authorized'">
<span>Your payment has been authorized.</span>
</t>
</div>
<div t-if="payment_tx_id.acquirer_id.post_msg" class="card-body">
<t t-raw="payment_tx_id.acquirer_id.post_msg" />
<div t-if="payment_tx_id.acquirer_id.provider == 'transfer' and order.reference">
<b>Communication: </b><span t-esc='order.reference'/>
</div>
<div t-if="payment_tx_id.acquirer_id.provider == 'transfer' and order.reference" class="card-body">
<b>Communication: </b><span t-esc='order.reference'/>
</div>
<div t-if="(payment_tx_id.acquirer_id.qr_code == True) and (payment_tx_id.acquirer_id.provider == 'transfer') and (payment_tx_id.partner_id.country_id.code in ['BE', 'AT', 'DE', 'FI', 'NL']) and (payment_tx_id.currency_id.name == 'EUR')">
<div class="card-body" t-if="payment_tx_id.acquirer_id.journal_id.bank_account_id.qr_code_valid">