mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
Initial commit of Forte Payment integration and parts of hr_payroll_payment to support it.
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
Adds the ability to register a payment on a payslip.
|
Adds the ability to register a payment on a payslip.
|
||||||
""",
|
""",
|
||||||
'website': 'https://hibou.io/',
|
'website': 'https://hibou.io/',
|
||||||
'depends': ['hr_payroll_account'],
|
'depends': ['hr_payroll_account', 'payment'],
|
||||||
'data': [
|
'data': [
|
||||||
'hr_payroll_register_payment.xml',
|
'hr_payroll_register_payment.xml',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ class HrPayrollRegisterPaymentWizard(models.TransientModel):
|
|||||||
journal_id = fields.Many2one('account.journal', string='Payment Method', required=True, domain=[('type', 'in', ('bank', 'cash'))])
|
journal_id = fields.Many2one('account.journal', string='Payment Method', required=True, domain=[('type', 'in', ('bank', 'cash'))])
|
||||||
company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', readonly=True, required=True)
|
company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', readonly=True, required=True)
|
||||||
payment_method_id = fields.Many2one('account.payment.method', string='Payment Type', required=True)
|
payment_method_id = fields.Many2one('account.payment.method', string='Payment Type', required=True)
|
||||||
|
payment_method_code = fields.Char(related='payment_method_id.code',
|
||||||
|
help="Technical field used to adapt the interface to the payment type selected.", readonly=True)
|
||||||
|
payment_transaction_id = fields.Many2one('payment.transaction', string="Payment Transaction")
|
||||||
|
payment_token_id = fields.Many2one('payment.token', string="Saved payment token",
|
||||||
|
domain=[('acquirer_id.capture_manually', '=', False)],
|
||||||
|
help="Note that tokens from acquirers set to only authorize transactions (instead of capturing the amount) are not available.")
|
||||||
amount = fields.Monetary(string='Payment Amount', required=True, default=_default_amount)
|
amount = fields.Monetary(string='Payment Amount', required=True, default=_default_amount)
|
||||||
currency_id = fields.Many2one('res.currency', string='Currency', required=True, default=lambda self: self.env.user.company_id.currency_id)
|
currency_id = fields.Many2one('res.currency', string='Currency', required=True, default=lambda self: self.env.user.company_id.currency_id)
|
||||||
payment_date = fields.Date(string='Payment Date', default=fields.Date.context_today, required=True)
|
payment_date = fields.Date(string='Payment Date', default=fields.Date.context_today, required=True)
|
||||||
@@ -63,6 +69,24 @@ class HrPayrollRegisterPaymentWizard(models.TransientModel):
|
|||||||
hide_payment_method = fields.Boolean(compute='_compute_hide_payment_method',
|
hide_payment_method = fields.Boolean(compute='_compute_hide_payment_method',
|
||||||
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'")
|
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'")
|
||||||
|
|
||||||
|
@api.onchange('partner_id')
|
||||||
|
def _onchange_partner_id(self):
|
||||||
|
res = {}
|
||||||
|
if self.partner_id:
|
||||||
|
partners = self.partner_id | self.partner_id.commercial_partner_id | self.partner_id.commercial_partner_id.child_ids
|
||||||
|
res['domain'] = {
|
||||||
|
'payment_token_id': [('partner_id', 'in', partners.ids), ('acquirer_id.capture_manually', '=', False)]}
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.onchange('payment_method_id', 'journal_id')
|
||||||
|
def _onchange_payment_method(self):
|
||||||
|
if self.payment_method_code == 'electronic':
|
||||||
|
self.payment_token_id = self.env['payment.token'].search(
|
||||||
|
[('partner_id', '=', self.partner_id.id), ('acquirer_id.capture_manually', '=', False)], limit=1)
|
||||||
|
else:
|
||||||
|
self.payment_token_id = False
|
||||||
|
|
||||||
@api.one
|
@api.one
|
||||||
@api.constrains('amount')
|
@api.constrains('amount')
|
||||||
def _check_amount(self):
|
def _check_amount(self):
|
||||||
@@ -106,7 +130,9 @@ class HrPayrollRegisterPaymentWizard(models.TransientModel):
|
|||||||
'amount': self.amount,
|
'amount': self.amount,
|
||||||
'currency_id': self.currency_id.id,
|
'currency_id': self.currency_id.id,
|
||||||
'payment_date': self.payment_date,
|
'payment_date': self.payment_date,
|
||||||
'communication': self.communication
|
'communication': self.communication,
|
||||||
|
'payment_transaction_id': self.payment_transaction_id.id if self.payment_transaction_id else False,
|
||||||
|
'payment_token_id': self.payment_token_id.id if self.payment_token_id else False,
|
||||||
})
|
})
|
||||||
payment.post()
|
payment.post()
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,10 @@
|
|||||||
<field name="journal_id" widget="selection"/>
|
<field name="journal_id" widget="selection"/>
|
||||||
<field name="hide_payment_method" invisible="1"/>
|
<field name="hide_payment_method" invisible="1"/>
|
||||||
<field name="payment_method_id" widget="radio" attrs="{'invisible': [('hide_payment_method', '=', True)]}"/>
|
<field name="payment_method_id" widget="radio" attrs="{'invisible': [('hide_payment_method', '=', True)]}"/>
|
||||||
|
<field name="payment_method_code" invisible="1"/>
|
||||||
|
<field name="payment_token_id" options="{'no_create': True}"
|
||||||
|
attrs="{'invisible': [('payment_method_code', '!=', 'electronic')],
|
||||||
|
'required': [('payment_method_code', '=', 'electronic')]}"/>
|
||||||
<label for="amount"/>
|
<label for="amount"/>
|
||||||
<div name="amount_div" class="o_row">
|
<div name="amount_div" class="o_row">
|
||||||
<field name="amount"/>
|
<field name="amount"/>
|
||||||
@@ -42,6 +46,7 @@
|
|||||||
<group>
|
<group>
|
||||||
<field name="payment_date"/>
|
<field name="payment_date"/>
|
||||||
<field name="communication"/>
|
<field name="communication"/>
|
||||||
|
<field name="payment_transaction_id"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
|
|||||||
1
payment_forte/__init__.py
Normal file
1
payment_forte/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
14
payment_forte/__manifest__.py
Normal file
14
payment_forte/__manifest__.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
'name': 'Forte Payment Acquirer',
|
||||||
|
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||||
|
'category': 'Accounting',
|
||||||
|
'summary': 'Payment Acquirer: Forte Implementation',
|
||||||
|
'version': '11.0.1.0.0',
|
||||||
|
'description': """Forte Payment Acquirer""",
|
||||||
|
'depends': ['payment'],
|
||||||
|
'data': [
|
||||||
|
'views/payment_views.xml',
|
||||||
|
'data/payment_acquirer_data.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
}
|
||||||
16
payment_forte/data/payment_acquirer_data.xml
Normal file
16
payment_forte/data/payment_acquirer_data.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="payment.payment_acquirer_forte" model="payment.acquirer">
|
||||||
|
<field name="name">Forte</field>
|
||||||
|
<field name="provider">forte</field>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="environment">test</field>
|
||||||
|
<field name="forte_organization_id">1000</field>
|
||||||
|
<field name="forte_access_id">dummy</field>
|
||||||
|
<field name="forte_secure_key">dummy</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
1
payment_forte/models/__init__.py
Normal file
1
payment_forte/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import payment
|
||||||
81
payment_forte/models/forte_request.py
Normal file
81
payment_forte/models/forte_request.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
from base64 import b64encode
|
||||||
|
from json import dumps
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class ForteAPI:
|
||||||
|
|
||||||
|
url_prod = 'https://api.forte.net/v3'
|
||||||
|
url_test = 'https://sandbox.forte.net/api/v3'
|
||||||
|
|
||||||
|
def __init__(self, organization_id, access_id, secure_key, environment):
|
||||||
|
self.organization_id = organization_id
|
||||||
|
self.access_id = access_id
|
||||||
|
self.secure_key = secure_key
|
||||||
|
self.basic_key = b64encode(bytes(access_id + ':' + secure_key, 'UTF-8')).decode()
|
||||||
|
self.environment = environment
|
||||||
|
self.headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Forte-Auth-Organization-Id': 'org_' + self.organization_id,
|
||||||
|
'Authorization': 'Basic ' + self.basic_key,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _build_request(self, location, method, data=None):
|
||||||
|
url = self.url_prod
|
||||||
|
if self.environment == 'test':
|
||||||
|
url = self.url_test
|
||||||
|
url += location
|
||||||
|
return requests.request(method, url, headers=self.headers, data=data)
|
||||||
|
|
||||||
|
def test_authenticate(self, location=None):
|
||||||
|
if location:
|
||||||
|
url = '/organizations/org_%s/locations/loc_%s/transactions/' % (self.organization_id, location)
|
||||||
|
else:
|
||||||
|
url = '/organizations/org_%s/transactions/' % (self.organization_id, )
|
||||||
|
return self._build_request(url, 'GET')
|
||||||
|
|
||||||
|
def _echeck_values(self, location, amount, account_type, routing_number, account_number, account_holder):
|
||||||
|
#{
|
||||||
|
# "action": "sale",
|
||||||
|
# "authorization_amount": 200.0,
|
||||||
|
# "billing_address": {
|
||||||
|
# "first_name": "Jared",
|
||||||
|
# "last_name": "Kipe"},
|
||||||
|
# "echeck": {
|
||||||
|
# "sec_code": "WEB",
|
||||||
|
# "account_type": "Checking",
|
||||||
|
# "routing_number": "021000021",
|
||||||
|
# "account_number": "000111222",
|
||||||
|
# "account_holder": "Jared Kipe"}
|
||||||
|
#}
|
||||||
|
holder_array = account_holder.strip().split()
|
||||||
|
first_name = ''
|
||||||
|
last_name = holder_array[-1]
|
||||||
|
if len(holder_array) >= 2:
|
||||||
|
first_name = ' '.join(holder_array[:-1])
|
||||||
|
data = {
|
||||||
|
'action': 'sale',
|
||||||
|
'authorization_amount': amount,
|
||||||
|
'billing_address': {
|
||||||
|
'first_name': first_name,
|
||||||
|
'last_name': last_name,
|
||||||
|
},
|
||||||
|
'echeck': {
|
||||||
|
'sec_code': 'WEB',
|
||||||
|
'account_type': account_type,
|
||||||
|
'routing_number': routing_number,
|
||||||
|
'account_number': account_number,
|
||||||
|
'account_holder': account_holder,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
url = '/organizations/org_%s/locations/loc_%s/transactions/' % (self.organization_id, location)
|
||||||
|
return url, data
|
||||||
|
|
||||||
|
def echeck_sale(self, location, amount, account_type, routing_number, account_number, account_holder):
|
||||||
|
url, data = self._echeck_values(location, amount, account_type, routing_number, account_number, account_holder)
|
||||||
|
return self._build_request(url, 'POST', data=dumps(data))
|
||||||
|
|
||||||
|
def echeck_credit(self, location, amount, account_type, routing_number, account_number, account_holder):
|
||||||
|
url, data = self._echeck_values(location, amount, account_type, routing_number, account_number, account_holder)
|
||||||
|
data['action'] = 'credit'
|
||||||
|
return self._build_request(url, 'POST', data=dumps(data))
|
||||||
132
payment_forte/models/payment.py
Normal file
132
payment_forte/models/payment.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from .forte_request import ForteAPI
|
||||||
|
from json import dumps
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def forte_get_api(acquirer):
|
||||||
|
return ForteAPI(acquirer.forte_organization_id,
|
||||||
|
acquirer.forte_access_id,
|
||||||
|
acquirer.forte_secure_key,
|
||||||
|
acquirer.environment)
|
||||||
|
|
||||||
|
|
||||||
|
class PaymentAcquirerForte(models.Model):
|
||||||
|
_inherit = 'payment.acquirer'
|
||||||
|
|
||||||
|
provider = fields.Selection(selection_add=[('forte', 'Forte')])
|
||||||
|
forte_organization_id = fields.Char(string='Organization ID')
|
||||||
|
forte_location_id = fields.Char(string='Location ID') # Probably move to Journal...
|
||||||
|
forte_access_id = fields.Char(string='Access ID')
|
||||||
|
forte_secure_key = fields.Char(string='Secure Key')
|
||||||
|
|
||||||
|
def _get_feature_support(self):
|
||||||
|
"""Get advanced feature support by provider.
|
||||||
|
|
||||||
|
Each provider should add its technical in the corresponding
|
||||||
|
key for the following features:
|
||||||
|
* fees: support payment fees computations
|
||||||
|
* authorize: support authorizing payment (separates
|
||||||
|
authorization and capture)
|
||||||
|
* tokenize: support saving payment data in a payment.tokenize
|
||||||
|
object
|
||||||
|
"""
|
||||||
|
res = super(PaymentAcquirerForte, self)._get_feature_support()
|
||||||
|
res['authorize'].append('authorize')
|
||||||
|
res['tokenize'].append('authorize')
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def forte_test_credentials(self):
|
||||||
|
self.ensure_one()
|
||||||
|
api = forte_get_api(self)
|
||||||
|
resp = api.test_authenticate()
|
||||||
|
if not resp.ok:
|
||||||
|
result = resp.json()
|
||||||
|
if result and result.get('response'):
|
||||||
|
raise ValidationError('Error: ' + dumps(result.get('response')))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AccountPayment(models.Model):
|
||||||
|
_inherit = 'account.payment'
|
||||||
|
|
||||||
|
def _do_payment(self):
|
||||||
|
self = self.with_context(payment_type=self.payment_type)
|
||||||
|
return super(AccountPayment, self)._do_payment()
|
||||||
|
|
||||||
|
|
||||||
|
class TxForte(models.Model):
|
||||||
|
_inherit = 'payment.transaction'
|
||||||
|
|
||||||
|
def forte_s2s_do_transaction(self, **data):
|
||||||
|
self.ensure_one()
|
||||||
|
api = forte_get_api(self.acquirer_id)
|
||||||
|
location = self.acquirer_id.forte_location_id
|
||||||
|
amount = self.amount
|
||||||
|
account_type = self.payment_token_id.forte_account_type
|
||||||
|
routing_number = self.payment_token_id.forte_routing_number
|
||||||
|
account_number = self.payment_token_id.forte_account_number
|
||||||
|
account_holder = self.payment_token_id.forte_account_holder
|
||||||
|
if not self.env.context.get('payment_type'):
|
||||||
|
_logger.warn('Trying to do a payment with Forte and no contextual payment_type will result in an inbound transaction.')
|
||||||
|
if self.env.context.get('payment_type', 'inbound') == 'inbound':
|
||||||
|
resp = api.echeck_sale(location, amount, account_type, routing_number, account_number, account_holder)
|
||||||
|
else:
|
||||||
|
resp = api.echeck_credit(location, amount, account_type, routing_number, account_number, account_holder)
|
||||||
|
|
||||||
|
if resp.ok and resp.json()['response']['response_desc'] == 'APPROVED':
|
||||||
|
ref = resp.json()['response']['authorization_code']
|
||||||
|
return self.write({'state': 'done', 'acquirer_reference': ref})
|
||||||
|
else:
|
||||||
|
result = resp.json()
|
||||||
|
if result and result.get('response'):
|
||||||
|
raise ValidationError('Error: ' + dumps(result.get('response')))
|
||||||
|
|
||||||
|
def forte_s2s_do_refund(self, **data):
|
||||||
|
self.ensure_one()
|
||||||
|
api = forte_get_api(self.acquirer_id)
|
||||||
|
location = self.acquirer_id.forte_location_id
|
||||||
|
amount = self.amount
|
||||||
|
account_type = self.payment_token_id.forte_account_type
|
||||||
|
routing_number = self.payment_token_id.forte_routing_number
|
||||||
|
account_number = self.payment_token_id.forte_account_number
|
||||||
|
account_holder = self.payment_token_id.forte_account_holder
|
||||||
|
if not self.env.context.get('payment_type'):
|
||||||
|
_logger.warn('Trying to do a refund payment with Forte and no contextual payment_type will result in an inbound transaction refund.')
|
||||||
|
if self.env.context.get('payment_type', 'inbound') == 'inbound':
|
||||||
|
resp = api.echeck_credit(location, amount, account_type, routing_number, account_number, account_holder)
|
||||||
|
else:
|
||||||
|
resp = api.echeck_sale(location, amount, account_type, routing_number, account_number, account_holder)
|
||||||
|
|
||||||
|
if resp.ok and resp.json()['response']['response_desc'] == 'APPROVED':
|
||||||
|
ref = resp.json()['response']['authorization_code']
|
||||||
|
return self.write({'state': 'refunded', 'acquirer_reference': ref})
|
||||||
|
else:
|
||||||
|
result = resp.json()
|
||||||
|
if result and result.get('response'):
|
||||||
|
raise ValidationError('Error: ' + dumps(result.get('response')))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PaymentToken(models.Model):
|
||||||
|
_inherit = 'payment.token'
|
||||||
|
|
||||||
|
forte_account_type = fields.Char(string='Forte Account Type', help='e.g. Checking')
|
||||||
|
forte_routing_number = fields.Char(string='Forte Routing Number', help='e.g. 021000021')
|
||||||
|
forte_account_number = fields.Char(string='Forte Account Number', help='e.g. 000111222')
|
||||||
|
forte_account_holder = fields.Char(string='Forte Account Holder', help='e.g. John Doe')
|
||||||
|
# Boilerplate for views
|
||||||
|
provider = fields.Selection(string='Provider', related='acquirer_id.provider')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def forte_create(self, values):
|
||||||
|
if values.get('forte_account_number'):
|
||||||
|
#acquirer = self.env['payment.acquirer'].browse(values['acquirer_id'])
|
||||||
|
#partner = self.env['res.partner'].browse(values['partner_id'])
|
||||||
|
# eventually check the types and account numbers
|
||||||
|
pass
|
||||||
|
return values
|
||||||
1
payment_forte/tests/__init__.py
Normal file
1
payment_forte/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import test_forte
|
||||||
45
payment_forte/tests/test_forte.py
Normal file
45
payment_forte/tests/test_forte.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from odoo.addons.payment.tests.common import PaymentAcquirerCommon
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class ForteCommon(PaymentAcquirerCommon):
|
||||||
|
def setUp(self):
|
||||||
|
super(ForteCommon, self).setUp()
|
||||||
|
self.currency_usd = self.env['res.currency'].search([('name', '=', 'USD')], limit=1)[0]
|
||||||
|
self.forte = self.env.ref('payment.payment_acquirer_forte')
|
||||||
|
self.method = self.env['account.payment.method'].search([('code', '=', 'electronic')], limit=1)[0]
|
||||||
|
self.journal = self.env['account.journal'].search([], limit=1)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class ForteACH(ForteCommon):
|
||||||
|
def test_10_forte_api(self):
|
||||||
|
self.assertEqual(self.forte.environment, 'test', 'Must test with test environment.')
|
||||||
|
response = self.forte.forte_test_credentials()
|
||||||
|
|
||||||
|
# Create/Save a Payment Token.
|
||||||
|
# Change Token numbers to real values if you want to try to get an approval.
|
||||||
|
token = self.env['payment.token'].create({
|
||||||
|
'partner_id': self.buyer_id,
|
||||||
|
'acquirer_id': self.forte.id,
|
||||||
|
'acquirer_ref': 'Test Token',
|
||||||
|
'forte_account_type': 'Checking',
|
||||||
|
'forte_routing_number': '021000021',
|
||||||
|
'forte_account_number': '000111222',
|
||||||
|
'forte_account_holder': self.buyer.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create Payment
|
||||||
|
try:
|
||||||
|
payment = self.env['account.payment'].create({
|
||||||
|
'payment_type': 'inbound',
|
||||||
|
'journal_id': self.journal.id,
|
||||||
|
'partner_id': self.buyer_id,
|
||||||
|
'payment_token_id': token.id,
|
||||||
|
'payment_method_id': self.method.id,
|
||||||
|
'amount': 22.00,
|
||||||
|
})
|
||||||
|
self.assertEqual(payment.payment_transaction_id.amount, 22.00)
|
||||||
|
self.assertTrue(payment.payment_transaction_id.acquirer_reference)
|
||||||
|
except ValidationError as e:
|
||||||
|
self.assertTrue(e.name.find('U02') >= 0)
|
||||||
|
|
||||||
33
payment_forte/views/payment_views.xml
Normal file
33
payment_forte/views/payment_views.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="acquirer_form_forte" model="ir.ui.view">
|
||||||
|
<field name="name">acquirer.form.forte</field>
|
||||||
|
<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'>
|
||||||
|
<group attrs="{'invisible': [('provider', '!=', 'forte')]}">
|
||||||
|
<field name="forte_organization_id"/>
|
||||||
|
<field name="forte_location_id"/>
|
||||||
|
<field name="forte_access_id"/>
|
||||||
|
<field name="forte_secure_key" password="True"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="token_form_forte" model="ir.ui.view">
|
||||||
|
<field name='name'>payment.token.form</field>
|
||||||
|
<field name='model'>payment.token</field>
|
||||||
|
<field name="inherit_id" ref="payment.payment_token_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr='//field[@name="acquirer_ref"]' position='after'>
|
||||||
|
<field name="forte_account_type" attrs="{'invisible':[('provider', '!=', 'forte')]}"/>
|
||||||
|
<field name="forte_routing_number" attrs="{'invisible':[('provider', '!=', 'forte')]}"/>
|
||||||
|
<field name="forte_account_number" attrs="{'invisible':[('provider', '!=', 'forte')]}"/>
|
||||||
|
<field name="forte_account_holder" attrs="{'invisible':[('provider', '!=', 'forte')]}"/>
|
||||||
|
<field name="provider" invisible='1'/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user