banking-addons-team team mailing list archive
-
banking-addons-team team
-
Mailing list archive
-
Message #00528
[Merge] lp:~therp-nl/banking-addons/6.1-link_partner_wizard into lp:banking-addons
Stefan Rijnhart (Therp) has proposed merging lp:~therp-nl/banking-addons/6.1-link_partner_wizard into lp:banking-addons.
Requested reviews:
Banking Addons Team (banking-addons-team)
For more details, see:
https://code.launchpad.net/~therp-nl/banking-addons/6.1-link_partner_wizard/+merge/160889
--
https://code.launchpad.net/~therp-nl/banking-addons/6.1-link_partner_wizard/+merge/160889
Your team Banking Addons Team is requested to review the proposed merge of lp:~therp-nl/banking-addons/6.1-link_partner_wizard into lp:banking-addons.
=== modified file 'account_banking/__openerp__.py'
--- account_banking/__openerp__.py 2012-05-07 11:49:08 +0000
+++ account_banking/__openerp__.py 2013-04-25 13:08:15 +0000
@@ -45,6 +45,7 @@
'account_banking_view.xml',
'account_banking_workflow.xml',
'wizard/banking_transaction_wizard.xml',
+ 'wizard/link_partner.xml',
'workflow/account_invoice.xml',
],
'demo_xml': [],
=== modified file 'account_banking/account_banking_view.xml'
--- account_banking/account_banking_view.xml 2013-04-25 08:15:21 +0000
+++ account_banking/account_banking_view.xml 2013-04-25 13:08:15 +0000
@@ -286,6 +286,13 @@
<field name="arch" type="xml">
<data>
<xpath expr="/form/notebook/page/field[@name='line_ids']/tree/field[@name='partner_id']" position="after">
+ <field name="link_partner_ok" invisible="1" />
+ <button name="link_partner"
+ string="Link partner"
+ icon="terp-partner"
+ type="object"
+ attrs="{'invisible': [('link_partner_ok', '=', False)]}"
+ />
<!-- TODO set partner_id when partner_bank_id changes -->
<field name="partner_bank_id"/>
</xpath>
@@ -514,6 +521,13 @@
<field name="name"/>
<field name="ref"/>
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
+ <field name="link_partner_ok" invisible="1" />
+ <button name="link_partner"
+ string="Link partner"
+ icon="terp-partner"
+ type="object"
+ attrs="{'invisible': [('link_partner_ok', '=', False)]}"
+ />
<!-- TODO set partner_id when partner_bank_id changes -->
<field name="partner_bank_id"/>
<field name="type" on_change="onchange_type(partner_id, type)"/>
=== modified file 'account_banking/banking_import_transaction.py'
--- account_banking/banking_import_transaction.py 2013-03-21 13:27:19 +0000
+++ account_banking/banking_import_transaction.py 2013-04-25 13:08:15 +0000
@@ -1134,6 +1134,9 @@
i += 1
continue
+ partner_banks = []
+ partner_ids = []
+
# TODO: optimize by ordering transactions per company,
# and perform the stanza below only once per company.
# In that case, take newest transaction date into account
@@ -1277,6 +1280,7 @@
if transaction.type == bt.STORNO:
move_info = self._match_storno(
cr, uid, transaction, results['log'], context)
+
# Allow inclusion of generated bank invoices
if transaction.type == bt.BANK_COSTS:
lines = self._match_costs(
@@ -1288,7 +1292,6 @@
if not [x for x in move_lines if x.id == line.id]:
move_lines.append(line)
partner_ids = [account_info.bank_partner_id.id]
- partner_banks = []
else:
# Link remote partner, import account when needed
partner_banks = get_bank_accounts(
@@ -1298,44 +1301,29 @@
if partner_banks:
partner_ids = [x.partner_id.id for x in partner_banks]
elif transaction.remote_owner:
- 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
- elif company.partner_id and company.partner_id.country:
- country_code = company.partner_id.country.code
- else:
- country_code = None
- partner_id = get_or_create_partner(
+ country_id = get_country_id(
+ self.pool, cr, uid, transaction, context=context)
+ partner_id = get_partner(
self.pool, cr, uid, transaction.remote_owner,
transaction.remote_owner_address,
transaction.remote_owner_postalcode,
transaction.remote_owner_city,
- country_code, results['log'], context=context)
- if transaction.remote_account:
- partner_bank_id = create_bank_account(
- self.pool, cr, uid, partner_id,
- transaction.remote_account,
- transaction.remote_owner,
- transaction.remote_owner_address,
- transaction.remote_owner_city,
- country_code, results['log'],
- bic=transaction.remote_bank_bic
- )
- partner_banks = partner_bank_obj.browse(
- cr, uid, [partner_bank_id]
- )
- else:
- partner_bank_id = None
- partner_banks = []
- partner_ids = [partner_id]
- else:
- partner_ids = []
- partner_banks = []
+ country_id, results['log'], context=context)
+ if partner_id:
+ partner_ids = [partner_id]
+ if transaction.remote_account:
+ partner_bank_id = create_bank_account(
+ self.pool, cr, uid, partner_id,
+ transaction.remote_account,
+ transaction.remote_owner,
+ transaction.remote_owner_address,
+ transaction.remote_owner_city,
+ country_id, results['log'],
+ bic=transaction.remote_bank_bic
+ )
+ partner_banks = partner_bank_obj.browse(
+ cr, uid, [partner_bank_id]
+ )
# Credit means payment... isn't it?
if (not move_info
@@ -1706,6 +1694,24 @@
class account_bank_statement_line(osv.osv):
_inherit = 'account.bank.statement.line'
+
+ def _get_link_partner_ok(
+ self, cr, uid, ids, name, args, context=None):
+ """
+ Deliver the values of the function field that
+ determines if the 'link partner' wizard is show on the
+ bank statement line
+ """
+ res = {}
+ for line in self.browse(cr, uid, ids, context):
+ res[line.id] = (
+ line.state == 'draft'
+ and not line.partner_id
+ and line.import_transaction_id
+ and line.import_transaction_id.remote_owner
+ and line.import_transaction_id.remote_account)
+ return res
+
_columns = {
'import_transaction_id': fields.many2one(
'banking.import.transaction',
@@ -1730,6 +1736,9 @@
'state': fields.selection(
[('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
readonly=True, required=True),
+ 'link_partner_ok': fields.function(
+ _get_link_partner_ok, type='boolean',
+ string='Can link partner'),
}
_defaults = {
@@ -1750,6 +1759,61 @@
res = wizard_obj.create_act_window(cr, uid, res_id, context=context)
return res
+ def link_partner(self, cr, uid, ids, context=None):
+ """
+ Get the appropriate partner or fire a wizard to create
+ or link one
+ """
+ res = False
+ if ids:
+ if isinstance(ids, (int, float)):
+ ids = [ids]
+
+ # Check if the partner is already known but not shown
+ # because the screen was not refreshed yet
+ statement_line = self.browse(
+ cr, uid, ids[0], context=context)
+ if statement_line.partner_id:
+ return True
+
+ # Check if the bank account was already been linked
+ # manually to another transaction
+ remote_account = statement_line.import_transaction_id.remote_account
+ source_line_ids = self.search(
+ cr, uid,
+ [('import_transaction_id.remote_account', '=', remote_account),
+ ('partner_bank_id.partner_id', '!=', False),
+ ], limit=1, context=context)
+ if source_line_ids:
+ source_line = self.browse(
+ cr, uid, source_line_ids[0], context=context)
+ target_line_ids = statement_line_obj.search(
+ cr, uid,
+ [('import_transaction_id.remote_account', '=', remote_account),
+ ('partner_bank_id', '=', False),
+ ('state', '=', 'draft')], context=context)
+ self.write(
+ cr, uid, statement_line_ids,
+ {'partner_bank_id': source_line.partner_bank_id.id,
+ 'partner_id': source_line.partner_bank_id.partner_id.id,
+ }, context=context)
+ return True
+
+ # Reuse the bank's partner if any
+ if (statement_line.partner_bank_id and
+ statement_line.partner_bank_id.partner_id):
+ statement_line.write(
+ {'partner_id': statement_line.partner_bank_id.partner_id.id})
+ return True
+
+ # Or fire the wizard to link partner and account
+ wizard_obj = self.pool.get('banking.link_partner')
+ res_id = wizard_obj.create(
+ cr, uid, {'statement_line_id': ids[0]}, context=context)
+ res = wizard_obj.create_act_window(cr, uid, res_id, context=context)
+
+ return res
+
def _convert_currency(
self, cr, uid, from_curr_id, to_curr_id, from_amount,
round=False, date=None, context=None):
=== modified file 'account_banking/wizard/__init__.py'
--- account_banking/wizard/__init__.py 2011-12-19 12:03:32 +0000
+++ account_banking/wizard/__init__.py 2013-04-25 13:08:15 +0000
@@ -22,5 +22,6 @@
import bank_payment_manual
import account_payment_order
import banking_transaction_wizard
+import link_partner
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== modified file 'account_banking/wizard/banktools.py'
--- account_banking/wizard/banktools.py 2013-02-07 09:38:41 +0000
+++ account_banking/wizard/banktools.py 2013-04-25 13:08:15 +0000
@@ -28,7 +28,8 @@
__all__ = [
'get_period',
'get_bank_accounts',
- 'get_or_create_partner',
+ 'get_partner',
+ 'get_country_id',
'get_company_bank_account',
'create_bank_account',
]
@@ -89,7 +90,7 @@
'''
# No need to search for nothing
if not account_number:
- return False
+ return []
partner_bank_obj = pool.get('res.partner.bank')
bank_account_ids = partner_bank_obj.search(cursor, uid, [
@@ -107,7 +108,7 @@
_('Bank account %(account_no)s was not found in the database')
% dict(account_no=account_number)
)
- return False
+ return []
return partner_bank_obj.browse(cursor, uid, bank_account_ids)
def _has_attr(obj, attr):
@@ -118,10 +119,10 @@
except KeyError:
return False
-def get_or_create_partner(pool, cr, uid, name, address, postal_code, city,
- country_code, log, context=None):
+def get_partner(pool, cr, uid, name, address, postal_code, city,
+ country_id, log, context=None):
'''
- Get or create the partner belonging to the account holders name <name>
+ 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.
@@ -131,16 +132,10 @@
partner_obj = pool.get('res.partner')
partner_ids = partner_obj.search(cr, uid, [('name', 'ilike', name)],
context=context)
- country_id = False
if not partner_ids:
# Try brute search on address and then match reverse
criteria = []
- if country_code:
- country_obj = pool.get('res.country')
- country_ids = country_obj.search(
- cr, uid, [('code', '=', country_code.upper())],
- context=context)
- country_id = country_ids and country_ids[0] or False
+ if country_id:
criteria.append(('address.country_id', '=', country_id))
if city:
criteria.append(('address.city', 'ilike', city))
@@ -148,6 +143,11 @@
criteria.append(('address.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] = ('address.country_id', '=', False)
+ partner_search_ids = partner_obj.search(
+ cr, uid, criteria, context=context)
key = name.lower()
partners = []
for partner in partner_obj.read(
@@ -156,31 +156,11 @@
partners.append(partner)
partners.sort(key=lambda x: len(x['name']), reverse=True)
partner_ids = [x['id'] for x in partners]
- if not partner_ids:
- if not country_id:
- user = pool.get('res.user').browse(cr, uid, uid, context=context)
- country_id = (
- user.company_id.partner_id.country and
- user.company_id.partner_id.country.id or
- False
- )
- partner_id = partner_obj.create(cr, uid, dict(
- name=name, active=True,
- comment='Generated from Bank Statements Import',
- address=[(0,0,{
- 'street': address and address[0] or '',
- 'street2': len(address) > 1 and address[1] or '',
- 'city': city,
- 'zip': postal_code or '',
- 'country_id': country_id,
- })]), context=context)
- else:
- if len(partner_ids) > 1:
- log.append(
- _('More than one possible match found for partner with '
- 'name %(name)s') % {'name': name})
- partner_id = partner_ids[0]
- return partner_id
+ 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, cursor, uid, account_number, currency,
company, log):
@@ -311,19 +291,47 @@
))
return bank_id, country_id
-def create_bank_account(pool, cursor, uid, partner_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_code, log, bic=False,
- ):
+ 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,
)
bankcode = None
- country_obj = pool.get('res.country')
# Are we dealing with IBAN?
iban = sepa.IBAN(account_number)
@@ -333,44 +341,29 @@
values.acc_number = str(iban)
values.acc_number_domestic = iban.BBAN
bankcode = iban.bankcode + iban.countrycode
- country_code = iban.countrycode
-
- if not country_code:
- country = pool.get('res.partner').browse(
- cursor, uid, partner_id).country
- country_code = country.code
- country_id = country.id
else:
- if iban.valid:
- country_ids = country_obj.search(cursor, uid,
- [('code', '=', iban.countrycode)]
- )
- else:
- country_ids = country_obj.search(cursor, uid,
- [('code', '=', country_code)]
- )
- country_id = country_ids[0]
-
- account_info = False
- if not iban.valid:
# No, try to convert to IBAN
values.state = 'bank'
values.acc_number = values.acc_number_domestic = account_number
- if country_code in sepa.IBAN.countries:
- account_info = sepa.online.account_info(country_code,
- values.acc_number
- )
- if account_info:
- values.acc_number = iban = account_info.iban
- values.state = 'iban'
- bankcode = account_info.code
- bic = account_info.bic
+
+ 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 = sepa.online.account_info(
+ country_code, values.acc_number)
+ if account_info:
+ values.acc_number = iban = account_info.iban
+ values.state = 'iban'
+ bankcode = account_info.code
+ bic = account_info.bic
if bic:
- values.bank = get_or_create_bank(pool, cursor, uid, bic)[0]
+ 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(cursor, uid, values)
+ return pool.get('res.partner.bank').create(
+ cr, uid, values, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== added file 'account_banking/wizard/link_partner.py'
--- account_banking/wizard/link_partner.py 1970-01-01 00:00:00 +0000
+++ account_banking/wizard/link_partner.py 2013-04-25 13:08:15 +0000
@@ -0,0 +1,152 @@
+# -*- 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 import sepa
+from openerp.addons.account_banking.wizard import banktools
+
+class link_partner(orm.Model):
+ _name = 'banking.link_partner'
+ _inherit = 'res.partner.address'
+ _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'),
+ 'statement_line_id': fields.many2one(
+ 'account.bank.statement.line',
+ 'Statement line', required=True),
+ 'remote_account': fields.char(
+ 'Account number', size=24, readonly=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_owner
+ 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
+
+ if not vals.get('street'):
+ vals['street'] = transaction.remote_owner_address
+ if not vals.get('street'):
+ 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.get('remote_account'):
+ vals['remote_account'] = transaction.remote_account
+
+ return super(link_partner, self).create(
+ cr, uid, vals, context=context)
+
+ 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:
+ partner_id = self.pool.get('res.partner').create(
+ cr, uid, {
+ 'name': wiz.name,
+ 'customer': wiz.customer,
+ 'supplier': wiz.supplier,
+ }, context=context)
+ address_vals = {'type': 'default'}
+ wiz_read = self.read(
+ cr, uid, ids[0], context=context, load='_classic_write')
+ address_fields = self.pool.get(
+ 'res.partner.address')._columns.keys()
+ for field in self._columns.keys():
+ if field in address_fields:
+ address_vals[field] = wiz_read[field]
+ self.pool.get('res.partner.address').create(
+ cr, uid, address_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
+ )
+
+ 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,
+ }
+
+
=== added file 'account_banking/wizard/link_partner.xml'
--- account_banking/wizard/link_partner.xml 1970-01-01 00:00:00 +0000
+++ account_banking/wizard/link_partner.xml 2013-04-25 13:08:15 +0000
@@ -0,0 +1,47 @@
+<?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">
+ <group colspan="4" col="6">
+ <field name="name"
+ attrs="{'readonly': [('partner_id', '!=', False)]}" />
+ <field name="partner_id"/>
+ <field name="remote_account" />
+ </group>
+ <group colspan="4"
+ string="Address"
+ attrs="{'invisible': [('partner_id', '!=', False)]}">
+ <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>
+ <button icon="gtk-cancel" string="Cancel" special="cancel" />
+ <button icon="gtk-ok" string="Create partner"
+ name="link_partner" type="object"
+ attrs="{'invisible': [('partner_id', '!=', False)]}"
+ />
+ <button icon="gtk-ok" string="Link existing partner"
+ name="link_partner" type="object"
+ attrs="{'invisible': [('partner_id', '==', False)]}"
+ />
+ </form>
+ </field>
+ </record>
+ </data>
+</openerp>
Follow ups