Browse Source

[IMP] Refactor improvements using AEP

pull/90/head
Adrien Peiffer (ACSONE) 10 years ago
committed by Stéphane Bidoul
parent
commit
d6f69f0886
  1. 1
      mis_builder/models/__init__.py
  2. 259
      mis_builder/models/mis_builder.py
  3. 2
      mis_builder/static/src/js/mis_builder.js

1
mis_builder/models/__init__.py

@ -23,3 +23,4 @@
############################################################################## ##############################################################################
from . import mis_builder from . import mis_builder
from . import aep

259
mis_builder/models/mis_builder.py

@ -33,22 +33,18 @@ from openerp.tools.safe_eval import safe_eval
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp import tools from openerp import tools
from collections import OrderedDict from collections import OrderedDict
from aep import AccountingExpressionProcessor
from openerp.api import Environment
FUNCTION = [('credit', 'cred'),
FUNCTION = [('credit', 'crd'),
('debit', 'deb'), ('debit', 'deb'),
('balance', 'bal') ('balance', 'bal')
] ]
FUNCTION_LIST = [x[1] for x in FUNCTION] FUNCTION_LIST = [x[1] for x in FUNCTION]
PARAMETERS = ['s', 'i', '']
PARAMETERS_WITHOUT_BLANK = [x for x in PARAMETERS if x != '']
SEPARATOR = '_'
PARAMETERS_STR = ''.join(PARAMETERS)
FUNCTION_CONDITION = '|'.join(PARAMETERS_WITHOUT_BLANK)
PARAMETERS = ['s', 'i', 'e', 'p', '']
class AutoStruct(object): class AutoStruct(object):
@ -90,39 +86,7 @@ def _get_sufix(is_solde=False, is_initial=False):
def _get_prefix(function, is_solde=False, is_initial=False): def _get_prefix(function, is_solde=False, is_initial=False):
return function + _get_sufix(is_solde=is_solde, is_initial=is_initial) \
+ SEPARATOR
def _python_account_var(function, account_code, is_solde=False,
is_initial=False):
prefix = _get_prefix(function, is_solde=is_solde, is_initial=is_initial)
return prefix + re.sub(r'\W', '_', account_code)
# TODO : To review
def _get_account_code(account_var):
res = re.findall(r'_(\d+)', account_var)
assert len(res) == 1
return res[0]
# TODO : Not use here, Check in upstream
# def _get_vars_in_expr(expr, varnames=None):
# if not varnames:
# return []
# varnames_re = r'\b' + r'\b|\b'.join(varnames) + r'\b'
# return re.findall(varnames_re, expr)
def _get_eval_expression(expr, domain_mapping):
domain_list = []
for function in FUNCTION_LIST:
domain_list.extend(re.findall(r'\b%s[%s]?_\w+(\[.+?\])' %
(function, PARAMETERS_STR), expr))
for domain in domain_list:
expr = expr.replace(domain, domain_mapping[domain])
return expr
return function + _get_sufix(is_solde=is_solde, is_initial=is_initial)
# TODO : To review # TODO : To review
@ -131,9 +95,11 @@ def _get_account_vars_in_expr(expr, res_vars, domain_mapping, is_solde=False,
for function in FUNCTION_LIST: for function in FUNCTION_LIST:
prefix = _get_prefix(function, is_solde=is_solde, prefix = _get_prefix(function, is_solde=is_solde,
is_initial=is_initial) is_initial=is_initial)
find_res = re.findall(r'\b%s\w+(?:\[.+?\])?' % prefix, expr)
find_res = re.findall(r'\b%s(?:_[0-9]+|\[.*?\])(?:\[.+?\])?' % prefix,
expr)
for item in find_res: for item in find_res:
match = re.match(r'\b(%s)(\w+)(\[.+?\])?' % prefix, item)
match = re.match(r'\b(%s)(_[0-9]+|\[.*?\])(\[.+?\])?' % prefix,
item)
var_tuple = match.groups() var_tuple = match.groups()
domain = "" if var_tuple[2] is None else var_tuple[2] domain = "" if var_tuple[2] is None else var_tuple[2]
key = "" key = ""
@ -146,22 +112,19 @@ def _get_account_vars_in_expr(expr, res_vars, domain_mapping, is_solde=False,
key_domain = (key, domain) key_domain = (key, domain)
if not res_vars.get(key_domain, False): if not res_vars.get(key_domain, False):
res_vars[key_domain] = set() res_vars[key_domain] = set()
res_vars[key_domain].add(var_tuple[1])
def _get_account_vars_in_report(report, domain_mapping, is_solde=False,
is_initial=False):
res_vars = {}
for kpi in report.kpi_ids:
_get_account_vars_in_expr(kpi.expression, res_vars, domain_mapping,
is_solde, is_initial)
return res_vars
if var_tuple[1].startswith('_'):
account_codes = var_tuple[1][1:]
else:
account_codes = var_tuple[1][1:-1]
account_codes = [a.strip() for a in account_codes.split(',')]
account_codes_set = set(account_codes)
res_vars[key_domain] |= account_codes_set
def _is_valid_python_var(name): def _is_valid_python_var(name):
for item in FUNCTION_LIST: for item in FUNCTION_LIST:
for param in PARAMETERS: for param in PARAMETERS:
if name.startswith(item + param + SEPARATOR):
if name.startswith(item + param):
return False return False
return re.match("[_A-Za-z][_a-zA-Z0-9]*$", name) return re.match("[_A-Za-z][_a-zA-Z0-9]*$", name)
@ -624,64 +587,25 @@ class mis_report_instance_period(orm.Model):
('date', '<=', c.date_to)]) ('date', '<=', c.date_to)])
return domain_list return domain_list
def _fetch_account(self, cr, uid, company_id, account_vars, context=None,
is_solde=False, is_initial=False):
account_obj = self.pool['account.account']
account_move_line_obj = self.pool['account.move.line']
# TODO: use child of company_id?
# first fetch all codes and filter the one we need+
balances = {}
for key_domain, account_code in account_vars.iteritems():
key, domain = key_domain
account_ids = account_obj.search(
cr, uid,
['|', ('company_id', '=', False),
('company_id', '=', company_id),
('code', 'in', list(account_code))],
context=context)
# fetch balances
where_clause = ''
where_clause_params = ()
if domain != '':
domain_eval = safe_eval(domain)
query = account_move_line_obj._where_calc(cr, uid, domain_eval,
context=context)
from_clause, where_clause, where_clause_params = \
query.get_sql()
assert from_clause == '"account_move_line"'
where_clause = where_clause.replace("account_move_line", "l")
where_clause_params = tuple(where_clause_params)
context.update({'query': where_clause,
'query_params': where_clause_params})
account_datas = account_obj\
.read(cr, uid, account_ids,
['code', 'balance', 'credit', 'debit'], context=context)
for account_data in account_datas:
for item in FUNCTION:
var = _python_account_var(item[1], account_data['code'],
is_solde=is_solde,
is_initial=is_initial)
var = var + key
assert key not in balances
balances[var] = account_data[item[0]]
return balances
def _get_context_period(self, cr, uid, report_period, is_solde=False,
is_initial=False, context=None):
context_period = {}
move_obj = self.pool['account.move']
def compute_period_domain(self, cr, uid, period_report, is_solde,
is_initial, context=None):
period_obj = self.pool['account.period'] period_obj = self.pool['account.period']
move_obj = self.pool['account.move']
domain_list = []
target_move = period_report.report_instance_id.target_move
if target_move == 'posted':
domain_list.append(('move_id.state', '=', target_move))
if not is_solde and not is_initial: if not is_solde and not is_initial:
if report_period.period_from:
context_period.\
update({'period_from': report_period.period_from.id,
'period_to': report_period.period_to.id})
else:
context_period.update({'date_from': report_period.date_from,
'date_to': report_period.date_to})
if period_report.period_from:
compute_period_ids = period_obj.build_ctx_periods(
cr, uid, period_report.period_from.id,
period_report.period_to.id)
domain_list.extend([('period_id', 'in', compute_period_ids)])
else: else:
period_to = report_period.period_to
domain_list.extend([('date', '>=', period_report.date_from),
('date', '<=', period_report.date_to)])
elif period_report.period_from and period_report.period_to:
period_to = period_report.period_to
if is_initial: if is_initial:
move_id = move_obj.search( move_id = move_obj.search(
cr, uid, [('period_id.special', '=', False), cr, uid, [('period_id.special', '=', False),
@ -694,7 +618,7 @@ class mis_report_instance_period(orm.Model):
else: else:
computed_period_to = self.pool['account.period'].search( computed_period_to = self.pool['account.period'].search(
cr, uid, [('company_id', '=', cr, uid, [('company_id', '=',
report_period.company_id.id)],
period_report.company_id.id)],
order='date_start desc', limit=1)[0] order='date_start desc', limit=1)[0]
# Change start period to search correctly period from # Change start period to search correctly period from
period_to = period_obj.browse(cr, uid, [computed_period_to], period_to = period_obj.browse(cr, uid, [computed_period_to],
@ -710,80 +634,15 @@ class mis_report_instance_period(orm.Model):
else: else:
computed_period_from = self.pool['account.period'].search( computed_period_from = self.pool['account.period'].search(
cr, uid, [('company_id', '=', cr, uid, [('company_id', '=',
report_period.company_id.id)],
period_report.company_id.id)],
order='date_start', limit=1)[0] order='date_start', limit=1)[0]
context_period.update({'period_from': computed_period_from,
'period_to': period_to.id})
return context_period
def _fetch_balances(self, cr, uid, c, account_vars, context=None):
""" fetch the general account balances for the given period
returns a dictionary {bal_<account.code>: account.balance}
"""
if not account_vars:
return {}
if context is None:
context = {}
search_ctx = dict(context)
search_ctx.update(self._get_context_period(cr, uid, c,
context=context))
# fetch balances
return self._fetch_account(cr, uid, c.company_id.id, account_vars,
search_ctx)
def _fetch_balances_solde(self, cr, uid, c, account_vars, context=None):
""" fetch the general account balances solde at the end of
the given period
the period from is computed by searching the last special period
with journal entries.
If nothing is found, the first period is used.
returns a dictionary {bals_<account.code>: account.balance.solde}
"""
if context is None:
context = {}
balances = {}
if not account_vars:
return balances
search_ctx = dict(context)
if c.period_to:
search_ctx.update(self._get_context_period(cr, uid, c,
is_solde=True,
context=context))
else:
return balances
# fetch balances
return self._fetch_account(cr, uid, c.company_id.id, account_vars,
search_ctx, is_solde=True)
def _fetch_balances_initial(self, cr, uid, c, account_vars, context=None):
if context is None:
context = {}
balances = {}
if not account_vars:
return balances
search_ctx = dict(context)
if c.period_to:
search_ctx.update(self._get_context_period(cr, uid, c,
is_initial=True,
context=context))
else:
return balances
# fetch balances
return self._fetch_account(cr, uid, c.company_id.id, account_vars,
search_ctx, is_initial=True)
compute_period_ids = period_obj.build_ctx_periods(
cr, uid, computed_period_from, period_to.id)
domain_list.extend([('period_id', 'in', compute_period_ids)])
return domain_list
def _fetch_queries(self, cr, uid, c, context): def _fetch_queries(self, cr, uid, c, context):
res = {} res = {}
report = c.report_instance_id.report_id report = c.report_instance_id.report_id
for query in report.query_ids: for query in report.query_ids:
obj = self.pool[query.model_id.model] obj = self.pool[query.model_id.model]
@ -806,11 +665,9 @@ class mis_report_instance_period(orm.Model):
obj_datas = obj.read( obj_datas = obj.read(
cr, uid, obj_ids, field_names, context=context) cr, uid, obj_ids, field_names, context=context)
res[query.name] = [AutoStruct(**d) for d in obj_datas] res[query.name] = [AutoStruct(**d) for d in obj_datas]
return res return res
def _compute(self, cr, uid, lang_id, c, account_vars, accounts_vars,
accounti_vars, domain_mapping, context=None):
def _compute(self, cr, uid, lang_id, c, aep, context=None):
if context is None: if context is None:
context = {} context = {}
@ -826,21 +683,20 @@ class mis_report_instance_period(orm.Model):
'len': len, 'len': len,
'avg': lambda l: sum(l) / float(len(l)), 'avg': lambda l: sum(l) / float(len(l)),
} }
localdict.update(self._fetch_balances(cr, uid, c, account_vars,
context=context))
localdict.update(self._fetch_balances_solde(cr, uid, c, accounts_vars,
context=context))
localdict.update(self._fetch_balances_initial(cr, uid, c,
accounti_vars,
context=context))
domain_p = self.compute_period_domain(cr, uid, c, False, False,
context=context)
domain_s = self.compute_period_domain(cr, uid, c, True, False,
context=context)
domain_i = self.compute_period_domain(cr, uid, c, False, True,
context=context)
aep.do_queries(domain_p, domain_i, domain_s)
localdict.update(self._fetch_queries(cr, uid, c, localdict.update(self._fetch_queries(cr, uid, c,
context=context)) context=context))
for kpi in c.report_instance_id.report_id.kpi_ids: for kpi in c.report_instance_id.report_id.kpi_ids:
try: try:
kpi_eval_expression = _get_eval_expression(kpi.expression,
domain_mapping)
kpi_val_comment = kpi.expression kpi_val_comment = kpi.expression
kpi_eval_expression = aep.replace_expr(kpi.expression)
kpi_val = safe_eval(kpi_eval_expression, localdict) kpi_val = safe_eval(kpi_eval_expression, localdict)
except ZeroDivisionError: except ZeroDivisionError:
kpi_val = None kpi_val = None
@ -978,28 +834,20 @@ class mis_report_instance(orm.Model):
# empty line name for header # empty line name for header
header = OrderedDict() header = OrderedDict()
header[''] = {'kpi_name': '', 'cols': [], 'default_style': ''} header[''] = {'kpi_name': '', 'cols': [], 'default_style': ''}
env = Environment(cr, uid, {})
aep = AccountingExpressionProcessor(env)
# initialize lines with kpi # initialize lines with kpi
for kpi in r.report_id.kpi_ids: for kpi in r.report_id.kpi_ids:
aep.parse_expr(kpi.expression)
content[kpi.name] = {'kpi_name': kpi.description, content[kpi.name] = {'kpi_name': kpi.description,
'cols': [], 'cols': [],
'default_style': ''} 'default_style': ''}
aep.done_parsing([('company_id', '=', r.company_id.id)])
report_instance_period_obj = self.pool.get( report_instance_period_obj = self.pool.get(
'mis.report.instance.period') 'mis.report.instance.period')
kpi_obj = self.pool.get('mis.report.kpi') kpi_obj = self.pool.get('mis.report.kpi')
period_values = {} period_values = {}
domain_mapping = {}
account_vars = _get_account_vars_in_report(r.report_id,
domain_mapping)
accounts_vars = _get_account_vars_in_report(r.report_id,
domain_mapping,
is_solde=True)
accounti_vars = _get_account_vars_in_report(r.report_id,
domain_mapping,
is_initial=True)
lang = self.pool['res.users'].read( lang = self.pool['res.users'].read(
cr, uid, uid, ['lang'], context=context)['lang'] cr, uid, uid, ['lang'], context=context)['lang']
lang_id = self.pool['res.lang'].search( lang_id = self.pool['res.lang'].search(
@ -1021,8 +869,7 @@ class mis_report_instance(orm.Model):
period.date_from)) period.date_from))
# compute kpi values # compute kpi values
values = report_instance_period_obj._compute( values = report_instance_period_obj._compute(
cr, uid, lang_id, period, account_vars, accounts_vars,
accounti_vars, domain_mapping, context=context)
cr, uid, lang_id, period, aep, context=context)
period_values[period.name] = values period_values[period.name] = values
for key in values: for key in values:
content[key]['default_style'] = values[key]['default_style'] content[key]['default_style'] = values[key]['default_style']

2
mis_builder/static/src/js/mis_builder.js

@ -29,7 +29,7 @@ openerp.mis_builder = function(instance) {
var period_id = JSON.parse($(event.target).data("period-id")); var period_id = JSON.parse($(event.target).data("period-id"));
var period_name = JSON.parse($(event.target).data("period-name")); var period_name = JSON.parse($(event.target).data("period-name"));
var self = this; var self = this;
if (!(val === null) && (val_c.indexOf('bal_') >=0)){
if (!(val === null) && (val_c.indexOf('bal') >=0)){
new instance.web.Model("mis.report.instance.period").call( new instance.web.Model("mis.report.instance.period").call(
"compute_domain", "compute_domain",
[period_id, val_c], [period_id, val_c],

Loading…
Cancel
Save