mirror of
https://github.com/OCA/bank-statement-import.git
synced 2025-01-20 12:37:43 +02:00
[FIX] Add back __unported__ to prevent deletions after PR.
This commit is contained in:
24
__unported__/account_banking/wizard/__init__.py
Normal file
24
__unported__/account_banking/wizard/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from . import bank_import
|
||||
from . import banking_transaction_wizard
|
||||
from . import link_partner
|
||||
338
__unported__/account_banking/wizard/banktools.py
Normal file
338
__unported__/account_banking/wizard/banktools.py
Normal file
@@ -0,0 +1,338 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.account_banking import sepa
|
||||
from openerp.addons.account_banking.struct import struct
|
||||
|
||||
__all__ = [
|
||||
'get_period',
|
||||
'get_bank_accounts',
|
||||
'get_partner',
|
||||
'get_country_id',
|
||||
'get_company_bank_account',
|
||||
'create_bank_account',
|
||||
]
|
||||
|
||||
|
||||
def get_period(pool, cr, uid, date, company, log=None):
|
||||
'''
|
||||
Wrapper over account_period.find() to log exceptions of
|
||||
missing periods instead of raising.
|
||||
'''
|
||||
context = {'account_period_prefer_normal': True}
|
||||
if company:
|
||||
context['company_id'] = company.id
|
||||
try:
|
||||
period_ids = pool.get('account.period').find(
|
||||
cr, uid, dt=date, context=context)
|
||||
except Exception as e:
|
||||
if log is None:
|
||||
raise
|
||||
else:
|
||||
log.append(e)
|
||||
return False
|
||||
return period_ids[0]
|
||||
|
||||
|
||||
def get_bank_accounts(pool, cr, uid, account_number, log, fail=False):
|
||||
'''
|
||||
Get the bank account with account number account_number
|
||||
'''
|
||||
# No need to search for nothing
|
||||
if not account_number:
|
||||
return []
|
||||
|
||||
partner_bank_obj = pool.get('res.partner.bank')
|
||||
bank_account_ids = partner_bank_obj.search(cr, uid, [
|
||||
('acc_number', '=', account_number)
|
||||
])
|
||||
if not bank_account_ids:
|
||||
if not fail:
|
||||
log.append(
|
||||
_('Bank account %(account_no)s was not found in the database')
|
||||
% dict(account_no=account_number)
|
||||
)
|
||||
return []
|
||||
return partner_bank_obj.browse(cr, uid, bank_account_ids)
|
||||
|
||||
|
||||
def _has_attr(obj, attr):
|
||||
# Needed for dangling addresses and a weird exception scheme in
|
||||
# OpenERP's orm.
|
||||
try:
|
||||
return bool(getattr(obj, attr))
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
|
||||
def get_partner(pool, cr, uid, name, address, postal_code, city,
|
||||
country_id, log, context=None):
|
||||
'''
|
||||
Get the partner belonging to the account holders name <name>
|
||||
|
||||
If multiple partners are found with the same name, select the first and
|
||||
add a warning to the import log.
|
||||
|
||||
TODO: revive the search by lines from the address argument
|
||||
'''
|
||||
partner_obj = pool.get('res.partner')
|
||||
partner_ids = partner_obj.search(
|
||||
cr, uid, [
|
||||
'|', ('is_company', '=', True), ('parent_id', '=', False),
|
||||
('name', 'ilike', name),
|
||||
], context=context)
|
||||
if not partner_ids:
|
||||
# Try brute search on address and then match reverse
|
||||
criteria = []
|
||||
if country_id:
|
||||
criteria.append(('country_id', '=', country_id))
|
||||
if city:
|
||||
criteria.append(('city', 'ilike', city))
|
||||
if postal_code:
|
||||
criteria.append(('zip', 'ilike', postal_code))
|
||||
partner_search_ids = partner_obj.search(
|
||||
cr, uid, criteria, context=context)
|
||||
if (not partner_search_ids and country_id):
|
||||
# Try again with country_id = False
|
||||
criteria[0] = ('country_id', '=', False)
|
||||
partner_search_ids = partner_obj.search(
|
||||
cr, uid, criteria, context=context)
|
||||
key = name.lower()
|
||||
partners = []
|
||||
for partner in partner_obj.read(
|
||||
cr, uid, partner_search_ids, ['name', 'commercial_partner_id'],
|
||||
context=context):
|
||||
if (len(partner['name']) > 3 and partner['name'].lower() in key):
|
||||
partners.append(partner)
|
||||
partners.sort(key=lambda x: len(x['name']), reverse=True)
|
||||
partner_ids = [x['commercial_partner_id'][0] for x in partners]
|
||||
if len(partner_ids) > 1:
|
||||
log.append(
|
||||
_('More than one possible match found for partner with '
|
||||
'name %(name)s') % {'name': name})
|
||||
return partner_ids and partner_ids[0] or False
|
||||
|
||||
|
||||
def get_company_bank_account(pool, cr, uid, account_number, currency,
|
||||
company, log):
|
||||
'''
|
||||
Get the matching bank account for this company. Currency is the ISO code
|
||||
for the requested currency.
|
||||
'''
|
||||
results = struct()
|
||||
bank_accounts = get_bank_accounts(pool, cr, uid, account_number, log,
|
||||
fail=True)
|
||||
if not bank_accounts:
|
||||
return False
|
||||
elif len(bank_accounts) != 1:
|
||||
log.append(
|
||||
_('More than one bank account was found with the same number '
|
||||
'%(account_no)s') % dict(account_no=account_number)
|
||||
)
|
||||
return False
|
||||
if bank_accounts[0].partner_id.id != company.partner_id.id:
|
||||
log.append(
|
||||
_('Account %(account_no)s is not owned by %(partner)s')
|
||||
% dict(account_no=account_number,
|
||||
partner=company.partner_id.name,
|
||||
))
|
||||
return False
|
||||
results.account = bank_accounts[0]
|
||||
bank_settings_obj = pool.get('account.banking.account.settings')
|
||||
criteria = [('partner_bank_id', '=', bank_accounts[0].id)]
|
||||
|
||||
# Find matching journal for currency
|
||||
journal_obj = pool.get('account.journal')
|
||||
journal_ids = journal_obj.search(cr, uid, [
|
||||
('type', '=', 'bank'),
|
||||
('currency.name', '=', currency or company.currency_id.name)
|
||||
])
|
||||
if currency == company.currency_id.name:
|
||||
journal_ids_no_curr = journal_obj.search(cr, uid, [
|
||||
('type', '=', 'bank'), ('currency', '=', False)
|
||||
])
|
||||
journal_ids.extend(journal_ids_no_curr)
|
||||
if journal_ids:
|
||||
criteria.append(('journal_id', 'in', journal_ids))
|
||||
|
||||
# Find bank account settings
|
||||
bank_settings_ids = bank_settings_obj.search(cr, uid, criteria)
|
||||
if bank_settings_ids:
|
||||
settings = bank_settings_obj.browse(cr, uid, bank_settings_ids)[0]
|
||||
results.company_id = company
|
||||
results.journal_id = settings.journal_id
|
||||
|
||||
# Take currency from settings or from company
|
||||
if settings.journal_id.currency.id:
|
||||
results.currency_id = settings.journal_id.currency
|
||||
else:
|
||||
results.currency_id = company.currency_id
|
||||
# Rest just copy/paste from settings. Why am I doing this?
|
||||
results.default_debit_account_id = settings.default_debit_account_id
|
||||
results.default_credit_account_id = settings.default_credit_account_id
|
||||
results.costs_account_id = settings.costs_account_id
|
||||
results.invoice_journal_id = settings.invoice_journal_id
|
||||
results.bank_partner_id = settings.bank_partner_id
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def get_or_create_bank(pool, cr, uid, bic, online=False, code=None,
|
||||
name=None, context=None):
|
||||
'''
|
||||
Find or create the bank with the provided BIC code.
|
||||
When online, the SWIFT database will be consulted in order to
|
||||
provide for missing information.
|
||||
'''
|
||||
# UPDATE: Free SWIFT databases are since 2/22/2010 hidden behind an
|
||||
# image challenge/response interface.
|
||||
|
||||
bank_obj = pool.get('res.bank')
|
||||
|
||||
# Self generated key?
|
||||
if len(bic) < 8:
|
||||
# search key
|
||||
bank_ids = bank_obj.search(
|
||||
cr, uid, [
|
||||
('bic', '=', bic[:6])
|
||||
])
|
||||
if not bank_ids:
|
||||
bank_ids = bank_obj.search(
|
||||
cr, uid, [
|
||||
('bic', 'ilike', bic + '%')
|
||||
])
|
||||
else:
|
||||
bank_ids = bank_obj.search(
|
||||
cr, uid, [
|
||||
('bic', '=', bic)
|
||||
])
|
||||
|
||||
if bank_ids and len(bank_ids) == 1:
|
||||
banks = bank_obj.browse(cr, uid, bank_ids)
|
||||
return banks[0].id, banks[0].country.id
|
||||
|
||||
country_obj = pool.get('res.country')
|
||||
country_ids = country_obj.search(
|
||||
cr, uid, [('code', '=', bic[4:6])]
|
||||
)
|
||||
country_id = country_ids and country_ids[0] or False
|
||||
bank_id = False
|
||||
|
||||
if online:
|
||||
info, address = bank_obj.online_bank_info(
|
||||
cr, uid, bic, context=context
|
||||
)
|
||||
if info:
|
||||
bank_id = bank_obj.create(cr, uid, dict(
|
||||
code=info.code,
|
||||
name=info.name,
|
||||
street=address.street,
|
||||
street2=address.street2,
|
||||
zip=address.zip,
|
||||
city=address.city,
|
||||
country=country_id,
|
||||
bic=info.bic[:8],
|
||||
))
|
||||
else:
|
||||
info = struct(name=name, code=code)
|
||||
|
||||
if not online or not bank_id:
|
||||
bank_id = bank_obj.create(cr, uid, dict(
|
||||
code=info.code or 'UNKNOW', # FIXME: Typo?
|
||||
name=info.name or _('Unknown Bank'),
|
||||
country=country_id,
|
||||
bic=bic,
|
||||
))
|
||||
return bank_id, country_id
|
||||
|
||||
|
||||
def get_country_id(pool, cr, uid, transaction, context=None):
|
||||
"""
|
||||
Derive a country id from the info on the transaction.
|
||||
|
||||
:param transaction: browse record of a transaction
|
||||
:returns: res.country id or False
|
||||
"""
|
||||
|
||||
country_code = False
|
||||
iban = sepa.IBAN(transaction.remote_account)
|
||||
if iban.valid:
|
||||
country_code = iban.countrycode
|
||||
elif transaction.remote_owner_country_code:
|
||||
country_code = transaction.remote_owner_country_code
|
||||
# fallback on the import parsers country code
|
||||
elif transaction.bank_country_code:
|
||||
country_code = transaction.bank_country_code
|
||||
if country_code:
|
||||
country_ids = pool.get('res.country').search(
|
||||
cr, uid, [('code', '=', country_code.upper())],
|
||||
context=context)
|
||||
country_id = country_ids and country_ids[0] or False
|
||||
if not country_id:
|
||||
company = transaction.statement_line_id.company_id
|
||||
if company.partner_id.country:
|
||||
country_id = company.partner_id.country.id
|
||||
return country_id
|
||||
|
||||
|
||||
def create_bank_account(pool, cr, uid, partner_id,
|
||||
account_number, holder_name, address, city,
|
||||
country_id, bic=False,
|
||||
context=None):
|
||||
'''
|
||||
Create a matching bank account with this holder for this partner.
|
||||
'''
|
||||
values = struct(
|
||||
partner_id=partner_id,
|
||||
owner_name=holder_name,
|
||||
country_id=country_id,
|
||||
)
|
||||
|
||||
# Are we dealing with IBAN?
|
||||
iban = sepa.IBAN(account_number)
|
||||
if iban.valid:
|
||||
# Take as much info as possible from IBAN
|
||||
values.state = 'iban'
|
||||
values.acc_number = str(iban)
|
||||
else:
|
||||
# No, try to convert to IBAN
|
||||
values.state = 'bank'
|
||||
values.acc_number = account_number
|
||||
|
||||
if country_id:
|
||||
country_code = pool.get('res.country').read(
|
||||
cr, uid, country_id, ['code'], context=context)['code']
|
||||
if country_code in sepa.IBAN.countries:
|
||||
account_info = pool['res.partner.bank'].online_account_info(
|
||||
cr, uid, country_code, values.acc_number, context=context)
|
||||
if account_info:
|
||||
values.acc_number = iban = account_info.iban
|
||||
values.state = 'iban'
|
||||
bic = account_info.bic
|
||||
|
||||
if bic:
|
||||
values.bank = get_or_create_bank(pool, cr, uid, bic)[0]
|
||||
values.bank_bic = bic
|
||||
|
||||
# Create bank account and return
|
||||
return pool.get('res.partner.bank').create(
|
||||
cr, uid, values, context=context)
|
||||
208
__unported__/account_banking/wizard/link_partner.py
Normal file
208
__unported__/account_banking/wizard/link_partner.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2013 Therp BV (<http://therp.nl>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.account_banking.wizard import banktools
|
||||
import ast
|
||||
|
||||
|
||||
class link_partner(orm.TransientModel):
|
||||
_name = 'banking.link_partner'
|
||||
_description = 'Link partner'
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(
|
||||
'Create partner with name', size=128, required=True),
|
||||
'supplier': fields.boolean('Supplier'),
|
||||
'customer': fields.boolean('Customer'),
|
||||
'partner_id': fields.many2one(
|
||||
'res.partner', 'or link existing partner',
|
||||
domain=['|', ('is_company', '=', True),
|
||||
('parent_id', '=', False)],
|
||||
),
|
||||
'statement_line_id': fields.many2one(
|
||||
'account.bank.statement.line',
|
||||
'Statement line', required=True),
|
||||
'amount': fields.related(
|
||||
'statement_line_id', 'amount', type='float',
|
||||
string="Amount", readonly=True),
|
||||
'ref': fields.related(
|
||||
'statement_line_id', 'ref', type='char', size=32,
|
||||
string="Reference", readonly=True),
|
||||
'message': fields.related(
|
||||
'statement_line_id', 'import_transaction_id', 'message',
|
||||
type='char', size=1024,
|
||||
string="Message", readonly=True),
|
||||
'remote_account': fields.char(
|
||||
'Account number', size=24, readonly=True),
|
||||
# Partner values
|
||||
'street': fields.char('Street', size=128),
|
||||
'street2': fields.char('Street2', size=128),
|
||||
'zip': fields.char('Zip', change_default=True, size=24),
|
||||
'city': fields.char('City', size=128),
|
||||
'state_id': fields.many2one("res.country.state", 'State'),
|
||||
'country_id': fields.many2one('res.country', 'Country'),
|
||||
'email': fields.char('Email', size=240),
|
||||
'phone': fields.char('Phone', size=64),
|
||||
'fax': fields.char('Fax', size=64),
|
||||
'mobile': fields.char('Mobile', size=64),
|
||||
'is_company': fields.boolean('Is a Company'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'is_company': True,
|
||||
}
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
"""
|
||||
Get default values from the transaction data
|
||||
on the statement line
|
||||
"""
|
||||
if vals and vals.get('statement_line_id'):
|
||||
statement_line_obj = self.pool.get('account.bank.statement.line')
|
||||
statement_line = statement_line_obj.browse(
|
||||
cr, uid, vals['statement_line_id'], context=context)
|
||||
transaction = statement_line.import_transaction_id
|
||||
|
||||
if statement_line.partner_bank_id:
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('Statement line is already linked to a bank account '))
|
||||
|
||||
if not(transaction
|
||||
and transaction.remote_account):
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('No transaction data on statement line'))
|
||||
|
||||
if 'supplier' not in vals and statement_line.amount < 0:
|
||||
vals['supplier'] = True
|
||||
if 'customer' not in vals and statement_line.amount > 0:
|
||||
vals['customer'] = True
|
||||
|
||||
address_list = []
|
||||
try:
|
||||
address_list = ast.literal_eval(
|
||||
transaction.remote_owner_address or [])
|
||||
except ValueError:
|
||||
pass
|
||||
if address_list and not vals.get('street'):
|
||||
vals['street'] = address_list.pop(0)
|
||||
if address_list and not vals.get('street2'):
|
||||
vals['street2'] = address_list.pop(0)
|
||||
if transaction.remote_owner_postalcode and not vals.get('zip'):
|
||||
vals['zip'] = transaction.remote_owner_postalcode
|
||||
if transaction.remote_owner_city and not vals.get('city'):
|
||||
vals['city'] = transaction.remote_owner_city
|
||||
if not vals.get('country_id'):
|
||||
vals['country_id'] = banktools.get_country_id(
|
||||
self.pool, cr, uid, transaction, context=context)
|
||||
if not vals.get('name'):
|
||||
vals['name'] = transaction.remote_owner
|
||||
if not vals['name']:
|
||||
vals['name'] = '/'
|
||||
if not vals.get('remote_account'):
|
||||
vals['remote_account'] = transaction.remote_account
|
||||
|
||||
return super(link_partner, self).create(
|
||||
cr, uid, vals, context=context)
|
||||
|
||||
def update_partner_values(self, cr, uid, wizard, values, context=None):
|
||||
"""
|
||||
Updates the new partner values with the values from the wizard
|
||||
|
||||
:param wizard: read record of wizard (with load='_classic_write')
|
||||
:param values: the dictionary of partner values that will be updated
|
||||
"""
|
||||
for field in ['is_company',
|
||||
'name',
|
||||
'street',
|
||||
'street2',
|
||||
'zip',
|
||||
'city',
|
||||
'country_id',
|
||||
'state_id',
|
||||
'phone',
|
||||
'fax',
|
||||
'mobile',
|
||||
'email'
|
||||
]:
|
||||
if wizard[field]:
|
||||
values[field] = wizard[field]
|
||||
return True
|
||||
|
||||
def link_partner(self, cr, uid, ids, context=None):
|
||||
statement_line_obj = self.pool.get(
|
||||
'account.bank.statement.line')
|
||||
wiz = self.browse(cr, uid, ids[0], context=context)
|
||||
|
||||
if wiz.partner_id:
|
||||
partner_id = wiz.partner_id.id
|
||||
else:
|
||||
wiz_read = self.read(
|
||||
cr, uid, ids[0], context=context, load='_classic_write')
|
||||
partner_vals = {
|
||||
'type': 'default',
|
||||
}
|
||||
self.update_partner_values(
|
||||
cr, uid, wiz_read, partner_vals, context=context)
|
||||
partner_id = self.pool.get('res.partner').create(
|
||||
cr, uid, partner_vals, context=context)
|
||||
|
||||
partner_bank_id = banktools.create_bank_account(
|
||||
self.pool, cr, uid, partner_id,
|
||||
wiz.remote_account, wiz.name,
|
||||
wiz.street, wiz.city,
|
||||
wiz.country_id and wiz.country_id.id or False,
|
||||
bic=wiz.statement_line_id.import_transaction_id.remote_bank_bic,
|
||||
context=context)
|
||||
|
||||
statement_line_ids = statement_line_obj.search(
|
||||
cr, uid,
|
||||
[('import_transaction_id.remote_account', '=', wiz.remote_account),
|
||||
('partner_bank_id', '=', False),
|
||||
('state', '=', 'draft')], context=context)
|
||||
statement_line_obj.write(
|
||||
cr, uid, statement_line_ids,
|
||||
{'partner_bank_id': partner_bank_id,
|
||||
'partner_id': partner_id}, context=context)
|
||||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def create_act_window(self, cr, uid, ids, nodestroy=True, context=None):
|
||||
"""
|
||||
Return a popup window for this model
|
||||
"""
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
return {
|
||||
'name': self._description,
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': self._name,
|
||||
'domain': [],
|
||||
'context': context,
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'res_id': ids[0],
|
||||
'nodestroy': nodestroy,
|
||||
}
|
||||
61
__unported__/account_banking/wizard/link_partner.xml
Normal file
61
__unported__/account_banking/wizard/link_partner.xml
Normal file
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="link_partner_view">
|
||||
<field name="name">Link partner wizard view</field>
|
||||
<field name="type">form</field>
|
||||
<field name="model">banking.link_partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Link partner" version="7.0" >
|
||||
<group colspan="4" col="6" string="Transaction data">
|
||||
<field name="ref" />
|
||||
<field name="amount" />
|
||||
<field name="remote_account" />
|
||||
<field name="message" colspan="6"/>
|
||||
</group>
|
||||
<group colspan="4" col="4" string="Create or link partner">
|
||||
<field name="name"
|
||||
attrs="{'readonly': [('partner_id', '!=', False)]}" />
|
||||
<field name="partner_id"/>
|
||||
</group>
|
||||
<group colspan="2">
|
||||
<field name="is_company" />
|
||||
</group>
|
||||
<group colspan="4"
|
||||
string="Address"
|
||||
attrs="{'invisible': [('partner_id', '!=', False)]}"
|
||||
col="4">
|
||||
<group colspan="2" col="2">
|
||||
<field name="street"/>
|
||||
<field name="street2"/>
|
||||
<field name="zip"/>
|
||||
<field name="city"/>
|
||||
<field name="country_id"/>
|
||||
<field name="state_id"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<field name="phone"/>
|
||||
<field name="fax"/>
|
||||
<field name="mobile"/>
|
||||
<field name="email" widget="email"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Create partner"
|
||||
name="link_partner" type="object"
|
||||
class="oe_highlight"
|
||||
attrs="{'invisible': [('partner_id', '!=', False)]}"
|
||||
/>
|
||||
<button string="Link existing partner"
|
||||
class="oe_highlight"
|
||||
name="link_partner" type="object"
|
||||
attrs="{'invisible': [('partner_id', '==', False)]}"
|
||||
/>
|
||||
or
|
||||
<button class="oe_link" string="Cancel" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user