[IMP] mis_builder: be more precise wrt the root account used
This solve issues with empty account selectors in presence of multiple companies.pull/86/head
parent
45d3477ded
commit
0ede82e6da
|
@ -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))
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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…
Reference in New Issue