[IMP] mis_builder: use company currency decimal place in deciding if initial balances are null or 0
parent
189483bba6
commit
53192385fb
|
@ -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):
|
def __init__(self, company):
|
||||||
self.env = env
|
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):
|
def _load_account_codes(self, account_codes):
|
||||||
account_model = self.env['account.account']
|
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,
|
mode, target_move)
|
||||||
company)
|
|
||||||
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.done_parsing()
|
||||||
aep.do_queries(company, date_from, date_to, target_move)
|
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:
|
||||||
|
|
|
@ -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,
|
aep.do_queries(date_from, date_to,
|
||||||
date_from, date_to,
|
|
||||||
target_move,
|
target_move,
|
||||||
additional_move_line_filter)
|
additional_move_line_filter)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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',
|
target_move='posted')
|
||||||
company=self.company)
|
|
||||||
|
|
||||||
def _eval(self, expr):
|
def _eval(self, expr):
|
||||||
eval_dict = {'AccountingNone': AccountingNone}
|
eval_dict = {'AccountingNone': AccountingNone}
|
||||||
|
|
Loading…
Reference in New Issue