|
|
@ -56,7 +56,8 @@ def _utc_midnight(d, tz_name, add_day=0): |
|
|
|
utc_tz = pytz.timezone('UTC') |
|
|
|
context_tz = pytz.timezone(tz_name) |
|
|
|
local_timestamp = context_tz.localize(d, is_dst=False) |
|
|
|
return datetime.strftime(local_timestamp.astimezone(utc_tz), tools.DEFAULT_SERVER_DATETIME_FORMAT) |
|
|
|
return datetime.strftime(local_timestamp.astimezone(utc_tz), |
|
|
|
tools.DEFAULT_SERVER_DATETIME_FORMAT) |
|
|
|
|
|
|
|
|
|
|
|
def _clean(varStr): |
|
|
@ -64,6 +65,7 @@ def _clean(varStr): |
|
|
|
|
|
|
|
|
|
|
|
class mis_report_kpi(orm.Model): |
|
|
|
|
|
|
|
""" A KPI is an element of a MIS report. |
|
|
|
|
|
|
|
In addition to a name and description, it has an expression |
|
|
@ -132,10 +134,13 @@ class mis_report_kpi(orm.Model): |
|
|
|
# check it is a valid python identifier |
|
|
|
res = {} |
|
|
|
if name and not re.match("[_A-Za-z][_a-zA-Z0-9]*$", name): |
|
|
|
res['warning'] = {'title': 'Invalid name', 'message': 'The name must be a valid python identifier'} |
|
|
|
res['warning'] = { |
|
|
|
'title': 'Invalid name', |
|
|
|
'message': 'The name must be a valid python identifier'} |
|
|
|
return res |
|
|
|
|
|
|
|
def onchange_description(self, cr, uid, ids, description, name, context=None): |
|
|
|
def onchange_description(self, cr, uid, ids, description, name, |
|
|
|
context=None): |
|
|
|
# construct name from description |
|
|
|
res = {} |
|
|
|
if description and not name: |
|
|
@ -155,47 +160,60 @@ class mis_report_kpi(orm.Model): |
|
|
|
def _render(self, cr, uid, kpi, value, context=None): |
|
|
|
""" render a KPI value as a unicode string, ready for display """ |
|
|
|
if kpi.type == 'num': |
|
|
|
return self._render_num(cr, uid, value, kpi.divider, kpi.dp, kpi.suffix, context=context) |
|
|
|
return self._render_num(cr, uid, value, kpi.divider, kpi.dp, |
|
|
|
kpi.suffix, context=context) |
|
|
|
elif kpi.type == 'pct': |
|
|
|
return self._render_num(cr, uid, value, 0.01, kpi.dp, '%', context=context) |
|
|
|
return self._render_num(cr, uid, value, 0.01, kpi.dp, '%', |
|
|
|
context=context) |
|
|
|
else: |
|
|
|
return unicode(value) |
|
|
|
|
|
|
|
def _render_comparison(self, cr, uid, kpi, value, base_value, average_value, average_base_value, context=None): |
|
|
|
def _render_comparison(self, cr, uid, kpi, value, base_value, |
|
|
|
average_value, average_base_value, context=None): |
|
|
|
""" render the comparison of two KPI values, ready for display """ |
|
|
|
if value is None or base_value is None: |
|
|
|
return '' |
|
|
|
if kpi.type == 'pct': |
|
|
|
return self._render_num(cr, uid, value - base_value, 0.01, kpi.dp, _('pp'), sign='+', context=context) |
|
|
|
return self._render_num(cr, uid, value - base_value, 0.01, kpi.dp, |
|
|
|
_('pp'), sign='+', context=context) |
|
|
|
elif kpi.type == 'num': |
|
|
|
if average_value: |
|
|
|
value = value / float(average_value) |
|
|
|
if average_base_value: |
|
|
|
base_value = base_value / float(average_base_value) |
|
|
|
if kpi.compare_method == 'diff': |
|
|
|
return self._render_num(cr, uid, value - base_value, kpi.divider, |
|
|
|
kpi.dp, kpi.suffix, sign='+', context=context) |
|
|
|
return self._render_num(cr, uid, value - base_value, |
|
|
|
kpi.divider, |
|
|
|
kpi.dp, kpi.suffix, sign='+', |
|
|
|
context=context) |
|
|
|
elif kpi.compare_method == 'pct' and base_value != 0: |
|
|
|
return self._render_num(cr, uid, value / base_value - 1, |
|
|
|
0.01, kpi.dp, '%', |
|
|
|
sign='+', context=context) |
|
|
|
return '' |
|
|
|
|
|
|
|
def _render_num(self, cr, uid, value, divider, dp, suffix, sign='-', context=None): |
|
|
|
divider_label = _get_selection_label(self._columns['divider'].selection, divider) |
|
|
|
def _render_num(self, cr, uid, value, divider, dp, suffix, sign='-', |
|
|
|
context=None): |
|
|
|
divider_label = _get_selection_label( |
|
|
|
self._columns['divider'].selection, divider) |
|
|
|
if divider_label == '1': |
|
|
|
divider_label = '' |
|
|
|
# format number following user language |
|
|
|
lang = self.pool['res.users'].read(cr, uid, uid, ['lang'], context=context)['lang'] |
|
|
|
language_id = self.pool['res.lang'].search(cr, uid, [('code', '=', lang)], context=context) |
|
|
|
lang = self.pool['res.users'].read( |
|
|
|
cr, uid, uid, ['lang'], context=context)['lang'] |
|
|
|
language_id = self.pool['res.lang'].search( |
|
|
|
cr, uid, [('code', '=', lang)], context=context) |
|
|
|
value = round(value / float(divider or 1), dp) or 0 |
|
|
|
return '%s %s%s' % (self.pool['res.lang'].format(cr, uid, language_id, |
|
|
|
'%%%s.%df' % (sign, dp), |
|
|
|
value, context=context), |
|
|
|
'%%%s.%df' % ( |
|
|
|
sign, dp), |
|
|
|
value, |
|
|
|
context=context), |
|
|
|
divider_label, suffix or '') |
|
|
|
|
|
|
|
|
|
|
|
class mis_report_query(orm.Model): |
|
|
|
|
|
|
|
""" A query to fetch data for a MIS report. |
|
|
|
|
|
|
|
A query works on a model and has a domain and list of fields to fetch. |
|
|
@ -216,7 +234,10 @@ class mis_report_query(orm.Model): |
|
|
|
def onchange_field_ids(self, cr, uid, ids, field_ids, context=None): |
|
|
|
# compute field_names |
|
|
|
field_name = [] |
|
|
|
for field in self.pool.get('ir.model.fields').read(cr, uid, field_ids[0][2], ['name'], context=context): |
|
|
|
for field in self.pool.get('ir.model.fields').read(cr, uid, |
|
|
|
field_ids[0][2], |
|
|
|
['name'], |
|
|
|
context=context): |
|
|
|
field_name.append(field['name']) |
|
|
|
return {'value': {'field_name': ', '.join(field_name)}} |
|
|
|
|
|
|
@ -227,12 +248,15 @@ class mis_report_query(orm.Model): |
|
|
|
string='Model'), |
|
|
|
'field_ids': fields.many2many('ir.model.fields', required=True, |
|
|
|
string='Fields to fetch'), |
|
|
|
'field_name': fields.function(_get_field_names, type='char', string='Fetched fields name', |
|
|
|
'field_name': fields.function(_get_field_names, type='char', |
|
|
|
string='Fetched fields name', |
|
|
|
store={'mis.report.query': |
|
|
|
(lambda self, cr, uid, ids, c={}: ids, ['field_ids'], 20), }), |
|
|
|
(lambda self, cr, uid, ids, c={}: |
|
|
|
ids, ['field_ids'], 20), }), |
|
|
|
'date_field': fields.many2one('ir.model.fields', required=True, |
|
|
|
string='Date field', |
|
|
|
domain=[('ttype', 'in', ('date', 'datetime'))]), |
|
|
|
domain=[('ttype', 'in', ('date', |
|
|
|
'datetime'))]), |
|
|
|
'domain': fields.char(string='Domain'), |
|
|
|
'report_id': fields.many2one('mis.report', string='Report'), |
|
|
|
} |
|
|
@ -251,6 +275,7 @@ class mis_report_query(orm.Model): |
|
|
|
|
|
|
|
|
|
|
|
class mis_report(orm.Model): |
|
|
|
|
|
|
|
""" A MIS report template (without period information) |
|
|
|
|
|
|
|
The MIS report holds: |
|
|
@ -284,19 +309,24 @@ class mis_report(orm.Model): |
|
|
|
if line[0] == 0: |
|
|
|
line[2]['sequence'] = idx + 1 |
|
|
|
else: |
|
|
|
mis_report_kpi_obj.write(cr, uid, [line[1]], {'sequence': idx + 1}, context=context) |
|
|
|
mis_report_kpi_obj.write( |
|
|
|
cr, uid, [line[1]], {'sequence': idx + 1}, |
|
|
|
context=context) |
|
|
|
return super(mis_report, self).create(cr, uid, vals, context=context) |
|
|
|
|
|
|
|
def write(self, cr, uid, ids, vals, context=None): |
|
|
|
res = super(mis_report, self).write(cr, uid, ids, vals, context=context) |
|
|
|
res = super(mis_report, self).write( |
|
|
|
cr, uid, ids, vals, context=context) |
|
|
|
mis_report_kpi_obj = self.pool.get('mis.report.kpi') |
|
|
|
for report in self.browse(cr, uid, ids, context): |
|
|
|
for idx, kpi in enumerate(report.kpi_ids): |
|
|
|
mis_report_kpi_obj.write(cr, uid, [kpi.id], {'sequence': idx + 1}, context=context) |
|
|
|
mis_report_kpi_obj.write( |
|
|
|
cr, uid, [kpi.id], {'sequence': idx + 1}, context=context) |
|
|
|
return res |
|
|
|
|
|
|
|
|
|
|
|
class mis_report_instance_period(orm.Model): |
|
|
|
|
|
|
|
""" A MIS report instance has the logic to compute |
|
|
|
a report template for a give date period. |
|
|
|
|
|
|
@ -313,34 +343,45 @@ class mis_report_instance_period(orm.Model): |
|
|
|
if c.type == 'd': |
|
|
|
date_from = d + timedelta(days=c.offset) |
|
|
|
date_to = date_from + timedelta(days=c.duration - 1) |
|
|
|
date_from = date_from.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) |
|
|
|
date_from = date_from.strftime( |
|
|
|
tools.DEFAULT_SERVER_DATE_FORMAT) |
|
|
|
date_to = date_to.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) |
|
|
|
period_ids = None |
|
|
|
elif c.type == 'w': |
|
|
|
date_from = d - timedelta(d.weekday()) |
|
|
|
date_from = date_from + timedelta(days=c.offset * 7) |
|
|
|
date_to = date_from + timedelta(days=(7 * c.duration) - 1) |
|
|
|
date_from = date_from.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) |
|
|
|
date_from = date_from.strftime( |
|
|
|
tools.DEFAULT_SERVER_DATE_FORMAT) |
|
|
|
date_to = date_to.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) |
|
|
|
period_ids = None |
|
|
|
elif c.type == 'fp': |
|
|
|
period_obj = self.pool['account.period'] |
|
|
|
all_period_ids = period_obj.search(cr, uid, |
|
|
|
all_period_ids = period_obj.search( |
|
|
|
cr, uid, |
|
|
|
[('special', '=', False), |
|
|
|
'|', ('company_id', '=', False), |
|
|
|
('company_id', '=', c.company_id.id)], |
|
|
|
'|', ( |
|
|
|
'company_id', '=', |
|
|
|
False), |
|
|
|
('company_id', '=', |
|
|
|
c.company_id.id)], |
|
|
|
order='date_start', |
|
|
|
context=context) |
|
|
|
current_period_ids = period_obj.search(cr, uid, |
|
|
|
current_period_ids = period_obj.search( |
|
|
|
cr, uid, |
|
|
|
[('special', '=', False), |
|
|
|
('date_start', '<=', d), |
|
|
|
('date_stop', '>=', d), |
|
|
|
'|', ('company_id', '=', False), |
|
|
|
('date_stop', |
|
|
|
'>=', d), |
|
|
|
'|', |
|
|
|
('company_id', |
|
|
|
'=', False), |
|
|
|
('company_id', '=', c.company_id.id)], |
|
|
|
context=context) |
|
|
|
if not current_period_ids: |
|
|
|
raise orm.except_orm(_("Error!"), |
|
|
|
_("No current fiscal period for %s") % d) |
|
|
|
_("No current fiscal period for %s") |
|
|
|
% d) |
|
|
|
p = all_period_ids.index(current_period_ids[0]) + c.offset |
|
|
|
if p < 0 or p >= len(all_period_ids): |
|
|
|
raise orm.except_orm(_("Error!"), |
|
|
@ -396,10 +437,15 @@ class mis_report_instance_period(orm.Model): |
|
|
|
'sequence': fields.integer(string='Sequence'), |
|
|
|
'report_instance_id': fields.many2one('mis.report.instance', |
|
|
|
string='Report Instance'), |
|
|
|
'comparison_column_ids': fields.many2many('mis.report.instance.period', 'mis_report_instance_period_rel', |
|
|
|
'period_id', 'compare_period_id', string='Compare with'), |
|
|
|
'comparison_column_ids': fields.many2many( |
|
|
|
'mis.report.instance.period', |
|
|
|
'mis_report_instance_period_rel', |
|
|
|
'period_id', |
|
|
|
'compare_period_id', |
|
|
|
string='Compare with'), |
|
|
|
'company_id': fields.many2one('res.company', 'Company', required=True), |
|
|
|
'normalize_factor': fields.integer(string='Factor', |
|
|
|
'normalize_factor': fields.integer( |
|
|
|
string='Factor', |
|
|
|
help='Factor to use to normalize the period (used in comparison'), |
|
|
|
} |
|
|
|
|
|
|
@ -408,7 +454,9 @@ class mis_report_instance_period(orm.Model): |
|
|
|
'duration': 1, |
|
|
|
'sequence': 100, |
|
|
|
'normalize_factor': 1, |
|
|
|
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, |
|
|
|
'company_id': lambda s, cr, uid, c: |
|
|
|
s.pool.get('res.company')._company_default_get( |
|
|
|
cr, uid, |
|
|
|
'mis.report.instance.period', |
|
|
|
context=c) |
|
|
|
} |
|
|
@ -416,9 +464,12 @@ class mis_report_instance_period(orm.Model): |
|
|
|
_order = 'sequence' |
|
|
|
|
|
|
|
_sql_constraints = [ |
|
|
|
('duration', 'CHECK (duration>0)', 'Wrong duration, it must be positive!'), |
|
|
|
('normalize_factor', 'CHECK (normalize_factor>0)', 'Wrong normalize factor, it must be positive!'), |
|
|
|
('name_unique', 'unique(name, report_instance_id)', 'Period name should be unique by report'), |
|
|
|
('duration', 'CHECK (duration>0)', |
|
|
|
'Wrong duration, it must be positive!'), |
|
|
|
('normalize_factor', 'CHECK (normalize_factor>0)', |
|
|
|
'Wrong normalize factor, it must be positive!'), |
|
|
|
('name_unique', 'unique(name, report_instance_id)', |
|
|
|
'Period name should be unique by report'), |
|
|
|
] |
|
|
|
|
|
|
|
def _fetch_balances(self, cr, uid, c, context=None): |
|
|
@ -439,8 +490,11 @@ class mis_report_instance_period(orm.Model): |
|
|
|
'date_to': c.date_to}) |
|
|
|
|
|
|
|
# TODO: initial balance? |
|
|
|
account_ids = account_obj.search(cr, uid, ['|', ('company_id', '=', False), ('company_id', '=', c.company_id.id)], context=context) |
|
|
|
account_datas = account_obj.read(cr, uid, account_ids, ['code', 'balance'], context=search_ctx) |
|
|
|
account_ids = account_obj.search(cr, uid, ['|', ('company_id', '=', |
|
|
|
False), ( |
|
|
|
'company_id', '=', c.company_id.id)], context=context) |
|
|
|
account_datas = account_obj.read( |
|
|
|
cr, uid, account_ids, ['code', 'balance'], context=search_ctx) |
|
|
|
balances = {} |
|
|
|
for account_data in account_datas: |
|
|
|
key = 'bal' + _clean(account_data['code']) |
|
|
@ -460,15 +514,20 @@ class mis_report_instance_period(orm.Model): |
|
|
|
domain.extend([(query.date_field.name, '>=', c.date_from), |
|
|
|
(query.date_field.name, '<=', c.date_to)]) |
|
|
|
else: |
|
|
|
datetime_from = _utc_midnight(c.date_from, context.get('tz', 'UTC')) |
|
|
|
datetime_to = _utc_midnight(c.date_to, context.get('tz', 'UTC'), add_day=1) |
|
|
|
datetime_from = _utc_midnight( |
|
|
|
c.date_from, context.get('tz', 'UTC')) |
|
|
|
datetime_to = _utc_midnight( |
|
|
|
c.date_to, context.get('tz', 'UTC'), add_day=1) |
|
|
|
domain.extend([(query.date_field.name, '>=', datetime_from), |
|
|
|
(query.date_field.name, '<', datetime_to)]) |
|
|
|
if obj._columns.get('company_id', False): |
|
|
|
domain.extend(['|', ('company_id', '=', False), ('company_id', '=', c.company_id.id)]) |
|
|
|
domain.extend( |
|
|
|
['|', ('company_id', '=', False), ('company_id', '=', |
|
|
|
c.company_id.id)]) |
|
|
|
field_names = [field.name for field in query.field_ids] |
|
|
|
obj_ids = obj.search(cr, uid, domain, context=context) |
|
|
|
obj_datas = obj.read(cr, uid, obj_ids, field_names, context=context) |
|
|
|
obj_datas = obj.read( |
|
|
|
cr, uid, obj_ids, field_names, context=context) |
|
|
|
res[query.name] = [AutoStruct(**d) for d in obj_datas] |
|
|
|
|
|
|
|
return res |
|
|
@ -504,7 +563,8 @@ class mis_report_instance_period(orm.Model): |
|
|
|
kpi_val_rendered = '#ERR' |
|
|
|
kpi_val_comment = traceback.format_exc() |
|
|
|
else: |
|
|
|
kpi_val_rendered = kpi_obj._render(cr, uid, kpi, kpi_val, context=context) |
|
|
|
kpi_val_rendered = kpi_obj._render( |
|
|
|
cr, uid, kpi, kpi_val, context=context) |
|
|
|
kpi_val_comment = None |
|
|
|
|
|
|
|
localdict[kpi.name] = kpi_val |
|
|
@ -529,6 +589,7 @@ class mis_report_instance_period(orm.Model): |
|
|
|
|
|
|
|
|
|
|
|
class mis_report_instance(orm.Model): |
|
|
|
|
|
|
|
""" The MIS report instance combines compute and |
|
|
|
display a MIS report template for a set of periods """ |
|
|
|
|
|
|
@ -573,28 +634,42 @@ class mis_report_instance(orm.Model): |
|
|
|
|
|
|
|
def create(self, cr, uid, vals, context=None): |
|
|
|
if 'period_ids' in vals: |
|
|
|
mis_report_instance_period_obj = self.pool.get('mis.report.instance.period') |
|
|
|
mis_report_instance_period_obj = self.pool.get( |
|
|
|
'mis.report.instance.period') |
|
|
|
for idx, line in enumerate(vals['period_ids']): |
|
|
|
if line[0] == 0: |
|
|
|
line[2]['sequence'] = idx + 1 |
|
|
|
else: |
|
|
|
mis_report_instance_period_obj.write(cr, uid, [line[1]], {'sequence': idx + 1}, context=context) |
|
|
|
return super(mis_report_instance, self).create(cr, uid, vals, context=context) |
|
|
|
mis_report_instance_period_obj.write( |
|
|
|
cr, uid, [line[1]], {'sequence': idx + 1}, |
|
|
|
context=context) |
|
|
|
return super(mis_report_instance, self).create(cr, uid, vals, |
|
|
|
context=context) |
|
|
|
|
|
|
|
def write(self, cr, uid, ids, vals, context=None): |
|
|
|
res = super(mis_report_instance, self).write(cr, uid, ids, vals, context=context) |
|
|
|
mis_report_instance_period_obj = self.pool.get('mis.report.instance.period') |
|
|
|
res = super(mis_report_instance, self).write( |
|
|
|
cr, uid, ids, vals, context=context) |
|
|
|
mis_report_instance_period_obj = self.pool.get( |
|
|
|
'mis.report.instance.period') |
|
|
|
for instance in self.browse(cr, uid, ids, context): |
|
|
|
for idx, period in enumerate(instance.period_ids): |
|
|
|
mis_report_instance_period_obj.write(cr, uid, [period.id], {'sequence': idx + 1}, context=context) |
|
|
|
mis_report_instance_period_obj.write( |
|
|
|
cr, uid, [period.id], {'sequence': idx + 1}, |
|
|
|
context=context) |
|
|
|
return res |
|
|
|
|
|
|
|
def _format_date(self, cr, uid, date, context=None): |
|
|
|
# format date following user language |
|
|
|
lang = self.pool['res.users'].read(cr, uid, uid, ['lang'], context=context)['lang'] |
|
|
|
language_id = self.pool['res.lang'].search(cr, uid, [('code', '=', lang)], context=context)[0] |
|
|
|
tformat = self.pool['res.lang'].read(cr, uid, language_id, ['date_format'])['date_format'] |
|
|
|
return datetime.strftime(datetime.strptime(date, tools.DEFAULT_SERVER_DATE_FORMAT), tformat) |
|
|
|
lang = self.pool['res.users'].read( |
|
|
|
cr, uid, uid, ['lang'], context=context)['lang'] |
|
|
|
language_id = self.pool['res.lang'].search( |
|
|
|
cr, uid, [('code', '=', lang)], context=context)[0] |
|
|
|
tformat = self.pool['res.lang'].read( |
|
|
|
cr, uid, language_id, ['date_format'])['date_format'] |
|
|
|
return datetime.strftime(datetime.strptime( |
|
|
|
date, |
|
|
|
tools.DEFAULT_SERVER_DATE_FORMAT), |
|
|
|
tformat) |
|
|
|
|
|
|
|
def compute(self, cr, uid, _ids, context=None): |
|
|
|
assert isinstance(_ids, (int, long)) |
|
|
@ -604,31 +679,37 @@ class mis_report_instance(orm.Model): |
|
|
|
context['state'] = r.target_move |
|
|
|
|
|
|
|
content = OrderedDict() |
|
|
|
#empty line name for header |
|
|
|
# empty line name for header |
|
|
|
header = OrderedDict() |
|
|
|
header[''] = {'kpi_name': '', 'cols': []} |
|
|
|
|
|
|
|
#initialize lines with kpi |
|
|
|
# initialize lines with kpi |
|
|
|
for kpi in r.report_id.kpi_ids: |
|
|
|
content[kpi.name] = {'kpi_name': kpi.description, 'cols': []} |
|
|
|
|
|
|
|
report_instance_period_obj = self.pool.get('mis.report.instance.period') |
|
|
|
report_instance_period_obj = self.pool.get( |
|
|
|
'mis.report.instance.period') |
|
|
|
kpi_obj = self.pool.get('mis.report.kpi') |
|
|
|
|
|
|
|
period_values = {} |
|
|
|
|
|
|
|
for period in r.period_ids: |
|
|
|
# add the column header |
|
|
|
header['']['cols'].append(dict(name=period.name, |
|
|
|
header['']['cols'].append(dict( |
|
|
|
name=period.name, |
|
|
|
date=(period.duration > 1 or period.type == 'w') and |
|
|
|
_('from %s to %s' % |
|
|
|
(period.period_from and period.period_from.name |
|
|
|
or self._format_date(cr, uid, period.date_from, context=context), |
|
|
|
or self._format_date(cr, uid, period.date_from, |
|
|
|
context=context), |
|
|
|
period.period_to and period.period_to.name |
|
|
|
or self._format_date(cr, uid, period.date_to, context=context))) |
|
|
|
or period.period_from and period.period_from.name or period.date_from)) |
|
|
|
or self._format_date(cr, uid, period.date_to, |
|
|
|
context=context))) |
|
|
|
or period.period_from and period.period_from.name or |
|
|
|
period.date_from)) |
|
|
|
# compute kpi values |
|
|
|
values = report_instance_period_obj._compute(cr, uid, period, context=context) |
|
|
|
values = report_instance_period_obj._compute( |
|
|
|
cr, uid, period, context=context) |
|
|
|
period_values[period.name] = values |
|
|
|
for key in values: |
|
|
|
content[key]['cols'].append(values[key]) |
|
|
@ -637,14 +718,22 @@ class mis_report_instance(orm.Model): |
|
|
|
for period in r.period_ids: |
|
|
|
for compare_col in period.comparison_column_ids: |
|
|
|
# add the column header |
|
|
|
header['']['cols'].append(dict(name='%s - %s' % (period.name, compare_col.name), date='')) |
|
|
|
header['']['cols'].append( |
|
|
|
dict(name='%s - %s' % (period.name, compare_col.name), |
|
|
|
date='')) |
|
|
|
column1_values = period_values[period.name] |
|
|
|
column2_values = period_values[compare_col.name] |
|
|
|
for kpi in r.report_id.kpi_ids: |
|
|
|
content[kpi.name]['cols'].append({'val_r': kpi_obj._render_comparison(cr, uid, kpi, |
|
|
|
content[kpi.name]['cols'].append( |
|
|
|
{'val_r': kpi_obj._render_comparison( |
|
|
|
cr, |
|
|
|
uid, |
|
|
|
kpi, |
|
|
|
column1_values[kpi.name]['val'], |
|
|
|
column2_values[kpi.name]['val'], |
|
|
|
period.normalize_factor, compare_col.normalize_factor, context=context)}) |
|
|
|
period.normalize_factor, |
|
|
|
compare_col.normalize_factor, |
|
|
|
context=context)}) |
|
|
|
|
|
|
|
return {'header': header, |
|
|
|
'content': content} |