Browse Source

[IMP] mis_builder: use company currency decimal place in deciding if initial balances are null or 0

pull/189/head
Stéphane Bidoul 9 years ago
parent
commit
53192385fb
  1. 53
      mis_builder/models/aep.py
  2. 7
      mis_builder/models/mis_report.py
  3. 5
      mis_builder/models/mis_report_instance.py
  4. 7
      mis_builder/tests/test_aep.py

53
mis_builder/models/aep.py

@ -67,8 +67,9 @@ class AccountingExpressionProcessor(object):
r"(?P<accounts>_[a-zA-Z0-9]+|\[.*?\])" r"(?P<accounts>_[a-zA-Z0-9]+|\[.*?\])"
r"(?P<domain>\[.*?\])?") r"(?P<domain>\[.*?\])?")
def __init__(self, env):
self.env = env
def __init__(self, company):
self.company = company
self.dp = company.currency_id.decimal_places
# before done_parsing: {(domain, mode): set(account_codes)} # before done_parsing: {(domain, mode): set(account_codes)}
# after done_parsing: {(domain, mode): list(account_ids)} # after done_parsing: {(domain, mode): list(account_ids)}
self._map_account_ids = defaultdict(set) self._map_account_ids = defaultdict(set)
@ -83,8 +84,8 @@ class AccountingExpressionProcessor(object):
# to get the variation, so it's a bit slower # to get the variation, so it's a bit slower
self.smart_end = True self.smart_end = True
def _load_account_codes(self, account_codes, company):
account_model = self.env['account.account']
def _load_account_codes(self, account_codes):
account_model = self.company.env['account.account']
exact_codes = set() exact_codes = set()
for account_code in account_codes: for account_code in account_codes:
if account_code in self._account_ids_by_code: if account_code in self._account_ids_by_code:
@ -92,19 +93,19 @@ class AccountingExpressionProcessor(object):
if account_code is None: if account_code is None:
# None means we want all accounts # None means we want all accounts
account_ids = account_model.\ account_ids = account_model.\
search([('company_id', '=', company.id)]).ids
search([('company_id', '=', self.company.id)]).ids
self._account_ids_by_code[account_code].update(account_ids) self._account_ids_by_code[account_code].update(account_ids)
elif '%' in account_code: elif '%' in account_code:
account_ids = account_model.\ account_ids = account_model.\
search([('code', '=like', account_code), search([('code', '=like', account_code),
('company_id', '=', company.id)]).ids
('company_id', '=', self.company.id)]).ids
self._account_ids_by_code[account_code].update(account_ids) self._account_ids_by_code[account_code].update(account_ids)
else: else:
# search exact codes after the loop to do less queries # search exact codes after the loop to do less queries
exact_codes.add(account_code) exact_codes.add(account_code)
for account in account_model.\ for account in account_model.\
search([('code', 'in', list(exact_codes)), search([('code', 'in', list(exact_codes)),
('company_id', '=', company.id)]):
('company_id', '=', self.company.id)]):
self._account_ids_by_code[account.code].add(account.id) self._account_ids_by_code[account.code].add(account.id)
def _parse_match_object(self, mo): def _parse_match_object(self, mo):
@ -146,13 +147,13 @@ class AccountingExpressionProcessor(object):
key = (domain, mode) key = (domain, mode)
self._map_account_ids[key].update(account_codes) self._map_account_ids[key].update(account_codes)
def done_parsing(self, company):
def done_parsing(self):
"""Load account codes and replace account codes by """Load account codes and replace account codes by
account ids in map.""" account ids in map."""
for key, account_codes in self._map_account_ids.items(): for key, account_codes in self._map_account_ids.items():
# TODO _load_account_codes could be done # TODO _load_account_codes could be done
# for all account_codes at once (also in v8) # for all account_codes at once (also in v8)
self._load_account_codes(account_codes, company)
self._load_account_codes(account_codes)
account_ids = set() account_ids = set()
for account_code in account_codes: for account_code in account_codes:
account_ids.update(self._account_ids_by_code[account_code]) account_ids.update(self._account_ids_by_code[account_code])
@ -165,7 +166,7 @@ class AccountingExpressionProcessor(object):
def get_aml_domain_for_expr(self, expr, def get_aml_domain_for_expr(self, expr,
date_from, date_to, date_from, date_to,
target_move, company,
target_move,
account_id=None): account_id=None):
""" Get a domain on account.move.line for an expression. """ Get a domain on account.move.line for an expression.
@ -197,15 +198,14 @@ class AccountingExpressionProcessor(object):
if mode not in date_domain_by_mode: if mode not in date_domain_by_mode:
date_domain_by_mode[mode] = \ date_domain_by_mode[mode] = \
self.get_aml_domain_for_dates(date_from, date_to, self.get_aml_domain_for_dates(date_from, date_to,
mode, target_move,
company)
mode, target_move)
assert aml_domains assert aml_domains
return expression.OR(aml_domains) + \ return expression.OR(aml_domains) + \
expression.OR(date_domain_by_mode.values()) expression.OR(date_domain_by_mode.values())
def get_aml_domain_for_dates(self, date_from, date_to, def get_aml_domain_for_dates(self, date_from, date_to,
mode, mode,
target_move, company):
target_move):
if mode == self.MODE_VARIATION: if mode == self.MODE_VARIATION:
domain = [('date', '>=', date_from), ('date', '<=', date_to)] domain = [('date', '>=', date_from), ('date', '<=', date_to)]
elif mode in (self.MODE_INITIAL, self.MODE_END): elif mode in (self.MODE_INITIAL, self.MODE_END):
@ -214,7 +214,8 @@ class AccountingExpressionProcessor(object):
# sum from the beginning of time # sum from the beginning of time
date_from_date = fields.Date.from_string(date_from) date_from_date = fields.Date.from_string(date_from)
fy_date_from = \ fy_date_from = \
company.compute_fiscalyear_dates(date_from_date)['date_from']
self.company.\
compute_fiscalyear_dates(date_from_date)['date_from']
domain = ['|', domain = ['|',
('date', '>=', fields.Date.to_string(fy_date_from)), ('date', '>=', fields.Date.to_string(fy_date_from)),
('user_type_id.include_initial_balance', '=', True)] ('user_type_id.include_initial_balance', '=', True)]
@ -225,21 +226,22 @@ class AccountingExpressionProcessor(object):
elif mode == self.MODE_UNALLOCATED: elif mode == self.MODE_UNALLOCATED:
date_from_date = fields.Date.from_string(date_from) date_from_date = fields.Date.from_string(date_from)
fy_date_from = \ fy_date_from = \
company.compute_fiscalyear_dates(date_from_date)['date_from']
self.company.\
compute_fiscalyear_dates(date_from_date)['date_from']
domain = [('date', '<', fields.Date.to_string(fy_date_from)), domain = [('date', '<', fields.Date.to_string(fy_date_from)),
('user_type_id.include_initial_balance', '=', False)] ('user_type_id.include_initial_balance', '=', False)]
if target_move == 'posted': if target_move == 'posted':
domain.append(('move_id.state', '=', 'posted')) domain.append(('move_id.state', '=', 'posted'))
return expression.normalize_domain(domain) return expression.normalize_domain(domain)
def do_queries(self, company, date_from, date_to,
def do_queries(self, date_from, date_to,
target_move='posted', additional_move_line_filter=None): target_move='posted', additional_move_line_filter=None):
"""Query sums of debit and credit for all accounts and domains """Query sums of debit and credit for all accounts and domains
used in expressions. used in expressions.
This method must be executed after done_parsing(). This method must be executed after done_parsing().
""" """
aml_model = self.env['account.move.line']
aml_model = self.company.env['account.move.line']
# {(domain, mode): {account_id: (debit, credit)}} # {(domain, mode): {account_id: (debit, credit)}}
self._data = defaultdict(dict) self._data = defaultdict(dict)
domain_by_mode = {} domain_by_mode = {}
@ -253,7 +255,7 @@ class AccountingExpressionProcessor(object):
if mode not in domain_by_mode: if mode not in domain_by_mode:
domain_by_mode[mode] = \ domain_by_mode[mode] = \
self.get_aml_domain_for_dates(date_from, date_to, self.get_aml_domain_for_dates(date_from, date_to,
mode, target_move, company)
mode, target_move)
domain = list(domain) + domain_by_mode[mode] domain = list(domain) + domain_by_mode[mode]
domain.append(('account_id', 'in', self._map_account_ids[key])) domain.append(('account_id', 'in', self._map_account_ids[key]))
if additional_move_line_filter: if additional_move_line_filter:
@ -266,7 +268,8 @@ class AccountingExpressionProcessor(object):
debit = acc['debit'] or 0.0 debit = acc['debit'] or 0.0
credit = acc['credit'] or 0.0 credit = acc['credit'] or 0.0
if mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED) and \ if mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED) and \
float_is_zero(debit-credit, precision_rounding=4):
float_is_zero(debit-credit,
precision_rounding=self.dp):
# in initial mode, ignore accounts with 0 balance # in initial mode, ignore accounts with 0 balance
continue continue
self._data[key][acc['account_id'][0]] = (debit, credit) self._data[key][acc['account_id'][0]] = (debit, credit)
@ -311,7 +314,7 @@ class AccountingExpressionProcessor(object):
# as it does not make sense to distinguish 0 from "no data" # as it does not make sense to distinguish 0 from "no data"
if v is not AccountingNone and \ if v is not AccountingNone and \
mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED) and \ mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED) and \
float_is_zero(v, precision_rounding=4):
float_is_zero(v, precision_rounding=self.dp):
v = AccountingNone v = AccountingNone
return '(' + repr(v) + ')' return '(' + repr(v) + ')'
@ -342,7 +345,7 @@ class AccountingExpressionProcessor(object):
# as it does not make sense to distinguish 0 from "no data" # as it does not make sense to distinguish 0 from "no data"
if v is not AccountingNone and \ if v is not AccountingNone and \
mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED) and \ mode in (self.MODE_INITIAL, self.MODE_UNALLOCATED) and \
float_is_zero(v, precision_rounding=4):
float_is_zero(v, precision_rounding=self.dp):
v = AccountingNone v = AccountingNone
return '(' + repr(v) + ')' return '(' + repr(v) + ')'
@ -365,13 +368,13 @@ class AccountingExpressionProcessor(object):
def _get_balances(cls, mode, company, date_from, date_to, def _get_balances(cls, mode, company, date_from, date_to,
target_move='posted'): target_move='posted'):
expr = 'deb{mode}[], crd{mode}[]'.format(mode=mode) expr = 'deb{mode}[], crd{mode}[]'.format(mode=mode)
aep = AccountingExpressionProcessor(company.env)
aep = AccountingExpressionProcessor(company)
# disable smart_end to have the data at once, instead # disable smart_end to have the data at once, instead
# of initial + variation # of initial + variation
aep.smart_end = False aep.smart_end = False
aep.parse_expr(expr) aep.parse_expr(expr)
aep.done_parsing(company)
aep.do_queries(company, date_from, date_to, target_move)
aep.done_parsing()
aep.do_queries(date_from, date_to, target_move)
return aep._data[((), mode)] return aep._data[((), mode)]
@classmethod @classmethod
@ -395,7 +398,7 @@ class AccountingExpressionProcessor(object):
""" A convenience method to obtain the ending balances of all accounts """ A convenience method to obtain the ending balances of all accounts
at a given date. at a given date.
It is the same as get_balances_init(date+1).
It is the same as get_balances_initial(date+1).
:param company: :param company:
:param date: :param date:

7
mis_builder/models/mis_report.py

@ -790,11 +790,11 @@ class MisReport(models.Model):
@api.multi @api.multi
def prepare_aep(self, company): def prepare_aep(self, company):
self.ensure_one() self.ensure_one()
aep = AEP(self.env)
aep = AEP(company)
for kpi in self.kpi_ids: for kpi in self.kpi_ids:
for expression in kpi.expression_ids: for expression in kpi.expression_ids:
aep.parse_expr(expression.name) aep.parse_expr(expression.name)
aep.done_parsing(company)
aep.done_parsing()
return aep return aep
@api.multi @api.multi
@ -915,8 +915,7 @@ class MisReport(models.Model):
additional_move_line_filter = None additional_move_line_filter = None
if get_additional_move_line_filter: if get_additional_move_line_filter:
additional_move_line_filter = get_additional_move_line_filter() additional_move_line_filter = get_additional_move_line_filter()
aep.do_queries(company,
date_from, date_to,
aep.do_queries(date_from, date_to,
target_move, target_move,
additional_move_line_filter) additional_move_line_filter)

5
mis_builder/models/mis_report_instance.py

@ -399,14 +399,13 @@ class MisReportInstance(models.Model):
account_id = arg.get('account_id') account_id = arg.get('account_id')
if period_id and expr and AEP.has_account_var(expr): if period_id and expr and AEP.has_account_var(expr):
period = self.env['mis.report.instance.period'].browse(period_id) period = self.env['mis.report.instance.period'].browse(period_id)
aep = AEP(self.env)
aep = AEP(self.company_id)
aep.parse_expr(expr) aep.parse_expr(expr)
aep.done_parsing(self.company_id)
aep.done_parsing()
domain = aep.get_aml_domain_for_expr( domain = aep.get_aml_domain_for_expr(
expr, expr,
period.date_from, period.date_to, period.date_from, period.date_to,
self.target_move, self.target_move,
self.company_id,
account_id) account_id)
domain.extend(period._get_additional_move_line_filter()) domain.extend(period._get_additional_move_line_filter())
return { return {

7
mis_builder/tests/test_aep.py

@ -66,7 +66,7 @@ class TestAEP(common.TransactionCase):
debit_acc=self.account_ar, debit_acc=self.account_ar,
credit_acc=self.account_in) credit_acc=self.account_in)
# create the AEP, and prepare the expressions we'll need # create the AEP, and prepare the expressions we'll need
self.aep = AEP(self.env)
self.aep = AEP(self.company)
self.aep.parse_expr("bali[]") self.aep.parse_expr("bali[]")
self.aep.parse_expr("bale[]") self.aep.parse_expr("bale[]")
self.aep.parse_expr("balp[]") self.aep.parse_expr("balp[]")
@ -81,7 +81,7 @@ class TestAEP(common.TransactionCase):
self.aep.parse_expr("crdp[700I%]") self.aep.parse_expr("crdp[700I%]")
self.aep.parse_expr("bal_700IN") # deprecated self.aep.parse_expr("bal_700IN") # deprecated
self.aep.parse_expr("bals[700IN]") # deprecated self.aep.parse_expr("bals[700IN]") # deprecated
self.aep.done_parsing(self.company)
self.aep.done_parsing()
def _create_move(self, date, amount, debit_acc, credit_acc): def _create_move(self, date, amount, debit_acc, credit_acc):
move = self.move_model.create({ move = self.move_model.create({
@ -103,8 +103,7 @@ class TestAEP(common.TransactionCase):
self.aep.do_queries( self.aep.do_queries(
date_from=fields.Date.to_string(date_from), date_from=fields.Date.to_string(date_from),
date_to=fields.Date.to_string(date_to), date_to=fields.Date.to_string(date_to),
target_move='posted',
company=self.company)
target_move='posted')
def _eval(self, expr): def _eval(self, expr):
eval_dict = {'AccountingNone': AccountingNone} eval_dict = {'AccountingNone': AccountingNone}

Loading…
Cancel
Save