banking-addons-team team mailing list archive
-
banking-addons-team team
-
Mailing list archive
-
Message #00521
lp:~camptocamp-reviewer/banking-addons/fix-type-and-account-in-bk-st-line into lp:banking-addons/bank-statement-reconcile-70
Nicolas Bessi - Camptocamp has proposed merging lp:~camptocamp-reviewer/banking-addons/fix-type-and-account-in-bk-st-line into lp:banking-addons/bank-statement-reconcile-70.
Requested reviews:
Nicolas Bessi - Camptocamp (nbessi-c2c)
Vincent Renaville@camptocamp (vrenaville-c2c)
For more details, see:
https://code.launchpad.net/~camptocamp-reviewer/banking-addons/fix-type-and-account-in-bk-st-line/+merge/160591
Major refactoring of statement import and completion:
Fix the way to look default account on partner:
- If master account is provided in profile it will be forced
- If the customer checkbox is checked on the found partner, type and account will be customer and receivable
- If the supplier checkbox is checked on the found partner, type and account will be supplier and payable
- If both checkbox are checked or none of them, it'll be based on the amount :
If amount is positif the type and account will be customer and receivable,
If amount is negativ, the type and account will be supplier and payable
Completion:
-Fix and refactor the the invoices lookup for completion
-Various fixes in completion rules
- Non matches lines are not mark as completed.
Optimisation:
Refactoring of statement import:
We by pass ORM to increase performances.
TODO support of sparse field
Refactoring of completion.
We have done some structural changes in order to avoid a lot of un needed call to ORM.
Bypass orm when writing to database.
These merge is required in order to fix transaction id completion rules and import +
Handle the new semantic change in OpenERP partner model
--
https://code.launchpad.net/~camptocamp-reviewer/banking-addons/fix-type-and-account-in-bk-st-line/+merge/160591
Your team Banking Addons Team is subscribed to branch lp:banking-addons/bank-statement-reconcile-70.
=== modified file 'account_statement_base_completion/__openerp__.py'
--- account_statement_base_completion/__openerp__.py 2013-01-10 15:09:55 +0000
+++ account_statement_base_completion/__openerp__.py 2013-04-24 10:20:36 +0000
@@ -52,6 +52,10 @@
You can use it with our account_advanced_reconcile module to automatize the reconciliation process.
+
+ TODO: The rules that look for invoices to find out the partner should take back the payable / receivable
+ account from there directly instead of retrieving it from partner properties !
+
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
=== modified file 'account_statement_base_completion/statement.py'
--- account_statement_base_completion/statement.py 2013-03-22 08:31:27 +0000
+++ account_statement_base_completion/statement.py 2013-04-24 10:20:36 +0000
@@ -18,15 +18,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
+# TODO replace customer supplier by package constant
+import traceback
+import sys
+import logging
+
from collections import defaultdict
import re
-
from tools.translate import _
-from openerp.osv.orm import Model, fields
+from openerp.osv import osv, orm, fields
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from operator import attrgetter
import datetime
+_logger = logging.getLogger(__name__)
class ErrorTooManyPartner(Exception):
"""
@@ -40,7 +45,7 @@
return repr(self.value)
-class AccountStatementProfil(Model):
+class AccountStatementProfil(orm.Model):
"""
Extend the class to add rules per profile that will match at least the partner,
but it could also be used to match other values as well.
@@ -61,12 +66,21 @@
rel='as_rul_st_prof_rel'),
}
- def find_values_from_rules(self, cr, uid, id, line_id, context=None):
+ def _get_callable(self, cr, uid, profile, context=None):
+ if isinstance(profile, (int, long)):
+ prof = self.browse(cr, uid, profile, context=context)
+ else:
+ prof = profile
+ # We need to respect the sequence order
+ sorted_array = sorted(prof.rule_ids, key=attrgetter('sequence'))
+ return tuple((x.function_to_call for x in sorted_array))
+
+ def _find_values_from_rules(self, cr, uid, calls, line, context=None):
"""
This method will execute all related rules, in their sequence order,
to retrieve all the values returned by the first rules that will match.
- :param int/long line_id: id of the concerned account.bank.statement.line
+ :param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
@@ -77,20 +91,20 @@
"""
if context is None:
context = {}
- res = {}
+ if not calls:
+ calls = self._get_callable(cr, uid, id, context=context)
rule_obj = self.pool.get('account.statement.completion.rule')
- profile = self.browse(cr, uid, id, context=context)
- # We need to respect the sequence order
- sorted_array = sorted(profile.rule_ids, key=attrgetter('sequence'))
- for rule in sorted_array:
- method_to_call = getattr(rule_obj, rule.function_to_call)
- result = method_to_call(cr, uid, line_id, context)
+
+ for call in calls:
+ method_to_call = getattr(rule_obj, call)
+ result = method_to_call(cr, uid, line, context)
if result:
+ result['already_completed'] = True
return result
- return res
-
-
-class AccountStatementCompletionRule(Model):
+ return None
+
+
+class AccountStatementCompletionRule(orm.Model):
"""
This will represent all the completion method that we can have to
fullfill the bank statement lines. You'll be able to extend them in you own module
@@ -109,12 +123,11 @@
List of available methods for rules. Override this to add you own.
"""
return [
- ('get_from_ref_and_invoice', 'From line reference (based on invoice number)'),
+ ('get_from_ref_and_invoice', 'From line reference (based on customer invoice number)'),
('get_from_ref_and_supplier_invoice', 'From line reference (based on supplier invoice number)'),
('get_from_ref_and_so', 'From line reference (based on SO number)'),
('get_from_label_and_partner_field', 'From line label (based on partner field)'),
- ('get_from_label_and_partner_name', 'From line label (based on partner name)'),
- ]
+ ('get_from_label_and_partner_name', 'From line label (based on partner name)')]
_columns = {
'sequence': fields.integer('Sequence', help="Lower means parsed first."),
@@ -126,13 +139,56 @@
'function_to_call': fields.selection(_get_functions, 'Method'),
}
- def get_from_ref_and_supplier_invoice(self, cr, uid, line_id, context=None):
+ def _find_invoice(self, cr, uid, st_line, inv_type, context=None):
+ """Find invoice related to statement line"""
+ inv_obj = self.pool.get('account.invoice')
+ if inv_type == 'supplier':
+ type_domain = ('in_invoice', 'in_refund')
+ number_field = 'supplier_invoice_number'
+ elif inv_type == 'customer':
+ type_domain = ('out_invoice', 'out_refund')
+ number_field = 'number'
+ else:
+ raise osv.except_osv(_('System error'),
+ _('Invalid invoice type for completion: %') % inv_type)
+
+ inv_id = inv_obj.search(cr, uid,
+ [(number_field, '=', st_line['ref'].strip()),
+ ('type', 'in', type_domain)],
+ context=context)
+ if inv_id:
+ if len(inv_id) == 1:
+ inv = inv_obj.browse(cr, uid, inv_id[0], context=context)
+ else:
+ raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
+ 'than one partner.') % (st_line['name'], st_line['ref']))
+ return inv
+ return False
+
+ def _from_invoice(self, cr, uid, line, inv_type, context):
+ """Populate statement line values"""
+ if not inv_type in ('supplier', 'customer'):
+ raise osv.except_osv(_('System error'),
+ _('Invalid invoice type for completion: %') % inv_type)
+ res = {}
+ inv = self._find_invoice(cr, uid, line, inv_type, context=context)
+ if inv:
+ res = {'partner_id': inv.partner_id.id,
+ 'account_id': inv.account_id.id,
+ 'type': inv_type}
+ override_acc = line['master_account_id']
+ if override_acc:
+ res['account_id'] = override_acc
+ return res
+
+ # Should be private but data are initialised with no update XML
+ def get_from_ref_and_supplier_invoice(self, cr, uid, line, context=None):
"""
Match the partner based on the invoice supplier invoice number and the reference of the statement
line. Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
- :param int/long line_id: id of the concerned account.bank.statement.line
+ :param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
@@ -141,40 +197,16 @@
...}
"""
- st_obj = self.pool['account.bank.statement.line']
- st_line = st_obj.browse(cr, uid, line_id, context=context)
- res = {}
- inv_obj = self.pool.get('account.invoice')
- if st_line:
- inv_id = inv_obj.search(cr,
- uid,
- [('supplier_invoice_number', '=', st_line.ref),
- ('type', 'in', ('in_invoice', 'in_refund'))],
- context=context)
- if inv_id:
- if len(inv_id) == 1:
- inv = inv_obj.browse(cr, uid, inv_id[0], context=context)
- res['partner_id'] = inv.partner_id.id
- else:
- raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
- 'than one partner.') % (st_line.name, st_line.ref))
- st_vals = st_obj.get_values_for_line(cr,
- uid,
- profile_id=st_line.statement_id.profile_id.id,
- partner_id=res.get('partner_id', False),
- line_type="supplier",
- amount=st_line.amount,
- context=context)
- res.update(st_vals)
- return res
+ return self._from_invoice(cr, uid, line, 'supplier', context=context)
- def get_from_ref_and_invoice(self, cr, uid, line_id, context=None):
+ # Should be private but data are initialised with no update XML
+ def get_from_ref_and_invoice(self, cr, uid, line, context=None):
"""
Match the partner based on the invoice number and the reference of the statement
line. Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
- :param int/long line_id: id of the concerned account.bank.statement.line
+ :param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
@@ -182,39 +214,16 @@
'account_id' : value,
...}
"""
- st_obj = self.pool.get('account.bank.statement.line')
- st_line = st_obj.browse(cr, uid, line_id, context=context)
- res = {}
- if st_line:
- inv_obj = self.pool.get('account.invoice')
- inv_id = inv_obj.search(cr,
- uid,
- [('number', '=', st_line.ref)],
- context=context)
- if inv_id:
- if len(inv_id) == 1:
- inv = inv_obj.browse(cr, uid, inv_id[0], context=context)
- res['partner_id'] = inv.partner_id.id
- else:
- raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
- 'than one partner.') % (st_line.name, st_line.ref))
- st_vals = st_obj.get_values_for_line(cr,
- uid,
- profile_id=st_line.statement_id.profile_id.id,
- partner_id=res.get('partner_id', False),
- line_type=st_line.type,
- amount=st_line.amount,
- context=context)
- res.update(st_vals)
- return res
+ return self._from_invoice(cr, uid, line, 'customer', context=context)
- def get_from_ref_and_so(self, cr, uid, line_id, context=None):
+ # Should be private but data are initialised with no update XML
+ def get_from_ref_and_so(self, cr, uid, st_line, context=None):
"""
Match the partner based on the SO number and the reference of the statement
line. Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
- :param int/long line_id: id of the concerned account.bank.statement.line
+ :param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
@@ -224,36 +233,34 @@
...}
"""
st_obj = self.pool.get('account.bank.statement.line')
- st_line = st_obj.browse(cr, uid, line_id, context=context)
res = {}
if st_line:
so_obj = self.pool.get('sale.order')
- so_id = so_obj.search(
- cr,
- uid,
- [('name', '=', st_line.ref)],
- context=context)
+ so_id = so_obj.search(cr,
+ uid,
+ [('name', '=', st_line['ref'])],
+ context=context)
if so_id:
if so_id and len(so_id) == 1:
so = so_obj.browse(cr, uid, so_id[0], context=context)
res['partner_id'] = so.partner_id.id
elif so_id and len(so_id) > 1:
- raise ErrorTooManyPartner(
- _('Line named "%s" (Ref:%s) was matched by more '
- 'than one partner.') %
- (st_line.name, st_line.ref))
- st_vals = st_obj.get_values_for_line(
- cr,
- uid,
- profile_id=st_line.statement_id.profile_id.id,
- partner_id=res.get('partner_id', False),
- line_type=st_line.type,
- amount=st_line.amount,
- context=context)
+ raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
+ 'than one partner.') %
+ (st_line['name'], st_line['ref']))
+ st_vals = st_obj.get_values_for_line(cr,
+ uid,
+ profile_id=st_line['profile_id'],
+ master_account_id=st_line['master_account_id'],
+ partner_id=res.get('partner_id', False),
+ line_type=st_line['type'],
+ amount=st_line['amount'] if st_line['amount'] else 0.0,
+ context=context)
res.update(st_vals)
return res
- def get_from_label_and_partner_field(self, cr, uid, line_id, context=None):
+ # Should be private but data are initialised with no update XML
+ def get_from_label_and_partner_field(self, cr, uid, st_line, context=None):
"""
Match the partner based on the label field of the statement line
and the text defined in the 'bank_statement_label' field of the partner.
@@ -261,7 +268,7 @@
get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
- :param int/long line_id: id of the concerned account.bank.statement.line
+ :param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
@@ -283,7 +290,7 @@
partner_ids = partner_obj.search(cr,
uid,
[('bank_statement_label', '!=', False)])
- line_ids = tuple(x.id for x in context.get('line_ids', []))
+ line_ids = context.get('line_ids', [])
for partner in partner_obj.browse(cr, uid, partner_ids, context=context):
vals = '|'.join(re.escape(x.strip()) for x in partner.bank_statement_label.split(';'))
or_regex = ".*%s*." % vals
@@ -294,32 +301,32 @@
pairs = cr.fetchall()
for pair in pairs:
context['label_memoizer'][pair[0]].append(partner)
- st_line = st_obj.browse(cr, uid, line_id, context=context)
- if st_line and st_line.id in context['label_memoizer']:
- found_partner = context['label_memoizer'][st_line.id]
+ if st_line['id'] in context['label_memoizer']:
+ found_partner = context['label_memoizer'][st_line['id']]
if len(found_partner) > 1:
raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by '
'more than one partner.') %
- (st_line.name, st_line.ref))
+ (st_line['name'], st_line['ref']))
res['partner_id'] = found_partner[0].id
st_vals = st_obj.get_values_for_line(cr,
uid,
- profile_id=st_line.statement_id.profile_id.id,
+ profile_id=st_line['profile_id'],
+ master_account_id=st_line['master_account_id'],
partner_id=found_partner[0].id,
- line_type=st_line.type,
- amount=st_line.amount,
+ line_type=st_line['type'],
+ amount=st_line['amount'] if st_line['amount'] else 0.0,
context=context)
res.update(st_vals)
return res
- def get_from_label_and_partner_name(self, cr, uid, line_id, context=None):
+ def get_from_label_and_partner_name(self, cr, uid, st_line, context=None):
"""
Match the partner based on the label field of the statement line
and the name of the partner.
Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
- :param int/long line_id: id of the concerned account.bank.statement.line
+ :param int/long st_line: read of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
@@ -328,36 +335,33 @@
...}
"""
- # This Method has not been tested yet !
res = {}
st_obj = self.pool.get('account.bank.statement.line')
- st_line = st_obj.browse(cr, uid, line_id, context=context)
- if st_line:
- sql = "SELECT id FROM res_partner WHERE name ~* %s"
- pattern = ".*%s.*" % re.escape(st_line.label)
- cr.execute(sql, (pattern,))
- result = cr.fetchall()
- if not result:
- return res
- if len(result) > 1:
- raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
- 'than one partner.') %
- (st_line.name, st_line.ref))
- res['partner_id'] = result[0][0] if result else False
- if res:
- st_vals = st_obj.get_values_for_line(
- cr,
- uid,
- profile_id=st_line.statement_id.profile_id.id,
- partner_id=res['partner_id'],
- line_type=st_line.type,
- amount=st_line.amount,
- context=context)
- res.update(st_vals)
+ sql = "SELECT id FROM res_partner WHERE name ~* %s"
+ pattern = ".*%s.*" % re.escape(st_line['name'])
+ cr.execute(sql, (pattern,))
+ result = cr.fetchall()
+ if not result:
+ return res
+ if len(result) > 1:
+ raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more '
+ 'than one partner.') %
+ (st_line['name'], st_line['ref']))
+ res['partner_id'] = result[0][0] if result else False
+ if res:
+ st_vals = st_obj.get_values_for_line(cr,
+ uid,
+ profile_id=st_line['porfile_id'],
+ master_account_id=profile['master_account_id'],
+ partner_id=res['partner_id'],
+ line_type=st_line['type'],
+ amount=st_line['amount'] if st_line['amount'] else 0.0,
+ context=context)
+ res.update(st_vals)
return res
-class AccountStatementLine(Model):
+class AccountStatementLine(orm.Model):
"""
Add sparse field on the statement line to allow to store all the
bank infos that are given by a bank/office. You can then add you own in your
@@ -390,7 +394,7 @@
'already_completed': False,
}
- def get_line_values_from_rules(self, cr, uid, ids, context=None):
+ def _get_line_values_from_rules(self, cr, uid, line, rules, context=None):
"""
We'll try to find out the values related to the line based on rules setted on
the profile.. We will ignore line for which already_completed is ticked.
@@ -401,36 +405,26 @@
{117009: {'partner_id': 100997, 'account_id': 489L}}
"""
profile_obj = self.pool.get('account.statement.profile')
- st_obj = self.pool.get('account.bank.statement.line')
res = {}
errors_stack = []
- for line in self.browse(cr, uid, ids, context=context):
- if line.already_completed:
- continue
- try:
- # Take the default values
- res[line.id] = st_obj.get_values_for_line(
- cr,
- uid,
- profile_id=line.statement_id.profile_id.id,
- line_type=line.type,
- amount=line.amount,
- context=context)
- # Ask the rule
- vals = profile_obj.find_values_from_rules(
- cr, uid, line.statement_id.profile_id.id, line.id, context)
- # Merge the result
- res[line.id].update(vals)
- except ErrorTooManyPartner, exc:
- msg = "Line ID %s had following error: %s" % (line.id, exc.value)
- errors_stack.append(msg)
+ if line.get('already_completed'):
+ return res
+ try:
+ # Ask the rule
+ vals = profile_obj._find_values_from_rules(cr, uid, rules, line, context)
+ if vals:
+ vals['id'] = line['id']
+ return vals
+ except ErrorTooManyPartner as exc:
+ msg = "Line ID %s had following error: %s" % (line['id'], exc.value)
+ errors_stack.append(msg)
if errors_stack:
msg = u"\n".join(errors_stack)
raise ErrorTooManyPartner(msg)
return res
-class AccountBankSatement(Model):
+class AccountBankSatement(orm.Model):
"""
We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually fullfill.
@@ -453,24 +447,29 @@
:return : True
"""
error_log = ""
- user_name = self.pool.get('res.users').read(
- cr, uid, uid, ['name'], context=context)['name']
- log = self.read(
- cr, uid, stat_id, ['completion_logs'], context=context)['completion_logs']
+ user_name = self.pool.get('res.users').read(cr, uid, uid,
+ ['name'], context=context)['name']
+
+ log = self.read(cr, uid, stat_id, ['completion_logs'],
+ context=context)['completion_logs']
+
log_line = log and log.split("\n") or []
completion_date = datetime.datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
if error_msg:
error_log = error_msg
log_line[0:0] = [completion_date + ' : '
- + _("Bank Statement ID %s has %s lines completed by %s") % (stat_id, number_imported, user_name)
- + "\n" + error_log + "-------------" + "\n"]
+ + _("Bank Statement ID %s has %s lines completed by %s") %
+ (stat_id, number_imported, user_name)
+ + "\n" + error_log + "-------------" + "\n"]
log = "\n".join(log_line)
self.write(cr, uid, [stat_id], {'completion_logs': log}, context=context)
- self.message_post(
- cr, uid,
- [stat_id],
- body=_('Statement ID %s auto-completed for %s lines completed') % (stat_id, number_imported),
- context=context)
+
+ body = (_('Statement ID %s auto-completed for %s lines completed') %
+ (stat_id, number_imported)),
+ self.message_post(cr, uid,
+ [stat_id],
+ body=body,
+ context=context)
return True
def button_auto_completion(self, cr, uid, ids, context=None):
@@ -480,29 +479,55 @@
"""
if context is None:
context = {}
- stat_line_obj = self.pool.get('account.bank.statement.line')
+ stat_line_obj = self.pool['account.bank.statement.line']
+ profile_obj = self.pool.get('account.statement.profile')
compl_lines = 0
+ stat_line_obj.check_access_rule(cr, uid, [], 'create')
+ stat_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
for stat in self.browse(cr, uid, ids, context=context):
msg_lines = []
ctx = context.copy()
- ctx['line_ids'] = stat.line_ids
- for line in stat.line_ids:
- res = {}
+ ctx['line_ids'] = tuple((x.id for x in stat.line_ids))
+ b_profile = stat.profile_id
+ rules = profile_obj._get_callable(cr, uid, b_profile, context=context)
+ profile_id = b_profile.id # Only for perfo even it gains almost nothing
+ master_account_id = b_profile.receivable_account_id
+ master_account_id = master_account_id.id if master_account_id else False
+ res = False
+ for line in stat_line_obj.read(cr, uid, ctx['line_ids']):
try:
- res = stat_line_obj.get_line_values_from_rules(
- cr, uid, [line.id], context=ctx)
+ # performance trick
+ line['master_account_id'] = master_account_id
+ line['profile_id'] = profile_id
+ res = stat_line_obj._get_line_values_from_rules(cr, uid, line,
+ rules, context=ctx)
if res:
compl_lines += 1
except ErrorTooManyPartner, exc:
msg_lines.append(repr(exc))
except Exception, exc:
msg_lines.append(repr(exc))
- # vals = res and res.keys() or False
+ error_type, error_value, trbk = sys.exc_info()
+ st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
+ st += ''.join(traceback.format_tb(trbk, 30))
+ print st
+ _logger.error(st)
if res:
- vals = res[line.id]
- vals['already_completed'] = True
- stat_line_obj.write(cr, uid, [line.id], vals, context=ctx)
+ #stat_line_obj.write(cr, uid, [line.id], vals, context=ctx)
+ try:
+ stat_line_obj._update_line(cr, uid, res, context=context)
+ except Exception as exc:
+ msg_lines.append(repr(exc))
+ error_type, error_value, trbk = sys.exc_info()
+ st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
+ st += ''.join(traceback.format_tb(trbk, 30))
+ print st
+ _logger.error(st)
+ # we can commit as it is not needed to be atomic
+ # commiting here adds a nice perfo boost
+ if not compl_lines % 500:
+ cr.commit()
msg = u'\n'.join(msg_lines)
self.write_completion_log(cr, uid, stat.id,
- msg, compl_lines, context=context)
+ msg, compl_lines, context=context)
return True
=== modified file 'account_statement_base_import/parser/generic_file_parser.py'
--- account_statement_base_import/parser/generic_file_parser.py 2013-02-26 08:22:25 +0000
+++ account_statement_base_import/parser/generic_file_parser.py 2013-04-24 10:20:36 +0000
@@ -29,6 +29,7 @@
except:
raise Exception(_('Please install python lib xlrd'))
+
def float_or_zero(val):
""" Conversion function used to manage
empty string into float usecase"""
@@ -82,14 +83,12 @@
In this generic parser, the commission is given for every line, so we store it
for each one.
"""
- return {
- 'name': line.get('label', line.get('ref', '/')),
- 'date': line.get('date', datetime.datetime.now().date()),
- 'amount': line.get('amount', 0.0),
- 'ref': line.get('ref', '/'),
- 'label': line.get('label', ''),
- 'commission_amount': line.get('commission_amount', 0.0),
- }
+ return {'name': line.get('label', line.get('ref', '/')),
+ 'date': line.get('date', datetime.datetime.now().date()),
+ 'amount': line.get('amount', 0.0),
+ 'ref': line.get('ref', '/'),
+ 'label': line.get('label', ''),
+ 'commission_amount': line.get('commission_amount', 0.0)}
def _post(self, *args, **kwargs):
"""
=== modified file 'account_statement_base_import/statement.py'
--- account_statement_base_import/statement.py 2013-02-26 08:45:03 +0000
+++ account_statement_base_import/statement.py 2013-04-24 10:20:36 +0000
@@ -18,14 +18,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
+import sys
+import traceback
+
+import psycopg2
from openerp.tools.translate import _
import datetime
from openerp.osv.orm import Model
from openerp.osv import fields, osv
from parser import new_bank_statement_parser
-import sys
-import traceback
class AccountStatementProfil(Model):
@@ -43,7 +45,8 @@
help="Tic that box to automatically launch the completion "
"on each imported file using this profile."),
'last_import_date': fields.datetime("Last Import Date"),
- 'rec_log': fields.text('log', readonly=True, deprecated=True),
+ # we remove deprecated as it floods logs in standard/warning level sob...
+ 'rec_log': fields.text('log', readonly=True), # Deprecated
'import_type': fields.selection(
get_import_type_selection,
'Type of import',
@@ -64,7 +67,8 @@
self.message_post(cr,
uid,
ids,
- body=_('Statement ID %s have been imported with %s lines.') % (statement_id, num_lines),
+ body=_('Statement ID %s have been imported with %s lines.') %
+ (statement_id, num_lines),
context=context)
return True
@@ -121,13 +125,27 @@
statement_obj = self.pool.get('account.bank.statement')
values = parser_vals
values['statement_id'] = statement_id
- values['account_id'] = statement_obj.get_account_for_counterpart(
- cr,
- uid,
- parser_vals['amount'],
- account_receivable,
- account_payable
- )
+ values['account_id'] = statement_obj.get_account_for_counterpart(cr,
+ uid,
+ parser_vals['amount'],
+ account_receivable,
+ account_payable)
+
+ date = values.get('date')
+ period_memoizer = context.get('period_memoizer')
+ if not period_memoizer:
+ period_memoizer = {}
+ context['period_memoizer'] = period_memoizer
+ if period_memoizer.get(date):
+ values['period_id'] = period_memoizer[date]
+ else:
+ # This is awfully slow...
+ periods = self.pool.get('account.period').find(cr, uid,
+ dt=values.get('date'),
+ context=context)
+ values['period_id'] = periods[0]
+ period_memoizer[date] = periods[0]
+ values['type'] = 'general'
return values
def statement_import(self, cr, uid, ids, profile_id, file_stream, ftype="csv", context=None):
@@ -148,75 +166,85 @@
attachment_obj = self.pool.get('ir.attachment')
prof_obj = self.pool.get("account.statement.profile")
if not profile_id:
- raise osv.except_osv(
- _("No Profile !"),
- _("You must provide a valid profile to import a bank statement !"))
+ raise osv.except_osv(_("No Profile !"),
+ _("You must provide a valid profile to import a bank statement !"))
prof = prof_obj.browse(cr, uid, profile_id, context=context)
parser = new_bank_statement_parser(prof.import_type, ftype=ftype)
result_row_list = parser.parse(file_stream)
# Check all key are present in account.bank.statement.line !!
+ if not result_row_list:
+ raise osv.except_osv(_("Nothing to import"),
+ _("The file is empty"))
parsed_cols = parser.get_st_line_vals(result_row_list[0]).keys()
for col in parsed_cols:
if col not in statement_line_obj._columns:
- raise osv.except_osv(
- _("Missing column !"),
- _("Column %s you try to import is not "
- "present in the bank statement line !") % col)
+ raise osv.except_osv(_("Missing column !"),
+ _("Column %s you try to import is not "
+ "present in the bank statement line !") % col)
- statement_id = statement_obj.create(
- cr, uid, {'profile_id': prof.id}, context=context)
- account_receivable, account_payable = statement_obj.get_default_pay_receiv_accounts(
- cr, uid, context)
+ statement_id = statement_obj.create(cr, uid,
+ {'profile_id': prof.id},
+ context=context)
+ if prof.receivable_account_id:
+ account_receivable = account_payable = prof.receivable_account_id.id
+ else:
+ account_receivable, account_payable = statement_obj.get_default_pay_receiv_accounts(
+ cr, uid, context)
try:
# Record every line in the bank statement and compute the global commission
# based on the commission_amount column
statement_store = []
for line in result_row_list:
parser_vals = parser.get_st_line_vals(line)
- values = self.prepare_statetement_lines_vals(
- cr, uid, parser_vals, account_payable,
- account_receivable, statement_id, context)
- # we finally create the line in system
- statement_store.append((0, 0, values))
+ values = self.prepare_statetement_lines_vals(cr, uid, parser_vals, account_payable,
+ account_receivable, statement_id, context)
+ statement_store.append(values)
+ # Hack to bypass ORM poor perfomance. Sob...
+ statement_line_obj._insert_lines(cr, uid, statement_store, context=context)
+
# Build and create the global commission line for the whole statement
- statement_obj.write(cr, uid, [statement_id],
- {'line_ids': statement_store}, context=context)
comm_vals = self.prepare_global_commission_line_vals(cr, uid, parser, result_row_list,
prof, statement_id, context)
if comm_vals:
statement_line_obj.create(cr, uid, comm_vals, context=context)
-
- attachment_obj.create(
- cr,
- uid,
- {
- 'name': 'statement file',
- 'datas': file_stream,
- 'datas_fname': "%s.%s" % (
- datetime.datetime.now().date(),
- ftype),
- 'res_model': 'account.bank.statement',
- 'res_id': statement_id,
- },
- context=context
- )
+ else:
+ # Trigger store field computation if someone has better idea
+ start_bal = statement_obj.read(cr, uid, statement_id,
+ ['balance_start'],
+ context=context)
+ start_bal = start_bal['balance_start']
+ statement_obj.write(cr, uid, [statement_id],
+ {'balance_start': start_bal})
+
+ attachment_obj.create(cr,
+ uid,
+ {'name': 'statement file',
+ 'datas': file_stream,
+ 'datas_fname': "%s.%s" % (
+ datetime.datetime.now().date(),
+ ftype),
+ 'res_model': 'account.bank.statement',
+ 'res_id': statement_id},
+ context=context)
+
# If user ask to launch completion at end of import, do it !
if prof.launch_import_completion:
statement_obj.button_auto_completion(cr, uid, [statement_id], context)
# Write the needed log infos on profile
- self.write_logs_after_import(
- cr, uid, prof.id, statement_id, len(result_row_list), context)
+ self.write_logs_after_import(cr, uid, prof.id,
+ statement_id,
+ len(result_row_list),
+ context)
except Exception:
statement_obj.unlink(cr, uid, [statement_id], context=context)
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
- raise osv.except_osv(
- _("Statement import error"),
- _("The statement cannot be created : %s") % st)
+ raise osv.except_osv(_("Statement import error"),
+ _("The statement cannot be created : %s") % st)
return statement_id
@@ -228,6 +256,46 @@
"""
_inherit = "account.bank.statement.line"
+ def _get_available_columns(self, statement_store):
+ """Return writeable by SQL columns"""
+ statement_line_obj = self.pool['account.bank.statement.line']
+ model_cols = statement_line_obj._columns
+ avail = [k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')]
+ keys = [k for k in statement_store[0].keys() if k in avail]
+ keys.sort()
+ return keys
+
+ def _insert_lines(self, cr, uid, statement_store, context=None):
+ """ Do raw insert into database because ORM is awfully slow
+ when doing batch write. It is a shame that batch function
+ does not exist"""
+ statement_line_obj = self.pool['account.bank.statement.line']
+ statement_line_obj.check_access_rule(cr, uid, [], 'create')
+ statement_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
+ cols = self._get_available_columns(statement_store)
+ tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in cols]))
+ sql = "INSERT INTO account_bank_statement_line (%s) VALUES (%s);" % tmp_vals
+ try:
+ cr.executemany(sql, tuple(statement_store))
+ except psycopg2.Error as sql_err:
+ cr.rollback()
+ raise osv.except_osv(_("ORM bypass error"),
+ sql_err.pgerror)
+
+ def _update_line(self, cr, uid, vals, context=None):
+ """ Do raw update into database because ORM is awfully slow
+ when doing batch write. It is a shame that batch function
+ does not exist"""
+ cols = self._get_available_columns([vals])
+ tmp_vals = (', '.join(['%s = %%(%s)s' % (i, i) for i in cols]))
+ sql = "UPDATE account_bank_statement_line SET %s where id = %%(id)s;" % tmp_vals
+ try:
+ cr.execute(sql, vals)
+ except psycopg2.Error as sql_err:
+ cr.rollback()
+ raise osv.except_osv(_("ORM bypass error"),
+ sql_err.pgerror)
+
_columns = {
'commission_amount': fields.sparse(
type='float',
=== modified file 'account_statement_ext/__openerp__.py'
--- account_statement_ext/__openerp__.py 2013-02-14 08:13:49 +0000
+++ account_statement_ext/__openerp__.py 2013-04-24 10:20:36 +0000
@@ -25,11 +25,9 @@
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal',
- 'depends': [
- 'account',
- 'report_webkit',
- 'account_voucher'
- ],
+ 'depends': ['account',
+ 'report_webkit',
+ 'account_voucher'],
'description': """
Improve the basic bank statement, by adding various new features,
and help dealing with huge volume of reconciliation through payment offices such as Paypal, Lazer,
@@ -65,7 +63,6 @@
4) Remove the period on the bank statement, and compute it for each line based on their date instead.
It also adds this feature in the voucher in order to compute the period correctly.
-
5) Cancelling a bank statement is much more easy and will cancel all related entries, unreconcile them,
and finally delete them.
@@ -88,4 +85,4 @@
'auto_install': False,
'license': 'AGPL-3',
'active': False,
-}
+ }
=== modified file 'account_statement_ext/statement.py'
--- account_statement_ext/statement.py 2013-03-01 14:33:32 +0000
+++ account_statement_ext/statement.py 2013-04-24 10:20:36 +0000
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+#-*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi, Joel Grand-Guillaume
@@ -18,12 +18,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
-
+import openerp.addons.account.account_bank_statement as stat_mod
from openerp.osv.orm import Model
from openerp.osv import fields, osv
from openerp.tools.translate import _
+# Monkey patch to fix bad write implementation...
+def fixed_write(self, cr, uid, ids, vals, context=None):
+ """ Fix performance desing of original function
+ Ideally we should use a real postgres sequence or serial fields.
+ Will do it when I have time."""
+ res = super(stat_mod.account_bank_statement, self).write(cr, uid, ids,
+ vals, context=context)
+ for s_id in ids:
+ cr.execute("UPDATE account_bank_statement_line"
+ " SET sequence = account_bank_statement_line.id + 1"
+ " where statement_id = %s", (s_id,))
+ return res
+stat_mod.account_bank_statement.write = fixed_write
+
+
class AccountStatementProfil(Model):
"""
A Profile will contain all infos related to the type of
@@ -44,38 +59,45 @@
"commission move (and optionaly on the counterpart "
"of the intermediate/banking move if you tick the "
"corresponding checkbox)."),
+
'journal_id': fields.many2one(
'account.journal',
'Financial journal to use for transaction',
required=True),
+
'commission_account_id': fields.many2one(
'account.account',
'Commission account',
required=True),
+
'commission_analytic_id': fields.many2one(
'account.analytic.account',
'Commission analytic account'),
+
'receivable_account_id': fields.many2one(
'account.account',
'Force Receivable/Payable Account',
help="Choose a receivable account to force the default "
"debit/credit account (eg. an intermediat bank account "
"instead of default debitors)."),
+
'force_partner_on_bank': fields.boolean(
'Force partner on bank move',
help="Tick that box if you want to use the credit "
"institute partner in the counterpart of the "
"intermediate/banking move."),
+
'balance_check': fields.boolean(
'Balance check',
help="Tick that box if you want OpenERP to control "
"the start/end balance before confirming a bank statement. "
- "If don't ticked, no balance control will be done."
- ),
- 'bank_statement_prefix': fields.char(
- 'Bank Statement Prefix', size=32),
- 'bank_statement_ids': fields.one2many(
- 'account.bank.statement', 'profile_id', 'Bank Statement Imported'),
+ "If don't ticked, no balance control will be done."),
+
+ 'bank_statement_prefix': fields.char('Bank Statement Prefix', size=32),
+
+ 'bank_statement_ids': fields.one2many('account.bank.statement',
+ 'profile_id',
+ 'Bank Statement Imported'),
'company_id': fields.many2one('res.company', 'Company'),
}
@@ -166,11 +188,11 @@
"""
for statement in self.browse(cr, uid, ids, context=context):
if (statement.period_id and
- statement.company_id.id != statement.period_id.company_id.id):
+ statement.company_id.id != statement.period_id.company_id.id):
return False
for line in statement.line_ids:
if (line.period_id and
- statement.company_id.id != line.period_id.company_id.id):
+ statement.company_id.id != line.period_id.company_id.id):
return False
return True
@@ -244,8 +266,10 @@
create the move from.
:return: int/long of the res.partner to use as counterpart
"""
- bank_partner_id = super(AccountBankSatement, self).\
- _get_counter_part_partner(cr, uid, st_line, context=context)
+ bank_partner_id = super(AccountBankSatement, self)._get_counter_part_partner(cr,
+ uid,
+ st_line,
+ context=context)
# get the right partner according to the chosen profil
if st_line.statement_id.profile_id.force_partner_on_bank:
bank_partner_id = st_line.statement_id.profile_id.partner_id.id
@@ -298,7 +322,7 @@
if (not st.journal_id.default_credit_account_id) \
or (not st.journal_id.default_debit_account_id):
raise osv.except_osv(_('Configuration Error !'),
- _('Please verify that an account is defined in the journal.'))
+ _('Please verify that an account is defined in the journal.'))
if not st.name == '/':
st_number = st.name
@@ -309,7 +333,7 @@
for line in st.move_line_ids:
if line.state != 'valid':
raise osv.except_osv(_('Error !'),
- _('The account entries lines are not in valid state.'))
+ _('The account entries lines are not in valid state.'))
# begin changes
errors_stack = []
for st_line in st.line_ids:
@@ -317,11 +341,15 @@
if st_line.analytic_account_id:
if not st.journal_id.analytic_journal_id:
raise osv.except_osv(_('No Analytic Journal !'),
- _("You have to assign an analytic journal on the '%s' journal!") % (st.journal_id.name,))
+ _("You have to assign an analytic"
+ " journal on the '%s' journal!") % st.journal_id.name)
if not st_line.amount:
continue
st_line_number = self.get_next_st_line_number(cr, uid, st_number, st_line, context)
- self.create_move_from_st_line(cr, uid, st_line.id, company_currency_id, st_line_number, context)
+ self.create_move_from_st_line(cr, uid, st_line.id,
+ company_currency_id,
+ st_line_number,
+ context)
except osv.except_osv, exc:
msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, exc.value)
errors_stack.append(msg)
@@ -332,37 +360,95 @@
msg = u"\n".join(errors_stack)
raise osv.except_osv(_('Error'), msg)
#end changes
- self.write(cr, uid, [st.id], {
- 'name': st_number,
- 'balance_end_real': st.balance_end
- }, context=context)
- self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st_number,), context=context)
+ self.write(cr, uid, [st.id],
+ {'name': st_number,
+ 'balance_end_real': st.balance_end},
+ context=context)
+ body = _('Statement %s confirmed, journal items were created.') % st_number
+ self.message_post(cr, uid, [st.id],
+ body,
+ context=context)
return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
- def get_account_for_counterpart(
- self, cr, uid, amount, account_receivable, account_payable):
+ def get_account_for_counterpart(self, cr, uid, amount, account_receivable, account_payable):
+ """For backward compatibility."""
+ account_id, type = self.get_account_and_type_for_counterpart(cr, uid, amount,
+ account_receivable,
+ account_payable)
+ return account_id
+
+ def _compute_type_from_partner_profile(self, cr, uid, partner_id,
+ default_type, context=None):
+ """Compute the statement line type
+ from partner profile (customer, supplier)"""
+ obj_partner = self.pool.get('res.partner')
+ part = obj_partner.browse(cr, uid, partner_id, context=context)
+ if part.supplier == part.customer:
+ return default_type
+ if part.supplier:
+ return 'supplier'
+ else:
+ return 'customer'
+
+ def _compute_type_from_amount(self, cr, uid, amount):
+ """Compute the statement type based on amount"""
+ if amount < 0:
+ return 'supplier'
+ return 'customer'
+
+ def get_type_for_counterpart(self, cr, uid, amount, partner_id=False):
+ """Give the amount and receive the type to use for the line.
+ The rules are:
+ - If the customer checkbox is checked on the found partner, type customer
+ - If the supplier checkbox is checked on the found partner, typewill be supplier
+ - If both checkbox are checked or none of them, it'll be based on the amount :
+ If amount is positif the type customer,
+ If amount is negativ, the type supplier
+ :param float: amount of the line
+ :param int/long: partner_id the partner id
+ :return: type as string: the default type to use: 'customer' or 'supplier'.
+ """
+ s_line_type = self._compute_type_from_amount(cr, uid, amount)
+ if partner_id:
+ s_line_type = self._compute_type_from_partner_profile(cr, uid,
+ partner_id, s_line_type)
+ return s_line_type
+
+ def get_account_and_type_for_counterpart(self, cr, uid, amount, account_receivable,
+ account_payable, partner_id=False):
"""
Give the amount, payable and receivable account (that can be found using
get_default_pay_receiv_accounts method) and receive the one to use. This method
should be use when there is no other way to know which one to take.
+ The rules are:
+ - If the customer checkbox is checked on the found partner, type and account will be customer and receivable
+ - If the supplier checkbox is checked on the found partner, type and account will be supplier and payable
+ - If both checkbox are checked or none of them, it'll be based on the amount :
+ If amount is positif the type and account will be customer and receivable,
+ If amount is negativ, the type and account will be supplier and payable
+ Note that we return the payable or receivable account from agrs and not from the optional partner_id
+ given !
:param float: amount of the line
:param int/long: account_receivable the receivable account
:param int/long: account_payable the payable account
- :return: int/long :the default account to be used by statement line as the counterpart
- of the journal account depending on the amount.
+ :param int/long: partner_id the partner id
+ :return: dict with [account_id as int/long,type as string]: the default account to be used by
+ statement line as the counterpart of the journal account depending on the amount and the type
+ as 'customer' or 'supplier'.
"""
account_id = False
- if amount >= 0:
+ type = self.get_type_for_counterpart(cr, uid, amount, partner_id=partner_id)
+ if type == 'supplier':
+ account_id = account_payable
+ else:
account_id = account_receivable
- else:
- account_id = account_payable
if not account_id:
raise osv.except_osv(
_('Can not determine account'),
_('Please ensure that minimal properties are set')
)
- return account_id
+ return [account_id, type]
def get_default_pay_receiv_accounts(self, cr, uid, context=None):
"""
@@ -386,14 +472,11 @@
('model', '=', 'res.partner')],
context=context
)
- property_ids = property_obj.search(
- cr,
- uid,
- [('fields_id', 'in', model_fields_ids),
- ('res_id', '=', False),
- ],
- context=context
- )
+ property_ids = property_obj.search(cr,
+ uid,
+ [('fields_id', 'in', model_fields_ids),
+ ('res_id', '=', False)],
+ context=context)
for erp_property in property_obj.browse(
cr, uid, property_ids, context=context):
@@ -433,13 +516,10 @@
journal_id = import_config.journal_id.id
account_id = import_config.journal_id.default_debit_account_id.id
credit_partner_id = import_config.partner_id and import_config.partner_id.id or False
- return {'value':
- {'journal_id': journal_id,
- 'account_id': account_id,
- 'balance_check': import_config.balance_check,
- 'credit_partner_id': credit_partner_id,
- }
- }
+ return {'value': {'journal_id': journal_id,
+ 'account_id': account_id,
+ 'balance_check': import_config.balance_check,
+ 'credit_partner_id': credit_partner_id}}
class AccountBankSatementLine(Model):
@@ -472,18 +552,24 @@
'account_id': _get_default_account,
}
- def get_values_for_line(self, cr, uid, profile_id=False, partner_id=False, line_type=False, amount=False, context=None):
+ def get_values_for_line(self, cr, uid, profile_id=False, partner_id=False, line_type=False, amount=False, master_account_id=None, context=None):
"""
Return the account_id to be used in the line of a bank statement. It'll base the result as follow:
- If a receivable_account_id is set in the profile, return this value and type = general
- - Elif line_type is given, take the partner receivable/payable property (payable if type= supplier, receivable
+ # TODO
+ - Elif how_get_type_account is set to force_supplier or force_customer, will take respectively payable and type=supplier,
+ receivable and type=customer otherwise
+ # END TODO
+ - Elif line_type is given, take the partner receivable/payable property (payable if type=supplier, receivable
otherwise)
- - Elif amount is given, take the partner receivable/payable property (receivable if amount >= 0.0,
- payable otherwise). In that case, we also fullfill the type (receivable = customer, payable = supplier)
- so it is easier for the accountant to know why the receivable/payable has been chosen
+ - Elif amount is given:
+ - If the customer checkbox is checked on the found partner, type and account will be customer and receivable
+ - If the supplier checkbox is checked on the found partner, type and account will be supplier and payable
+ - If both checkbox are checked or none of them, it'll be based on the amount :
+ If amount is positif the type and account will be customer and receivable,
+ If amount is negativ, the type and account will be supplier an payable
- Then, if no partner are given we look and take the property from the company so we always give a value
for account_id. Note that in that case, we return the receivable one.
-
:param int/long profile_id of the related bank statement
:param int/long partner_id of the line
:param char line_type: a value from: 'general', 'supplier', 'customer'
@@ -501,14 +587,19 @@
obj_stat = self.pool.get('account.bank.statement')
line_type = receiv_account = pay_account = account_id = False
# If profile has a receivable_account_id, we return it in any case
- if profile_id:
+ if master_account_id:
+ res['account_id'] = master_account_id
+ res['type'] = 'general'
+ return res
+ # To obtimize we consider passing false means there is no account
+ # on profile
+ if profile_id and master_account_id is None:
profile = self.pool.get("account.statement.profile").browse(
- cr, uid, profile_id, context=context)
+ cr, uid, profile_id, context=context)
if profile.receivable_account_id:
- account_id = profile.receivable_account_id.id
- line_type = 'general'
+ res['account_id'] = profile.receivable_account_id.id
+ res['type'] = 'general'
return res
- # If partner -> take from him
if partner_id:
part = obj_partner.browse(cr, uid, partner_id, context=context)
pay_account = part.property_account_payable.id
@@ -523,12 +614,8 @@
if line_type == 'supplier':
account_id = pay_account
elif amount is not False:
- if amount >= 0:
- account_id = receiv_account
- line_type = 'customer'
- else:
- account_id = pay_account
- line_type = 'supplier'
+ account_id, line_type = obj_stat.get_account_and_type_for_counterpart(cr, uid, amount,
+ receiv_account, pay_account, partner_id=partner_id)
res['account_id'] = account_id if account_id else receiv_account
res['type'] = line_type
return res
@@ -537,41 +624,36 @@
"""
Override of the basic method as we need to pass the profile_id in the on_change_type
call.
+ Moreover, we now call the get_account_and_type_for_counterpart method now to get the
+ type to use.
"""
- obj_partner = self.pool.get('res.partner')
+ obj_stat = self.pool.get('account.bank.statement')
if not partner_id:
return {}
- part = obj_partner.browse(cr, uid, partner_id, context=context)
- if not part.supplier and not part.customer:
- type = 'general'
- elif part.supplier and part.customer:
- type = 'general'
- else:
- if part.supplier == True:
- type = 'supplier'
- if part.customer == True:
- type = 'customer'
- res_type = self.onchange_type(cr, uid, ids, partner_id, type, profile_id, context=context) # Chg
+ line_type = obj_stat.get_type_for_counterpart(cr, uid, 0.0, partner_id=partner_id)
+ res_type = self.onchange_type(cr, uid, ids, partner_id, line_type, profile_id, context=context)
if res_type['value'] and res_type['value'].get('account_id', False):
- return {'value': {'type': type,
+ return {'value': {'type': line_type,
'account_id': res_type['value']['account_id'],
'voucher_id': False}}
- return {'value': {'type': type}}
+ return {'value': {'type': line_type}}
- def onchange_type(self, cr, uid, line_id, partner_id, type, profile_id, context=None):
+ def onchange_type(self, cr, uid, line_id, partner_id, line_type, profile_id, context=None):
"""
Keep the same features as in standard and call super. If an account is returned,
call the method to compute line values.
"""
- res = super(AccountBankSatementLine, self).onchange_type(
- cr, uid, line_id, partner_id, type, context=context)
+ res = super(AccountBankSatementLine, self).onchange_type(cr, uid,
+ line_id,
+ partner_id,
+ line_type,
+ context=context)
if 'account_id' in res['value']:
- result = self.get_values_for_line(
- cr, uid,
- profile_id=profile_id,
- partner_id=partner_id,
- line_type=type,
- context=context)
+ result = self.get_values_for_line(cr, uid,
+ profile_id=profile_id,
+ partner_id=partner_id,
+ line_type=line_type,
+ context=context)
if result:
res['value'].update({'account_id': result['account_id']})
return res
Follow ups