diff --git a/addons/account/views/account_portal_templates.xml b/addons/account/views/account_portal_templates.xml index 51ac3563c2e..3bf6aa8dd7f 100644 --- a/addons/account/views/account_portal_templates.xml +++ b/addons/account/views/account_portal_templates.xml @@ -253,8 +253,8 @@ + + + + + diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 319807780c1..9edd843fba7 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -151,9 +151,9 @@ class PaymentAcquirer(models.Model): "Use this field anywhere a small image is required.") payment_icon_ids = fields.Many2many('payment.icon', string='Supported Payment Icons') - payment_flow = fields.Selection(selection=[('s2s','The customer encode his payment details on the website.'), - ('form', 'The customer is redirected to the website of the acquirer.')], - default='form', required=True, string='Payment flow', + payment_flow = fields.Selection(selection=[('form', 'Redirection to the acquirer website'), + ('s2s','Payment from Odoo')], + default='form', required=True, string='Payment Flow', help="""Note: Subscriptions does not take this field in account, it uses server to server by default.""") def _search_is_tokenized(self, operator, value): diff --git a/addons/payment/static/src/css/portal_payment.css b/addons/payment/static/src/css/portal_payment.css index e9db1fd8d28..eeb40dbd3a3 100644 --- a/addons/payment/static/src/css/portal_payment.css +++ b/addons/payment/static/src/css/portal_payment.css @@ -14,6 +14,15 @@ div.card_placeholder { transition: 0.4s cubic-bezier(0.455, 0.03, 0.515, 0.955); pointer-events: none; } +/* if s2s form not in bootstrap_formatting */ +div.o_card_brand_detail { + position: relative; +} + +div.o_card_brand_detail div.card_placeholder { + right: 5px; +} + div.amex { background-image: url("/website_payment/static/src/img/amex.png"); background-repeat: no-repeat; } diff --git a/addons/payment/static/src/js/payment_form.js b/addons/payment/static/src/js/payment_form.js index dd643840e95..494281759a9 100644 --- a/addons/payment/static/src/js/payment_form.js +++ b/addons/payment/static/src/js/payment_form.js @@ -57,29 +57,34 @@ odoo.define('payment.payment_form', function (require) { console.warn('payment_form: unset partner_id when adding new token; things could go wrong'); } var form_data = this.getFormData(inputs_form); - var empty_inputs = false; + var wrong_input = false; inputs_form.toArray().forEach(function (element) { - if (element.dataset.isRequired) { - if (element.value.length === 0) { + //skip the check of non visible inputs + if ($(element).attr('type') == 'hidden') { + return true; + } + $(element).closest('div.form-group').removeClass('has-error'); + $(element).siblings( ".o_invalid_field" ).remove(); + //force check of forms validity (useful for Firefox that refill forms automatically on f5) + $(element).trigger("focusout"); + if (element.dataset.isRequired && element.value.length === 0) { $(element).closest('div.form-group').addClass('has-error'); - empty_inputs = true; - } - else { - $(element).closest('div.form-group').removeClass('has-error'); - } + $(element).closest('div.form-group').append('
' + _.str.escapeHTML("The value is invalid.") + '
'); + wrong_input = true; + } + else if ($(element).closest('div.form-group').hasClass('has-error')) { + wrong_input = true; + $(element).closest('div.form-group').append('
' + _.str.escapeHTML("The value is invalid.") + '
'); } }); - if (empty_inputs) { - this.displayError( - _t('Missing values'), - _t('

Please fill all the inputs required.

') - ); + if (wrong_input) { return; } $(button).attr('disabled', true); + $(button).children('.fa-plus-circle').removeClass('fa-plus-circle') $(button).prepend(' '); var verify_validity = this.$el.find('input[name="verify_validity"]'); @@ -104,24 +109,30 @@ odoo.define('payment.payment_form', function (require) { } // if the server has returned false, we display an error else { - self.displayError( - _t('Server Error'), - _t("

We are not able to add your payment method at the moment.

")); + if (data.error) { + self.displayError( + '', + data.error); + } else { // if the server doesn't provide an error message + self.displayError( + _t('Server Error'), + _t('e.g. Your credit card details are wrong. Please verify.')); + } } // here we remove the 'processing' icon from the 'add a new payment' button $(button).attr('disabled', false); + $(button).children('.fa').addClass('fa-plus-circle') $(button).find('span.o_loader').remove(); }).fail(function (message, data) { // if the rpc fails, pretty obvious $(button).attr('disabled', false); + $(button).children('.fa').addClass('fa-plus-circle') $(button).find('span.o_loader').remove(); self.displayError( _t('Server Error'), - _t("

We are not able to add your payment method at the moment.

") + - "

" + - (core.debug ? (data.data.message.replace(/\n/g, "
")): '') + - "

" + _t("We are not able to add your payment method at the moment.") + + data.data.message ); }); } @@ -131,7 +142,7 @@ odoo.define('payment.payment_form', function (require) { // if there's a prepare tx url set if ($tx_url.length === 1) { // if the user wants to save his credit card info - var form_save_token = $('input[name="o_payment_form_save_token"]').prop('checked'); + var form_save_token = acquirer_form.find('input[name="o_payment_form_save_token"]').prop('checked'); // then we call the route to prepare the transaction ajax.jsonRpc($tx_url[0].value, 'call', { 'acquirer_id': parseInt(acquirer_id), @@ -159,16 +170,14 @@ odoo.define('payment.payment_form', function (require) { else { self.displayError( _t('Server Error'), - _t("

We are not able to redirect you to the payment form.

") + _t("We are not able to redirect you to the payment form.") ); } }).fail(function (message, data) { self.displayError( _t('Server Error'), - _t("

We are not able to redirect you to the payment form.

") + - "

" + - (core.debug ? (data.data.message.replace(/\n/g, "
")): '') + - "

" + _t("We are not able to redirect you to the payment form.<") + + data.data.message ); }); } @@ -176,7 +185,7 @@ odoo.define('payment.payment_form', function (require) { // we append the form to the body and send it. this.displayError( _t("Cannot set-up the payment"), - _t("

We're unable to process your payment.

") + _t("We're unable to process your payment.") ); } } @@ -187,7 +196,7 @@ odoo.define('payment.payment_form', function (require) { else { this.displayError( _t('No payment method selected'), - _t('

Please select a payment method.

') + _t('Please select a payment method.') ); } }, @@ -209,29 +218,36 @@ odoo.define('payment.payment_form', function (require) { var inputs_form = $('input', acquirer_form); var form_data = this.getFormData(inputs_form); var ds = $('input[name="data_set"]', acquirer_form)[0]; - var empty_inputs = false; - + var wrong_input = false; + inputs_form.toArray().forEach(function (element) { - if (element.dataset.isRequired) { - if (element.value.length === 0) { + //skip the check of non visible inputs + if ($(element).attr('type') == 'hidden') { + return true; + } + $(element).closest('div.form-group').removeClass('has-error'); + $(element).siblings( ".o_invalid_field" ).remove(); + //force check of forms validity (useful for Firefox that refill forms automatically on f5) + $(element).trigger("focusout"); + if (element.dataset.isRequired && element.value.length === 0) { $(element).closest('div.form-group').addClass('has-error'); - empty_inputs = true; - } - else { - $(element).closest('div.form-group').removeClass('has-error'); - } + var message = '
' + _.str.escapeHTML("The value is invalid.") + '
'; + $(element).closest('div.form-group').append(message); + wrong_input = true; + } + else if ($(element).closest('div.form-group').hasClass('has-error')) { + wrong_input = true; + var message = '
' + _.str.escapeHTML("The value is invalid.") + '
'; + $(element).closest('div.form-group').append(message); } }); - if (empty_inputs) { - this.displayError( - _t('Missing values'), - _t('

Please fill all the inputs required.

') - ); + if (wrong_input) { return; } // We add a 'processing' icon into the 'add a new payment' button $(button).attr('disabled', true); + $(button).children('.fa-plus-circle').removeClass('fa-plus-circle') $(button).prepend(' '); // we force the check when adding a card trough here @@ -259,32 +275,37 @@ odoo.define('payment.payment_form', function (require) { } // if the server has returned false, we display an error else { - self.displayError( - _t('Server Error'), - _t("

We are not able to add your payment method at the moment.

") - ); + if (data.error) { + self.displayError( + '', + data.error); + } else { // if the server doesn't provide an error message + self.displayError( + _t('Server Error'), + _t('e.g. Your credit card details are wrong. Please verify.')); + } } // here we remove the 'processing' icon from the 'add a new payment' button $(button).attr('disabled', false); + $(button).children('.fa').addClass('fa-plus-circle') $(button).find('span.o_loader').remove(); }).fail(function (message, data) { // if the rpc fails, pretty obvious $(button).attr('disabled', false); + $(button).children('.fa').addClass('fa-plus-circle') $(button).find('span.o_loader').remove(); self.displayError( _t('Server error'), - _t("

We are not able to add your payment method at the moment.

") + - "

" + - (core.debug ? (data.data.message.replace(/\n/g, "
")): '') + - "

" + _t("We are not able to add your payment method at the moment.

") + + data.data.message ); }); } else { this.displayError( _t('No payment method selected'), - _t('

Please select the option to add a new payment method.

') + _t('Please select the option to add a new payment method.') ); } }, @@ -308,7 +329,7 @@ odoo.define('payment.payment_form', function (require) { }, function () { self.displayError( _t('Server Error'), - _t("

We are not able to delete your payment method at the moment.

") + _t("We are not able to delete your payment method at the moment.") ); }); }; @@ -322,7 +343,7 @@ odoo.define('payment.payment_form', function (require) { // if there's records linked to this payment method var content = ''; result[pm_id].forEach(function (sub) { - content += '

' + _.str.escapeHTML(sub.name) + '

'; + content += '

' + sub.name + '

'; }); content = $('

').html(_t('

This card is currently linked to the following records:

') + content); @@ -343,7 +364,7 @@ odoo.define('payment.payment_form', function (require) { }, function (type, err) { self.displayError( _t('Server Error'), - _t("

We are not able to delete your payment method at the moment.

") + (core.debug ? err.data.message : '') + _t("We are not able to delete your payment method at the moment.") + err.data.message ); }); }, @@ -391,12 +412,27 @@ odoo.define('payment.payment_form', function (require) { return $(element).data('acquirer-id'); }, displayError: function (title, message) { - return new Dialog(null, { - title: _t('Error: ') + title, - size: 'medium', - $content: message || "", - buttons: [ - {text: _t('Ok'), close: true}]}).open(); + var $checkedRadio = this.$('input[type="radio"]:checked'), + acquirerID = this.getAcquirerIdFromRadio($checkedRadio[0]), + $acquirerForm = this.$('#o_payment_add_token_acq_' + acquirerID); + + if ($checkedRadio.length === 0) { + return new Dialog(null, { + title: _t('Error: ') + _.str.escapeHTML(title), + size: 'medium', + $content: "

" + (_.str.escapeHTML(message) || "") + "

" , + buttons: [ + {text: _t('Ok'), close: true}]}).open(); + } else { + // removed if exist error message + this.$('#payment_error').remove(); + var messageResult = '
'; + if (title != '') { + messageResult = messageResult + '' + _.str.escapeHTML(title) + ':
'; + } + messageResult = messageResult + _.str.escapeHTML(message) + '
'; + $acquirerForm.append(messageResult); + } }, getFormData: function ($form) { var unindexed_array = $form.serializeArray(); diff --git a/addons/payment/static/src/js/payment_portal.js b/addons/payment/static/src/js/payment_portal.js index d7397f1b04d..efab5f0a8e0 100644 --- a/addons/payment/static/src/js/payment_portal.js +++ b/addons/payment/static/src/js/payment_portal.js @@ -17,6 +17,7 @@ $(document).ready(function () { if (valid_value) { $(this).parent('.form-group').addClass('has-success'); $(this).parent('.form-group').removeClass('has-error'); + $(this).siblings('.o_invalid_field').remove(); } else { $(this).parent('.form-group').addClass('has-error'); @@ -31,6 +32,7 @@ $(document).ready(function () { if (valid_value) { $(this).parent('.form-group').addClass('has-success'); $(this).parent('.form-group').removeClass('has-error'); + $(this).siblings('.o_invalid_field').remove(); } else { $(this).parent('.form-group').addClass('has-error'); @@ -46,6 +48,7 @@ $(document).ready(function () { if (valid_value) { $(this).parent('.form-group').addClass('has-success'); $(this).parent('.form-group').removeClass('has-error'); + $(this).siblings('.o_invalid_field').remove(); } else { $(this).parent('.form-group').addClass('has-error'); diff --git a/addons/payment/static/src/less/payment_form.less b/addons/payment/static/src/less/payment_form.less index b5aeb2e928e..5e3bb794160 100644 --- a/addons/payment/static/src/less/payment_form.less +++ b/addons/payment/static/src/less/payment_form.less @@ -1,6 +1,6 @@ .o_payment_form { label > input[type="radio"], input[type="checkbox"]{ - vertical-align: bottom; + vertical-align: top; margin-right: 5px; } @@ -14,6 +14,7 @@ label { font-weight: normal; + margin-top: 5px; } .panel-body:first-child { @@ -35,16 +36,18 @@ } .payment_icon_list { + position: relative; li { padding-left: 5px !important; padding-right: 0px !important; } .more_option { + .o-position-absolute(@right: 0px, @bottom: -10px); font-size:10px; } margin-top: 0px !important; margin-bottom: -5px !important; } -} \ No newline at end of file +} diff --git a/addons/payment/views/payment_portal_templates.xml b/addons/payment/views/payment_portal_templates.xml index f599329e5e1..d2fa3c1c4cd 100644 --- a/addons/payment/views/payment_portal_templates.xml +++ b/addons/payment/views/payment_portal_templates.xml @@ -34,7 +34,7 @@ + + diff --git a/addons/payment/views/payment_templates.xml b/addons/payment/views/payment_templates.xml index d85ae8a135e..1b6246290fa 100644 --- a/addons/payment/views/payment_templates.xml +++ b/addons/payment/views/payment_templates.xml @@ -41,6 +41,7 @@
+