diff --git a/mis_builder/models/aep.py b/mis_builder/models/aep.py index bc2f4021..500174ec 100644 --- a/mis_builder/models/aep.py +++ b/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)) diff --git a/mis_builder/models/mis_builder.py b/mis_builder/models/mis_builder.py index 73932f15..767d40ca 100644 --- a/mis_builder/models/mis_builder.py +++ b/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') diff --git a/mis_builder/views/mis_builder.xml b/mis_builder/views/mis_builder.xml index e4a34ae1..f7e09a83 100644 --- a/mis_builder/views/mis_builder.xml +++ b/mis_builder/views/mis_builder.xml @@ -139,6 +139,7 @@ +