Browse Source

[IMP] mis_builder: be more precise wrt the root account used

This solve issues with empty account selectors in presence of multiple companies.
pull/90/head
Stéphane Bidoul 9 years ago
parent
commit
38d34b95b1
  1. 62
      mis_builder/models/aep.py
  2. 33
      mis_builder/models/mis_builder.py
  3. 1
      mis_builder/views/mis_builder.xml

62
mis_builder/models/aep.py

@ -62,10 +62,9 @@ class AccountingExpressionProcessor(object):
# before done_parsing: {(domain, mode): set(account_codes)}
# after done_parsing: {(domain, mode): set(account_ids)}
self._map_account_ids = defaultdict(set)
self._set_all_accounts = set() # set((domain, mode))
self._account_ids_by_code = defaultdict(set)
def _load_account_codes(self, account_codes, account_domain):
def _load_account_codes(self, account_codes, root_account):
account_model = self.env['account.account']
# TODO: account_obj is necessary because _get_children_and_consol
# does not work in new API?
@ -75,23 +74,35 @@ class AccountingExpressionProcessor(object):
for account_code in account_codes:
if account_code in self._account_ids_by_code:
continue
if '%' in account_code:
if account_code is None:
# by convention the root account is keyed as
# None in _account_ids_by_code, so it is consistent
# with what _parse_match_object returns for an
# empty list of account codes, ie [None]
exact_codes.add(root_account.code)
elif '%' in account_code:
like_codes.add(account_code)
else:
exact_codes.add(account_code)
for account in account_model.\
search([('code', 'in', list(exact_codes))] + account_domain):
search([('code', 'in', list(exact_codes)),
('parent_id', 'child_of', root_account.id)]):
if account.code == root_account.code:
code = None
else:
code = account.code
if account.type in ('view', 'consolidation'):
self._account_ids_by_code[account.code].update(
self._account_ids_by_code[code].update(
account_obj._get_children_and_consol(
self.env.cr, self.env.uid,
[account.id],
self.env.context))
else:
self._account_ids_by_code[account.code].add(account.id)
self._account_ids_by_code[code].add(account.id)
for like_code in like_codes:
for account in account_model.\
search([('code', 'like', like_code)] + account_domain):
search([('code', 'like', like_code),
('parent_id', 'child_of', root_account.id)]):
if account.type in ('view', 'consolidation'):
self._account_ids_by_code[like_code].update(
account_obj._get_children_and_consol(
@ -118,7 +129,7 @@ class AccountingExpressionProcessor(object):
if account_codes.strip():
account_codes = [a.strip() for a in account_codes.split(',')]
else:
account_codes = None
account_codes = [None]
domain = domain or '[]'
domain = tuple(safe_eval(domain))
return field, mode, account_codes, domain
@ -132,15 +143,12 @@ class AccountingExpressionProcessor(object):
for mo in self.ACC_RE.finditer(expr):
_, mode, account_codes, domain = self._parse_match_object(mo)
key = (domain, mode)
if account_codes:
self._map_account_ids[key].update(account_codes)
else:
self._set_all_accounts.add(key)
self._map_account_ids[key].update(account_codes)
def done_parsing(self, account_domain):
def done_parsing(self, root_account):
# load account codes and replace account codes by account ids in _map
for key, account_codes in self._map_account_ids.items():
self._load_account_codes(account_codes, account_domain)
self._load_account_codes(account_codes, root_account)
account_ids = set()
for account_code in account_codes:
account_ids.update(self._account_ids_by_code[account_code])
@ -255,7 +263,7 @@ class AccountingExpressionProcessor(object):
if opening_period.date_start == period_from.date_start and \
mode == MODE_INITIAL:
# if the opening period has the same start date as
# period_from, the we'll find the initial balance
# period_from, then we'll find the initial balance
# in the initial period and that's it
period_ids.append(opening_period[0].id)
continue
@ -296,23 +304,6 @@ class AccountingExpressionProcessor(object):
for acc in accs:
self._data[key][acc['account_id'][0]] = \
(acc['debit'] or 0.0, acc['credit'] or 0.0)
# fetch sum of debit/credit for expressions with no account
for key in self._set_all_accounts:
domain, mode = key
if mode == MODE_VARIATION:
domain = list(domain) + period_domain
elif mode == MODE_INITIAL:
domain = list(domain) + period_domain_i
elif mode == MODE_END:
domain = list(domain) + period_domain_e
else:
raise RuntimeError("unexpected mode %s" % (mode,))
accs = aml_model.read_group(domain,
['debit', 'credit'],
[])
assert len(accs) == 1
self._data[key][None] = \
(accs[0]['debit'] or 0.0, accs[0]['credit'] or 0.0)
def replace_expr(self, expr):
"""Replace accounting variables in an expression by their amount.
@ -326,11 +317,8 @@ class AccountingExpressionProcessor(object):
key = (domain, mode)
account_ids_data = self._data[key]
v = 0.0
for account_code in account_codes or [None]:
if account_code:
account_ids = self._account_ids_by_code[account_code]
else:
account_ids = [None]
for account_code in account_codes:
account_ids = self._account_ids_by_code[account_code]
for account_id in account_ids:
debit, credit = \
account_ids_data.get(account_id, (0.0, 0.0))

33
mis_builder/models/mis_builder.py

@ -483,13 +483,12 @@ class mis_report_instance_period(orm.Model):
'Period name should be unique by report'),
]
def drilldown(self, cr, uid, id, expr, context=None):
this = self.browse(cr, uid, id, context=context)[0]
def drilldown(self, cr, uid, _id, expr, context=None):
this = self.browse(cr, uid, _id, context=context)[0]
env = Environment(cr, uid, {})
aep = AccountingExpressionProcessor(env)
aep.parse_expr(expr)
aep.done_parsing([('company_id', '=',
this.report_instance_id.company_id.id)])
aep.done_parsing(this.report_instance_id.root_account)
domain = aep.get_aml_domain_for_expr(expr)
if domain:
# TODO: reuse compute_period_domain
@ -680,6 +679,19 @@ class mis_report_instance(orm.Model):
context=context)
return res
def _get_root_account(self, cr, uid, ids, field_name, arg, context=None):
res = {}
account_obj = self.pool['account.account']
for r in self.browse(cr, uid, ids, context=context):
account_ids = account_obj.search(
cr, uid,
[('parent_id', '=', False),
('company_id', '=', r.company_id.id)],
context=context)
if len(account_ids) == 1:
res[r.id] = account_ids[0]
return res
_name = 'mis.report.instance'
_columns = {
@ -704,6 +716,9 @@ class mis_report_instance(orm.Model):
('all', 'All Entries'),
], 'Target Moves', required=True),
'company_id': fields.many2one('res.company', 'Company', required=True),
'root_account': fields.function(_get_root_account,
type='many2one', obj='account.account',
string="Account chart"),
}
_defaults = {
@ -754,13 +769,11 @@ class mis_report_instance(orm.Model):
tools.DEFAULT_SERVER_DATE_FORMAT),
tformat)
def compute(self, cr, uid, _ids, context=None):
assert isinstance(_ids, (int, long))
def compute(self, cr, uid, _id, context=None):
assert isinstance(_id, (int, long))
if context is None:
context = {}
r = self.browse(cr, uid, _ids, context=context)
context['state'] = r.target_move
r = self.browse(cr, uid, _id, context=context)
content = OrderedDict()
# empty line name for header
header = OrderedDict()
@ -773,7 +786,7 @@ class mis_report_instance(orm.Model):
content[kpi.name] = {'kpi_name': kpi.description,
'cols': [],
'default_style': ''}
aep.done_parsing([('company_id', '=', r.company_id.id)])
aep.done_parsing(r.root_account)
report_instance_period_obj = self.pool.get(
'mis.report.instance.period')
kpi_obj = self.pool.get('mis.report.kpi')

1
mis_builder/views/mis_builder.xml

@ -139,6 +139,7 @@
<field name="description"/>
<field name="report_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="root_account"/>
<field name="target_move"/>
<field name="date"/>
<field name="period_ids">

Loading…
Cancel
Save