summaryrefslogtreecommitdiff
path: root/addons/pos_adyen
diff options
context:
space:
mode:
authorAntoine Prieels <anp@odoo.com>2020-03-25 14:09:24 +0000
committerPierre Masereel <pim@odoo.com>2020-09-29 11:44:46 +0000
commite6dc4ea2e6c321829ed2fe8c1c7b0e58db354935 (patch)
treef18015990f7d66ff0db3d1954b881982a2fa4015 /addons/pos_adyen
parent761639b56b0c9210c77e277f01cea45725b82ef6 (diff)
[ADD] adyen_platforms,pos_adyen: Add support for Adyen for Platforms
The goal is to add a nice payment solution to our users. - Why Adyen? - Worldwide - Manages local payment solutions - Easy onboarding - For both PoS and eCommerce - Why Adyen for Platforms? - The onboarding will be done directly from the customer's DB. - It's quite complicated for SMEs to get a partnership with Adyen as they have high requirements for transaction volume. By registering Odoo as a Platform with Adyen Marketpay, we allow our users to register as sub-merchants under a common account, removing this constraint. As all users are grouped under a common account, all requests need to provide the same API key. As this key cannot be shared, all requests to the Adyen API will need to be proxied. A new proxy will be created on `paymentproxy.odoo.com`. To initiate a payment, 3 values are required: - `account_holder_code`: This code is provided by Adyen and is used to identify the sub-merchant under the common account. - `adyen_uuid`: This ID is used internally to identify the user. - `proxy_token`: This token is generated by the proxy during the account creation and is used to sign all requests. The DB of the customer is the only host who has all the information required to modify the sub-merchant info or initiate a payment. If this information leak, the `proxy_token` can be reset on the proxy to revoke all access to the API. Before accessing the form to create an account, users will be redirected to www.odoo.com where they will be asked to log in. This ensures that we have contact information for this sub-merchant. TaskID: 2129218
Diffstat (limited to 'addons/pos_adyen')
-rw-r--r--addons/pos_adyen/__manifest__.py6
-rw-r--r--addons/pos_adyen/models/__init__.py2
-rw-r--r--addons/pos_adyen/models/adyen_account.py107
-rw-r--r--addons/pos_adyen/models/pos_payment_method.py71
-rw-r--r--addons/pos_adyen/models/res_config_settings.py13
-rw-r--r--addons/pos_adyen/security/ir.model.access.csv4
-rw-r--r--addons/pos_adyen/static/src/js/models.js1
-rw-r--r--addons/pos_adyen/static/src/js/payment_adyen.js11
-rw-r--r--addons/pos_adyen/views/adyen_account_views.xml68
-rw-r--r--addons/pos_adyen/views/pos_payment_method_views.xml25
-rw-r--r--addons/pos_adyen/views/res_config_settings_views.xml20
11 files changed, 288 insertions, 40 deletions
diff --git a/addons/pos_adyen/__manifest__.py b/addons/pos_adyen/__manifest__.py
index b291bdaabc2..d34a34d5b47 100644
--- a/addons/pos_adyen/__manifest__.py
+++ b/addons/pos_adyen/__manifest__.py
@@ -8,12 +8,14 @@
'summary': 'Integrate your POS with an Adyen payment terminal',
'description': '',
'data': [
+ 'security/ir.model.access.csv',
+ 'views/adyen_account_views.xml',
'views/pos_config_views.xml',
'views/pos_payment_method_views.xml',
'views/point_of_sale_assets.xml',
+ 'views/res_config_settings_views.xml',
],
- 'depends': ['point_of_sale'],
+ 'depends': ['adyen_platforms', 'point_of_sale'],
'qweb': ['static/src/xml/pos.xml'],
'installable': True,
- 'license': 'OEEL-1',
}
diff --git a/addons/pos_adyen/models/__init__.py b/addons/pos_adyen/models/__init__.py
index 77e4db1fdaf..d8557bb1c89 100644
--- a/addons/pos_adyen/models/__init__.py
+++ b/addons/pos_adyen/models/__init__.py
@@ -1,5 +1,7 @@
# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import adyen_account
from . import pos_config
from . import pos_payment_method
+from . import res_config_settings
diff --git a/addons/pos_adyen/models/adyen_account.py b/addons/pos_adyen/models/adyen_account.py
new file mode 100644
index 00000000000..2db95110ccb
--- /dev/null
+++ b/addons/pos_adyen/models/adyen_account.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import uuid
+from werkzeug.urls import url_join
+
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+
+
+class AdyenAccount(models.Model):
+ _inherit = 'adyen.account'
+
+ store_ids = fields.One2many('adyen.store', 'adyen_account_id')
+ terminal_ids = fields.One2many('adyen.terminal', 'adyen_account_id')
+
+ def action_order_terminal(self):
+ if not self.store_ids:
+ raise ValidationError(_('Please create a store first.'))
+
+ store_uuids = ','.join(self.store_ids.mapped('store_uuid'))
+ onboarding_url = self.env['ir.config_parameter'].sudo().get_param('adyen_platforms.onboarding_url')
+ return {
+ 'type': 'ir.actions.act_url',
+ 'target': 'new',
+ 'url': url_join(onboarding_url, 'order_terminals?store_uuids=%s' % store_uuids),
+ }
+
+
+class AdyenStore(models.Model):
+ _name = 'adyen.store'
+ _inherit = ['adyen.address.mixin']
+ _description = 'Adyen for Plaforms Store'
+
+ adyen_account_id = fields.Many2one('adyen.account', ondelete='cascade')
+ store_reference = fields.Char('Reference', default=lambda self: uuid.uuid4().hex)
+ store_uuid = fields.Char('UUID', readonly=True) # Given by Adyen
+ name = fields.Char('Name', required=True)
+ phone_number = fields.Char('Phone Number', required=True)
+ terminal_ids = fields.One2many('adyen.terminal', 'store_id', string='Payment Terminals', readonly=True)
+
+ @api.model
+ def create(self, values):
+ adyen_store_id = super(AdyenStore, self).create(values)
+ response = adyen_store_id.adyen_account_id._adyen_rpc('update_account_holder', adyen_store_id._format_data())
+ stores = response['accountHolderDetails']['storeDetails']
+ created_store = next(store for store in stores if store['storeReference'] == adyen_store_id.store_reference)
+ adyen_store_id.with_context(update_from_adyen=True).sudo().write({
+ 'store_uuid': created_store['store'],
+ })
+ return adyen_store_id
+
+ def unlink(self):
+ for store_id in self:
+ store_id.adyen_account_id._adyen_rpc('close_stores', {
+ 'accountHolderCode': store_id.adyen_account_id.account_holder_code,
+ 'stores': [store_id.store_uuid],
+ })
+ return super(AdyenStore, self).unlink()
+
+ def _format_data(self):
+ return {
+ 'accountHolderCode': self.adyen_account_id.account_holder_code,
+ 'accountHolderDetails': {
+ 'storeDetails': [{
+ 'storeReference': self.store_reference,
+ 'storeName': self.name,
+ 'merchantAccount': 'OdooMP_POS',
+ 'merchantCategoryCode': '7999',
+ 'address': {
+ 'city': self.city,
+ 'country': self.country_id.code,
+ 'houseNumberOrName': self.house_number_or_name,
+ 'postalCode': self.zip,
+ 'stateOrProvince': self.state_id.code or None,
+ 'street': self.street,
+ },
+ 'fullPhoneNumber': self.phone_number,
+ }],
+ }
+ }
+
+
+class AdyenTerminal(models.Model):
+ _name = 'adyen.terminal'
+ _description = 'Adyen for Plaforms Terminal'
+ _rec_name = 'terminal_uuid'
+
+ adyen_account_id = fields.Many2one('adyen.account', ondelete='cascade')
+ store_id = fields.Many2one('adyen.store')
+ terminal_uuid = fields.Char('Terminal ID')
+
+ @api.model
+ def _sync_adyen_terminals(self):
+ for adyen_store_id in self.env['adyen.store'].search([]):
+ response = adyen_store_id.adyen_account_id._adyen_rpc('connected_terminals', {
+ 'store': adyen_store_id.store_uuid,
+ })
+ terminals_in_db = set(self.search([('store_id', '=', adyen_store_id.id)]).mapped('terminal_uuid'))
+
+ # Added terminals
+ for terminal in set(response.get('uniqueTerminalIds')) - terminals_in_db:
+ self.sudo().create({
+ 'adyen_account_id': adyen_store_id.adyen_account_id.id,
+ 'store_id': adyen_store_id.id,
+ 'terminal_uuid': terminal,
+ })
diff --git a/addons/pos_adyen/models/pos_payment_method.py b/addons/pos_adyen/models/pos_payment_method.py
index ddc0ab395b9..73c09287bb3 100644
--- a/addons/pos_adyen/models/pos_payment_method.py
+++ b/addons/pos_adyen/models/pos_payment_method.py
@@ -6,6 +6,7 @@ import pprint
import random
import requests
import string
+from werkzeug.exceptions import Forbidden
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
@@ -16,11 +17,18 @@ class PosPaymentMethod(models.Model):
_inherit = 'pos.payment.method'
def _get_payment_terminal_selection(self):
- return super(PosPaymentMethod, self)._get_payment_terminal_selection() + [('adyen', 'Adyen')]
+ return super(PosPaymentMethod, self)._get_payment_terminal_selection() + [('odoo_adyen', 'Odoo Payments by Adyen'), ('adyen', 'Adyen')]
+ # Adyen
adyen_api_key = fields.Char(string="Adyen API key", help='Used when connecting to Adyen: https://docs.adyen.com/user-management/how-to-get-the-api-key/#description', copy=False)
adyen_terminal_identifier = fields.Char(help='[Terminal model]-[Serial number], for example: P400Plus-123456789', copy=False)
adyen_test_mode = fields.Boolean(help='Run transactions in the test environment.')
+
+ # Odoo Payments by Adyen
+ adyen_account_id = fields.Many2one('adyen.account', related='company_id.adyen_account_id')
+ adyen_payout_id = fields.Many2one('adyen.payout', string='Adyen Payout', domain="[('adyen_account_id', '=', adyen_account_id)]")
+ adyen_terminal_id = fields.Many2one('adyen.terminal', string='Adyen Terminal', domain="[('adyen_account_id', '=', adyen_account_id)]")
+
adyen_latest_response = fields.Char(help='Technical field used to buffer the latest asynchronous notification from Adyen.', copy=False, groups='base.group_erp_manager')
adyen_latest_diagnosis = fields.Char(help='Technical field used to determine if the terminal is still connected.', copy=False, groups='base.group_erp_manager')
@@ -36,6 +44,17 @@ class PosPaymentMethod(models.Model):
raise ValidationError(_('Terminal %s is already used on payment method %s.')
% (payment_method.adyen_terminal_identifier, existing_payment_method.display_name))
+ def _get_adyen_endpoints(self):
+ return {
+ 'terminal_request': 'https://terminal-api-%s.adyen.com/async',
+ }
+
+ @api.onchange('adyen_terminal_id')
+ def onchange_use_payment_terminal(self):
+ for payment_method in self:
+ if payment_method.use_payment_terminal == 'odoo_adyen' and payment_method.adyen_terminal_id:
+ payment_method.adyen_terminal_identifier = payment_method.adyen_terminal_id.terminal_uuid
+
def _is_write_forbidden(self, fields):
whitelisted_fields = set(('adyen_latest_response', 'adyen_latest_diagnosis'))
return super(PosPaymentMethod, self)._is_write_forbidden(fields - whitelisted_fields)
@@ -70,34 +89,35 @@ class PosPaymentMethod(models.Model):
latest_response = self.sudo().adyen_latest_response
latest_response = json.loads(latest_response) if latest_response else False
- self.adyen_latest_response = '' # avoid handling old responses multiple times
+ self.sudo().adyen_latest_response = '' # avoid handling old responses multiple times
return {
'latest_response': latest_response,
- 'last_received_diagnosis_id': self.adyen_latest_diagnosis,
+ 'last_received_diagnosis_id': self.sudo().adyen_latest_diagnosis,
}
- def proxy_adyen_request(self, data, test_endpoint=False, live_endpoint=False):
+ def proxy_adyen_request(self, data, operation=False):
''' Necessary because Adyen's endpoints don't have CORS enabled '''
- self.ensure_one()
- TIMEOUT = 10
+ if not operation:
+ operation = 'terminal_request'
- if not test_endpoint:
- test_endpoint = 'https://terminal-api-test.adyen.com/async'
- if not live_endpoint:
- live_endpoint = 'https://terminal-api-live.adyen.com/async'
+ if self.use_payment_terminal == 'odoo_adyen':
+ return self._proxy_adyen_request_odoo_proxy(data, operation)
+ else:
+ return self._proxy_adyen_request_direct(data, operation)
- endpoint = live_endpoint
- if test_mode:
- endpoint = test_endpoint
+ def _proxy_adyen_request_direct(self, data, operation):
+ self.ensure_one()
+ TIMEOUT = 10
_logger.info('request to adyen\n%s', pprint.pformat(data))
+
+ environment = 'test' if self.adyen_test_mode else 'live'
+ endpoint = self._get_adyen_endpoints()[operation] % environment
headers = {
'x-api-key': self.adyen_api_key,
- 'Content-Type': 'application/json'
}
- req = requests.post(endpoint, data=json.dumps(data), headers=headers, timeout=TIMEOUT)
- _logger.info('response from adyen (HTTP status %s):\n%s', req.status_code, req.text)
+ req = requests.post(endpoint, json=data, headers=headers, timeout=TIMEOUT)
# Authentication error doesn't return JSON
if req.status_code == 401:
@@ -113,9 +133,16 @@ class PosPaymentMethod(models.Model):
return req.json()
- @api.onchange('use_payment_terminal')
- def _onchange_use_payment_terminal(self):
- super(PosPaymentMethod, self)._onchange_use_payment_terminal()
- if self.use_payment_terminal != 'adyen':
- self.adyen_api_key = False
- self.adyen_terminal_identifier = False
+ def _proxy_adyen_request_odoo_proxy(self, data, operation):
+ try:
+ return self.env.company.sudo().adyen_account_id._adyen_rpc(operation, {
+ 'request_data': data,
+ 'account_code': self.sudo().adyen_payout_id.code,
+ 'notification_url': self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
+ })
+ except Forbidden:
+ return {
+ 'error': {
+ 'status_code': 401,
+ }
+ }
diff --git a/addons/pos_adyen/models/res_config_settings.py b/addons/pos_adyen/models/res_config_settings.py
new file mode 100644
index 00000000000..4e3b7102c94
--- /dev/null
+++ b/addons/pos_adyen/models/res_config_settings.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ adyen_account_id = fields.Many2one(string='Adyen Account', related='company_id.adyen_account_id')
+
+ def create_adyen_account(self):
+ return self.env['adyen.account'].action_create_redirect()
diff --git a/addons/pos_adyen/security/ir.model.access.csv b/addons/pos_adyen/security/ir.model.access.csv
new file mode 100644
index 00000000000..302878ed368
--- /dev/null
+++ b/addons/pos_adyen/security/ir.model.access.csv
@@ -0,0 +1,4 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_adyen_store_group_erp_manager,adyen.store,model_adyen_store,base.group_erp_manager,1,0,1,1
+access_adyen_payout_group_pos_manager,adyen.payout,adyen_platforms.model_adyen_payout,point_of_sale.group_pos_manager,1,0,0,0
+access_adyen_terminal_group_pos_manager,adyen.terminal,model_adyen_terminal,point_of_sale.group_pos_manager,1,0,0,0
diff --git a/addons/pos_adyen/static/src/js/models.js b/addons/pos_adyen/static/src/js/models.js
index f2734e00a94..c022ea7ba2b 100644
--- a/addons/pos_adyen/static/src/js/models.js
+++ b/addons/pos_adyen/static/src/js/models.js
@@ -3,5 +3,6 @@ var models = require('point_of_sale.models');
var PaymentAdyen = require('pos_adyen.payment');
models.register_payment_method('adyen', PaymentAdyen);
+models.register_payment_method('odoo_adyen', PaymentAdyen);
models.load_fields('pos.payment.method', 'adyen_terminal_identifier');
});
diff --git a/addons/pos_adyen/static/src/js/payment_adyen.js b/addons/pos_adyen/static/src/js/payment_adyen.js
index 82c6a9f91ea..7e0350f8328 100644
--- a/addons/pos_adyen/static/src/js/payment_adyen.js
+++ b/addons/pos_adyen/static/src/js/payment_adyen.js
@@ -43,12 +43,11 @@ var PaymentAdyen = PaymentInterface.extend({
return Promise.reject(data); // prevent subsequent onFullFilled's from being called
},
- _call_adyen: function (data, test_endpoint, live_endpoint) {
- var self = this;
+ _call_adyen: function (data, operation) {
return rpc.query({
model: 'pos.payment.method',
method: 'proxy_adyen_request',
- args: [[this.payment_method.id], data, test_endpoint, live_endpoint],
+ args: [[this.payment_method.id], data, operation],
}, {
// When a payment terminal is disconnected it takes Adyen
// a while to return an error (~6s). So wait 10 seconds
@@ -121,6 +120,7 @@ var PaymentAdyen = PaymentInterface.extend({
},
_adyen_cancel: function (ignore_error) {
+ var self = this;
var previous_service_id = this.most_recent_service_id;
var header = _.extend(this._adyen_common_message_header(), {
'MessageCategory': 'Abort',
@@ -133,7 +133,6 @@ var PaymentAdyen = PaymentInterface.extend({
'AbortReason': 'MerchantAbort',
'MessageReference': {
'MessageCategory': 'Payment',
- 'SaleID': header.SaleID,
'ServiceID': previous_service_id,
}
},
@@ -144,7 +143,7 @@ var PaymentAdyen = PaymentInterface.extend({
// Only valid response is a 200 OK HTTP response which is
// represented by true.
- if (! ignore_error && data !== true) {
+ if (! ignore_error && data !== "ok") {
self._show_error(_('Cancelling the payment failed. Please cancel it manually on the payment terminal.'));
}
});
@@ -238,7 +237,7 @@ var PaymentAdyen = PaymentInterface.extend({
if (message.startsWith('108 ')) {
resolve(false);
} else {
- line.set_payment_status('force_done');
+ line.set_payment_status('retry');
reject();
}
}
diff --git a/addons/pos_adyen/views/adyen_account_views.xml b/addons/pos_adyen/views/adyen_account_views.xml
new file mode 100644
index 00000000000..c31771d9d97
--- /dev/null
+++ b/addons/pos_adyen/views/adyen_account_views.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="adyen_account_view_form" model="ir.ui.view">
+ <field name="name">adyen.account.view.form.inherit.pos.adyen</field>
+ <field name="model">adyen.account</field>
+ <field name="inherit_id" ref="adyen_platforms.adyen_account_view_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//header" position="inside">
+ <button name="action_order_terminal" string="Order a Terminal" class="oe_highlight" type="object" attrs="{'invisible': [('adyen_uuid', '=', False)]}"/>
+ </xpath>
+ <xpath expr="//notebook" position="inside">
+ <page string="Stores">
+ <field name="store_ids">
+ <tree>
+ <field name="name"/>
+ <field name="store_uuid"/>
+ </tree>
+ </field>
+ </page>
+ </xpath>
+ </field>
+ </record>
+
+
+ <record id="adyen_store_view_form" model="ir.ui.view">
+ <field name="name">adyen.store.view.form</field>
+ <field name="model">adyen.store</field>
+ <field name="arch" type="xml">
+ <form string="Adyen Store">
+ <sheet>
+ <group>
+ <field name="store_uuid" attrs="{'invisible': [('id', '=', False)]}"/>
+ <field name="name" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <field name="phone_number" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <label for="street" string="Address"/>
+ <div class="o_address_format">
+ <field name="street" placeholder="Street" class="o_address_street" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <field name="house_number_or_name" placeholder="House number or name" class="o_address_street" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <field name="city" placeholder="City" class="o_address_city" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <field name="state_id" class="o_address_state" placeholder="State" options="{'no_open': True, 'no_quick_create': True}"
+ attrs="{'required': [('country_code', 'in', ['AU', 'CA', 'IT', 'US'])], 'readonly': [('id', '!=', False)]}"/>
+ <field name="zip" placeholder="ZIP" class="o_address_zip" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <field name="country_id" placeholder="Country" class="o_address_country" options="{'no_open': True, 'no_create': True}" attrs="{'readonly': [('id', '!=', False)]}"/>
+ <field name="country_code" invisible="1"/>
+ <field name="terminal_ids" attrs="{'invisible': [('terminal_ids', '=', [])]}"/>
+ </div>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_pos_adyen_account" model="ir.actions.server">
+ <field name="name">Adyen Account</field>
+ <field name="model_id" ref="model_adyen_account"/>
+ <field name="state">code</field>
+ <field name="code">
+ action = model.action_create_redirect()
+ </field>
+ </record>
+
+ <menuitem id="menu_pos_adyen_account"
+ parent="point_of_sale.menu_point_config_product"
+ action="action_pos_adyen_account"
+ sequence="5"
+ groups="point_of_sale.group_pos_manager,point_of_sale.group_pos_user"/>
+
+</odoo>
diff --git a/addons/pos_adyen/views/pos_payment_method_views.xml b/addons/pos_adyen/views/pos_payment_method_views.xml
index 7c1d28597f0..fe6ce37dae2 100644
--- a/addons/pos_adyen/views/pos_payment_method_views.xml
+++ b/addons/pos_adyen/views/pos_payment_method_views.xml
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="pos_payment_method_view_form_inherit_pos_adyen" model="ir.ui.view">
- <field name="name">pos.payment.method.form.inherit.adyen</field>
- <field name="model">pos.payment.method</field>
- <field name="inherit_id" ref="point_of_sale.pos_payment_method_view_form"/>
- <field name="arch" type="xml">
- <xpath expr="//field[@name='use_payment_terminal']" position="after">
- <field name="adyen_api_key"
- attrs="{'invisible': [('use_payment_terminal', '!=', 'adyen')], 'required': [('use_payment_terminal', '=', 'adyen')]}"/>
+ <field name="name">pos.payment.method.form.inherit.adyen</field>
+ <field name="model">pos.payment.method</field>
+ <field name="inherit_id" ref="point_of_sale.pos_payment_method_view_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='use_payment_terminal']" position="after">
+ <!-- Adyen -->
+ <field name="adyen_api_key" attrs="{'invisible': [('use_payment_terminal', '!=', 'adyen')], 'required': [('use_payment_terminal', '=', 'adyen')]}"/>
<field name="adyen_terminal_identifier" attrs="{'invisible': [('use_payment_terminal', '!=', 'adyen')], 'required': [('use_payment_terminal', '=', 'adyen')]}"/>
- <field name="adyen_test_mode" attrs="{'invisible': [('use_payment_terminal', '!=', 'adyen')]}"/>
- </xpath>
- </field>
+ <field name="adyen_test_mode" attrs="{'invisible': [('use_payment_terminal', '!=', 'adyen')], 'required': [('use_payment_terminal', '=', 'adyen')]}"/>
+
+ <!-- Odoo Payments by Adyen -->
+ <field name="adyen_account_id" invisible="1"/>
+ <field name="adyen_terminal_id" attrs="{'invisible': [('use_payment_terminal', '!=', 'odoo_adyen')], 'required': [('use_payment_terminal', '=', 'odoo_adyen')]}"/>
+ <field name="adyen_payout_id" attrs="{'invisible': [('use_payment_terminal', '!=', 'odoo_adyen')], 'required': [('use_payment_terminal', '=', 'odoo_adyen')]}" options="{'no_create': True}"/>
+ </xpath>
+ </field>
</record>
</odoo>
diff --git a/addons/pos_adyen/views/res_config_settings_views.xml b/addons/pos_adyen/views/res_config_settings_views.xml
new file mode 100644
index 00000000000..4d9995072ef
--- /dev/null
+++ b/addons/pos_adyen/views/res_config_settings_views.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="res_config_settings_view_form" model="ir.ui.view">
+ <field name="name">res.config.settings.view.form.inherit.point_of_sale</field>
+ <field name="model">res.config.settings</field>
+ <field name="priority" eval="95"/>
+ <field name="inherit_id" ref="base.res_config_settings_view_form" />
+ <field name="arch" type="xml">
+ <xpath expr="//div[@id='adyen_payment_terminal_setting']/div[hasclass('o_setting_right_pane')]" position="inside">
+ <span attrs="{'invisible': [('adyen_account_id', '=', False)]}">
+ <label for="adyen_account_id" string="Account"/>
+ <span class="fa fa-lg fa-building-o mr8" title="Values set here are company-specific." aria-label="Values set here are company-specific." groups="base.group_multi_company" role="img"/>
+ <field name="adyen_account_id"/>
+ </span>
+ <button name="create_adyen_account" icon="fa-arrow-right" type="object" class="btn-link" string="Create an account in 1 minute"
+ attrs="{'invisible': [('adyen_account_id', '!=', False)]}"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>