diff --git a/addons/account_payment/views/account_portal_templates.xml b/addons/account_payment/views/account_portal_templates.xml index 9bb739202bd..a48a322ba75 100644 --- a/addons/account_payment/views/account_portal_templates.xml +++ b/addons/account_payment/views/account_portal_templates.xml @@ -132,8 +132,7 @@ - -
+
Communication:
diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py index 4db7e0153ea..773eb111201 100644 --- a/addons/payment/controllers/portal.py +++ b/addons/payment/controllers/portal.py @@ -79,7 +79,7 @@ class PaymentProcessing(http.Controller): } # populate the returned dictionnary with the transactions data for tx in payment_transaction_ids: - message_to_display = tx.acquirer_id[tx.state + '_msg'] if tx.state in ['done', 'pending', 'cancel', 'error'] else None + message_to_display = tx.acquirer_id[tx.state + '_msg'] if tx.state in ['done', 'pending', 'cancel'] else None result['transactions'].append({ 'reference': tx.reference, 'state': tx.state, @@ -111,7 +111,7 @@ class WebsitePayment(http.Controller): @http.route(['/my/payment_method'], type='http', auth="user", website=True) def payment_method(self, **kwargs): acquirers = list(request.env['payment.acquirer'].search([ - ('website_published', '=', True), ('registration_view_template_id', '!=', False), + ('state', 'in', ['enabled', 'test']), ('registration_view_template_id', '!=', False), ('payment_flow', '=', 's2s'), ('company_id', '=', request.env.company.id) ])) partner = request.env.user.partner_id @@ -178,7 +178,7 @@ class WebsitePayment(http.Controller): if acquirer_id: acquirers = env['payment.acquirer'].browse(int(acquirer_id)) if not acquirers: - acquirers = env['payment.acquirer'].search([('website_published', '=', True), ('company_id', '=', user.company_id.id)]) + acquirers = env['payment.acquirer'].search([('state', 'in', ['enabled', 'test']), ('company_id', '=', user.company_id.id)]) # Check partner partner_id = user.partner_id.id if not user._is_public() else False @@ -280,9 +280,6 @@ class WebsitePayment(http.Controller): elif tx.state == 'pending': status = 'warning' message = tx.acquirer_id.pending_msg - else: - status = 'danger' - message = tx.acquirer_id.error_msg PaymentProcessing.remove_payment_transaction(tx) return request.render('payment.confirm', {'tx': tx, 'status': status, 'message': message}) else: diff --git a/addons/payment/data/payment_acquirer_data.xml b/addons/payment/data/payment_acquirer_data.xml index 8ff2324d2a7..233baf64501 100644 --- a/addons/payment/data/payment_acquirer_data.xml +++ b/addons/payment/data/payment_acquirer_data.xml @@ -14,6 +14,7 @@ Buckaroo + Credit Card (powered by Buckaroo) @@ -34,25 +35,28 @@ ref("payment.payment_icon_cc_american_express")])]'/> - + Ingenico + Credit Card (powered by Ingenico) 2 - - + +

- Ingenico Payment Services (formerly Ogone) supports credit cards, debit cards and bank transfers. + Ingenico Payment Services (formerly Ogone) supports a wide range of payment methods: credit cards, debit cards, bank transfers, Bancontact, iDeal, Giropay.

  • Online Payment
  • Payment Status Tracking
  • Subscriptions
  • Save Cards
  • +
  • Embedded Credit Card Form
- @@ -60,6 +64,7 @@ Adyen + Credit Card (powered by Adyen) @@ -85,6 +90,7 @@ Authorize.net + Credit Card (powered by Authorize) 3 @@ -99,6 +105,7 @@
  • Subscriptions
  • Save Cards
  • Manual Capture
  • +
  • Embedded Credit Card Form
  • @@ -126,6 +133,7 @@ Sips + Credit Card (powered by Sips) @@ -178,6 +186,7 @@ Stripe + Credit Card (powered by Stripe) @@ -205,23 +214,9 @@ ref("payment.payment_icon_cc_visa")])]'/> - - Custom - - - - -

    - A generic payment acquirer to send payment instructions to any payment acquirer. -

    -
      -
    • Online Payment
    • -
    -
    -
    - PayUmoney + Credit Card (powered by PayUmoney) @@ -245,6 +240,7 @@ PayU Latam + Credit Card (powered by PayU Latam) @@ -267,6 +263,7 @@ Alipay + Credit Card (powered by Alipay) diff --git a/addons/payment/data/payment_icon_data.xml b/addons/payment/data/payment_icon_data.xml index a4613d3e3d9..44780e8b493 100644 --- a/addons/payment/data/payment_icon_data.xml +++ b/addons/payment/data/payment_icon_data.xml @@ -60,8 +60,13 @@ + + iDEAL + + + WebMoney - \ No newline at end of file + diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 49da8d89e6a..f33db57faf8 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -37,7 +37,7 @@ class PaymentAcquirer(models.Model): to have required fields that depend on a specific acquirer. Each acquirer has a link to an ir.ui.view record that is a template of - a button used to display the payment form. See examples in ``payment_ogone`` + a button used to display the payment form. See examples in ``payment_ingenico`` and ``payment_paypal`` modules. Methods that should be added in an acquirer-specific implementation: @@ -59,47 +59,53 @@ class PaymentAcquirer(models.Model): """ _name = 'payment.acquirer' _description = 'Payment Acquirer' - _order = 'website_published desc, sequence, name' + _order = 'state desc, sequence, name' def _get_default_view_template_id(self): return self.env.ref('payment.default_acquirer_button', raise_if_not_found=False) name = fields.Char('Name', required=True, translate=True) + color = fields.Integer('Color', compute='_compute_color', store=True) + display_as = fields.Char('Displayed as', translate=True, help="How the acquirer is displayed to the customers.") description = fields.Html('Description') sequence = fields.Integer('Sequence', default=10, help="Determine the display order") provider = fields.Selection( - selection=[('manual', 'Manual Configuration')], string='Provider', + selection=[('manual', 'Custom Payment Form')], string='Provider', default='manual', required=True) company_id = fields.Many2one( 'res.company', 'Company', 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") - environment = fields.Selection([ - ('test', 'Test'), - ('prod', 'Production')], string='Environment', - default='test', required=True) - website_published = fields.Boolean( - 'Visible in Portal / Website', copy=False, - help="Make this payment acquirer available (Customer invoices, etc.)") - # Formerly associated to `authorize` option from auto_confirm + state = fields.Selection([ + ('disabled', 'Disabled'), + ('enabled', 'Enabled'), + ('test', 'Test Mode')], required=True, default='disabled', + help="""In test mode, a fake payment is processed through a test + payment interface. This mode is advised when setting up the + acquirer. Watch out, test and production modes require + different credentials.""") capture_manually = fields.Boolean(string="Capture Amount Manually", 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.""") @@ -111,25 +117,22 @@ class PaymentAcquirer(models.Model): pre_msg = fields.Html( 'Help Message', translate=True, help='Message displayed to explain and help the payment process.') - post_msg = fields.Html( - 'Thanks Message', translate=True, - help='Message displayed after having done the payment process.') + auth_msg = fields.Html( + 'Authorize Message', translate=True, + default=lambda s: _('Your payment has been authorized.'), + help='Message displayed if payment is authorized.') pending_msg = fields.Html( 'Pending Message', translate=True, - default=lambda s: _('Pending, 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: _('Done, 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: _('Cancel, 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, - default=lambda s: _('Error, Please be aware that an error occurred during the transaction. The order has been confirmed but will not be paid. Do not hesitate to contact us if you have any questions on the status of your order.'), - help='Message displayed, if error is occur during the payment process.') save_token = fields.Selection([ ('none', 'Never'), ('ask', 'Let the customer decide'), @@ -150,7 +153,7 @@ class PaymentAcquirer(models.Model): # TDE FIXME: remove that brol module_id = fields.Many2one('ir.module.module', string='Corresponding Module') - module_state = fields.Selection(selection=ir_module.STATES, string='Installation State', related='module_id.state', readonly=False) + module_state = fields.Selection(selection=ir_module.STATES, string='Installation State', related='module_id.state', store=True) image_128 = fields.Image("Image", max_width=128, max_height=128) @@ -170,12 +173,18 @@ class PaymentAcquirer(models.Model): elif electronic in self.inbound_payment_method_ids: self.inbound_payment_method_ids = [(2, electronic.id)] + @api.onchange('state') + def onchange_state(self): + """Disable dashboard display for test acquirer journal.""" + self.journal_id.update({'show_on_dashboard': self.state == 'enabled'}) + def _search_is_tokenized(self, operator, value): tokenized = self._get_feature_support()['tokenize'] if (operator, value) in [('=', True), ('!=', False)]: return [('provider', 'in', tokenized)] return [('provider', 'not in', tokenized)] + @api.depends('provider') def _compute_feature_support(self): feature_support = self._get_feature_support() for acquirer in self: @@ -183,15 +192,28 @@ class PaymentAcquirer(models.Model): acquirer.authorize_implemented = acquirer.provider in feature_support['authorize'] acquirer.token_implemented = acquirer.provider in feature_support['tokenize'] + @api.depends('state', 'module_state') + def _compute_color(self): + for acquirer in self: + if acquirer.module_id and not acquirer.module_state == 'installed': + acquirer.color = 4 # blue + elif acquirer.state == 'disabled': + acquirer.color = 3 # yellow + elif acquirer.state == 'test': + acquirer.color = 2 # orange + elif acquirer.state == 'enabled': + acquirer.color = 7 # green + def _check_required_if_provider(self): """ If the field has 'required_if_provider=""' attribute, then it required if record.provider is . """ field_names = [] + enabled_acquirers = self.filtered(lambda acq: acq.state in ['enabled', 'test']) for k, f in self._fields.items(): provider = getattr(f, 'required_if_provider', None) if provider and any( acquirer.provider == provider and not acquirer[k] - for acquirer in self + for acquirer in enabled_acquirers ): ir_field = self.env['ir.model.fields']._get(self._name, k) field_names.append(ir_field.field_description) @@ -230,7 +252,7 @@ class PaymentAcquirer(models.Model): 'default_debit_account_id': account.id, 'default_credit_account_id': account.id, # Show the journal on dashboard if the acquirer is published on the website. - 'show_on_dashboard': self.website_published, + 'show_on_dashboard': self.state == 'enabled', # Don't show payment methods in the backend. 'inbound_payment_method_ids': inbound_payment_method_ids, 'outbound_payment_method_ids': [], @@ -273,16 +295,6 @@ class PaymentAcquirer(models.Model): self._check_required_if_provider() return result - def toggle_website_published(self): - ''' When clicking on the website publish toggle button, the website_published is reversed and - the acquirer journal is set or not in favorite on the dashboard. - ''' - self.ensure_one() - self.website_published = not self.website_published - if self.journal_id: - self.journal_id.show_on_dashboard = self.website_published - return True - def get_acquirer_extra_fees(self, amount, currency_id, country_id): extra_fees = { 'currency_id': currency_id @@ -314,7 +326,7 @@ class PaymentAcquirer(models.Model): company = self.env.company if not partner: partner = self.env.user.partner_id - active_acquirers = self.sudo().search([('website_published', '=', True), ('company_id', '=', company.id)]) + active_acquirers = self.sudo().search([('state', 'in', ['enabled', 'test']), ('company_id', '=', company.id)]) acquirers = active_acquirers.filtered(lambda acq: (acq.payment_flow == 'form' and acq.view_template_id) or (acq.payment_flow == 's2s' and acq.registration_view_template_id)) return { @@ -477,11 +489,6 @@ class PaymentAcquirer(models.Model): return method(data) return True - def toggle_environment_value(self): - prod = self.filtered(lambda acquirer: acquirer.environment == 'prod') - prod.write({'environment': 'test'}) - (self-prod).write({'environment': 'prod'}) - def button_immediate_install(self): # TDE FIXME: remove that brol if self.module_id and self.module_state != 'installed': @@ -693,7 +700,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.')) @@ -855,7 +862,7 @@ class PaymentTransaction(models.Model): @api.model def create(self, values): - # call custom create method if defined (i.e. ogone_create for ogone) + # call custom create method if defined acquirer = self.env['payment.acquirer'].browse(values['acquirer_id']) if values.get('partner_id'): partner = self.env['res.partner'].browse(values['partner_id']) @@ -1019,7 +1026,7 @@ class PaymentToken(models.Model): @api.model def create(self, values): - # call custom create method if defined (i.e. ogone_create for ogone) + # call custom create method if defined if values.get('acquirer_id'): acquirer = self.env['payment.acquirer'].browse(values['acquirer_id']) diff --git a/addons/payment/static/img/ideal.png b/addons/payment/static/img/ideal.png new file mode 100644 index 00000000000..e506097ca55 Binary files /dev/null and b/addons/payment/static/img/ideal.png differ diff --git a/addons/payment/static/src/js/payment_processing.js b/addons/payment/static/src/js/payment_processing.js index ec1028188ec..361cc3dbf4c 100644 --- a/addons/payment/static/src/js/payment_processing.js +++ b/addons/payment/static/src/js/payment_processing.js @@ -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': '

    ' + '
    ' + msg + diff --git a/addons/payment/static/src/scss/payment_acquirer.scss b/addons/payment/static/src/scss/payment_acquirer.scss index 5f71663533e..e73582ec847 100644 --- a/addons/payment/static/src/scss/payment_acquirer.scss +++ b/addons/payment/static/src/scss/payment_acquirer.scss @@ -42,6 +42,7 @@ .o_form_view { .o_payment_acquirer_desc { + margin-top: 10px; ul { list-style-type: none; padding: 0; diff --git a/addons/payment/views/payment_portal_templates.xml b/addons/payment/views/payment_portal_templates.xml index 20d519599be..75d4008173b 100644 --- a/addons/payment/views/payment_portal_templates.xml +++ b/addons/payment/views/payment_portal_templates.xml @@ -33,7 +33,7 @@