Browse Source

Add demo data. Set computed result. In instance add target_move (all or posted). Add onchange and check function on report

pull/86/head
Laetitia Gangloff 11 years ago
committed by Stéphane Bidoul
parent
commit
c9bb2c64c3
  1. 6
      mis_builder/__openerp__.py
  2. 97
      mis_builder/models/mis_builder.py
  3. 19
      mis_builder/static/src/xml/mis_widget.xml
  4. 2
      mis_builder/tests/mis.report.csv
  5. 2
      mis_builder/tests/mis.report.instance.csv
  6. 7
      mis_builder/tests/mis.report.instance.period.csv
  7. 7
      mis_builder/tests/mis.report.kpi.csv
  8. 2
      mis_builder/tests/mis.report.query.csv
  9. 14
      mis_builder/views/mis_builder.xml

6
mis_builder/__openerp__.py

@ -36,7 +36,11 @@
],
'test': [
],
'demo': [
'demo': ['tests/mis.report.kpi.csv',
'tests/mis.report.query.csv',
'tests/mis.report.csv',
'tests/mis.report.instance.period.csv',
'tests/mis.report.instance.csv',
],
'js': [
'static/src/js/*.js'

97
mis_builder/models/mis_builder.py

@ -26,6 +26,7 @@ from datetime import datetime, timedelta
from dateutil import parser
import traceback
from lxml import etree
import re
from openerp.osv import orm, fields
from openerp.tools.safe_eval import safe_eval
@ -76,11 +77,12 @@ class mis_report_kpi(orm.Model):
string='Type'),
'divider': fields.selection([('1e-6', _('µ')),
('1e-3', _('m')),
('1', _('1')),
('1e3', _('k')),
('1e6', _('M'))],
string='Factor'),
'dp': fields.integer(string='Rounding'),
'suffix': fields.char(size=16, string='Unit'),
'suffix': fields.char(size=16, string='Suffix'),
'compare_method': fields.selection([('diff', _('Difference')),
('pct', _('Percentage')),
('none', _('None'))],
@ -99,9 +101,40 @@ class mis_report_kpi(orm.Model):
_order = 'sequence'
# TODO: constraint to check name is a valid python identifier
# TODO: onchange type pct -> force comparison method = diff
# TODO: onchange type str -> divider, dp, suffix, compare_method read only
def _check_name(self, cr, uid, ids, context=None):
for record_name in self.read(cr, uid, ids, ['name']):
if not re.match("[_A-Za-z][_a-zA-Z0-9]*$", record_name['name']):
return False
return True
_constraints = [
(_check_name, 'The name must be a valid python identifier', ['name']),
]
def onchange_name(self, cr, uid, ids, name, context=None):
# 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'}
return res
def onchange_description(self, cr, uid, ids, description, context=None):
# construct name from description
clean = lambda varStr: re.sub('\W|^(?=\d)', '_', varStr)
res = {}
if description:
res = {'value': {'name': clean(description)}}
return res
def onchange_type(self, cr, uid, ids, kpi_type, context=None):
res = {}
if kpi_type == 'pct':
res['value'] = {'compare_method': 'diff'}
elif kpi_type == 'str':
res['value'] = {'compare_method': 'none',
'divider': '',
'dp': 0}
return res
def _render(self, kpi, value):
""" render a KPI value as a unicode string, ready for display """
@ -207,6 +240,11 @@ class mis_report_instance_period(orm.Model):
if isinstance(ids, (int, long)):
ids = [ids]
res = {}
company_id = context.get('force_company')
if not company_id:
user = self.pool.get('res.users').read(cr, uid, uid, ['company_id'], context=context)
if user['company_id']:
company_id = user['company_id'][0]
for c in self.browse(cr, uid, ids, context=context):
d = parser.parse(c.report_instance_id.pivot_date)
if c.type == 'd':
@ -223,17 +261,17 @@ class mis_report_instance_period(orm.Model):
date_to = date_to.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
period_ids = None
elif c.type == 'fp':
# TODO: filter on company_id
# TODO: date!
period_obj = self.pool['account.period']
all_period_ids = period_obj.search(cr, uid,
[('special', '=', False)],
[('special', '=', False), ('company_id', '=', company_id)],
order='date_start',
context=context)
current_period_ids = period_obj.search(cr, uid,
[('special', '=', False),
('date_start', '<=', d),
('date_stop', '>=', d)],
('date_stop', '>=', d),
('company_id', '=', company_id)],
context=context)
if not current_period_ids:
raise orm.except_orm(_("Error!"),
@ -264,7 +302,7 @@ class mis_report_instance_period(orm.Model):
_columns = {
'name': fields.char(size=32, required=True,
string='Name', translate=True),
string='Description', translate=True),
'type': fields.selection([('d', _('Day')),
('w', _('Week')),
('fp', _('Fiscal Period')),
@ -296,19 +334,23 @@ class mis_report_instance_period(orm.Model):
}
_defaults = {
'offset':-1,
'offset': -1,
'duration': 1,
}
_order = 'sequence'
# TODO: constraint duration >= 1
_sql_constraints = [
('duration', 'CHECK (duration>0)', 'Wrong duration, it must be positive!')
]
def _fetch_balances(self, cr, uid, c, context=None):
""" fetch the general account balances for the given period
returns a dictionary {bal_<account.code>: account.balance}
"""
if context is None:
context = {}
account_obj = self.pool['account.account']
search_ctx = dict(context)
@ -320,8 +362,12 @@ class mis_report_instance_period(orm.Model):
'date_to': c.date_to})
# TODO: initial balance?
# TODO: draft or posted?
account_ids = account_obj.search(cr, uid, [])
company_id = context.get('force_company')
if not company_id:
user = self.pool.get('res.users').read(cr, uid, uid, ['company_id'], context=context)
if user['company_id']:
company_id = user['company_id'][0]
account_ids = account_obj.search(cr, uid, [('company_id', '=', company_id)], context=context)
account_datas = account_obj.read(cr, uid, account_ids,
['code', 'balance'],
context=search_ctx)
@ -340,6 +386,11 @@ class mis_report_instance_period(orm.Model):
res = {}
report = c.report_instance_id.report_id
company_id = context.get('force_company')
if not company_id:
user = self.pool.get('res.users').read(cr, uid, uid, ['company_id'], context=context)
if user['company_id']:
company_id = user['company_id'][0]
for query in report.query_ids:
obj = self.pool[query.model_id.model]
domain = query.domain and safe_eval(query.domain) or []
@ -353,11 +404,10 @@ class mis_report_instance_period(orm.Model):
# domain.extend([(query.date_field.name, '>=', datetime_from),
# (query.date_field.name, '<', datetime_to)])
raise orm.except_orm(_('Error!'), _('Not implemented'))
domain.extend([('company_id', '=', company_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_ids = obj.search(cr, uid, domain, 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
@ -381,7 +431,7 @@ class mis_report_instance_period(orm.Model):
localdict.update(self._fetch_balances(cr, uid, c, context=context))
localdict.update(self._fetch_queries(cr, uid, c, context=context))
for kpi in c.report_instance_id.report.kpi_ids:
for kpi in c.report_instance_id.report_id.kpi_ids:
try:
kpi_val = safe_eval(kpi.expression, localdict)
except ZeroDivisionError:
@ -443,12 +493,20 @@ class mis_report_instance(orm.Model):
'report_instance_id',
required=True,
string='Periods'),
#'comparison_column': fields.many2many('mis.report.instance')
'target_move': fields.selection([('posted', 'All Posted Entries'),
('all', 'All Entries'),
], 'Target Moves', required=True),
}
_defaults = {
'target_move': 'posted',
}
def compute(self, cr, uid, _ids, context=None):
assert isinstance(_ids, (int, long))
r = self.browse(cr, uid, _ids, context=context)
context['state'] = r.target_move
res = {}
@ -459,8 +517,9 @@ class mis_report_instance(orm.Model):
res['rows'] = rows
cols = []
report_instance_period_obj = self.pool.get('mis.report.instance.period')
for period in r.period_ids:
cols.append(dict(name=period.name, description=period.name))
cols.append(dict(name=period.name, values=report_instance_period_obj._compute(cr, uid, period, context=context)))
res['cols'] = cols
return res

19
mis_builder/static/src/xml/mis_widget.xml

@ -1,17 +1,28 @@
<template>
<t t-name="mis_builder.MisReport">
<p>Yo!</p>
<p> </p>
<table t-if="widget.mis_report_data">
<th>
<td></td>
<td t-foreach="widget.mis_report_data.cols" t-as="col" style="padding-right:10px">
<th t-foreach="widget.mis_report_data.cols" t-as="col" style="padding-right:10px">
<t t-esc="col.name"/>
</td>
</th>
</th>
<tr t-foreach="widget.mis_report_data.rows" t-as="row">
<th>
<t t-esc="row.name"/>
<t t-esc="row.description"/>
</th>
<td></td>
<td t-foreach="widget.mis_report_data.cols" t-as="col" style="padding-right:10px">
<t t-foreach="col.values" t-as="value">
<t t-if="value == row.name">
<t t-esc="value_value.val_r"/>
<t t-if="value_value.val_c">
<t t-esc="value_value.val_c"/>
</t>
</t>
</t>
</td>
</tr>
</table>
</t>

2
mis_builder/tests/mis.report.csv

@ -0,0 +1,2 @@
"id","description","kpi_ids/id","name","query_ids/id"
"mis_report","","mis_report_kpi_1,mis_report_kpi_2,mis_report_kpi_3,mis_report_kpi_4,mis_report_kpi_5,mis_report_kpi_6","Test","mis_report_query"

2
mis_builder/tests/mis.report.instance.csv

@ -0,0 +1,2 @@
"id","date","description","name","period_ids/id","report_id/id"
"mis_report_instance","2014-07-18","","Test-report-instance","mis_report_instance_period_1,mis_report_instance_period_2,mis_report_instance_period_3,mis_report_instance_period_4,mis_report_instance_period_5,mis_report_instance_period_6","mis_report"

7
mis_builder/tests/mis.report.instance.period.csv

@ -0,0 +1,7 @@
"id","duration","name","offset","type","sequence"
"mis_report_instance_period_1","1","today","","Day",""
"mis_report_instance_period_2","1","yesterday","-1","Day",""
"mis_report_instance_period_3","1","last week","-1","Week","2"
"mis_report_instance_period_4","2","last 2 period","-2","Fiscal Period","3"
"mis_report_instance_period_5","2","last 2 week","-2","Week","4"
"mis_report_instance_period_6","1","yesterday","","Day","1"

7
mis_builder/tests/mis.report.kpi.csv

@ -0,0 +1,7 @@
"id","compare_method","description","expression","divider","name","dp","sequence","type","suffix"
"mis_report_kpi_1","Percentage","chiffre d'affaire","-bal_70","","ca","","","Numeric","€"
"mis_report_kpi_2","Percentage","cost","-bal_60 - bal_61","","cost","","","Numeric","€"
"mis_report_kpi_3","Percentage","profit","ca - cost","","profit","","","Numeric","€"
"mis_report_kpi_4","Difference","margin","profit/ca","","margin","","","Percentage","%"
"mis_report_kpi_5","None","couleur","'vert' if profit > 0 else 'rouge'","","couleur","","","String"
"mis_report_kpi_6","Percentage","total invoice","len(inv)","","total_invoice","","","Numeric","€"

2
mis_builder/tests/mis.report.query.csv

@ -0,0 +1,2 @@
"id","date_field/id","domain","field_ids/id","model_id/id","name"
"mis_report_query","account.field_account_invoice_date_invoice","","account.field_account_invoice_amount_untaxed","account.model_account_invoice","inv"

14
mis_builder/views/mis_builder.xml

@ -34,14 +34,14 @@
<field name="kpi_ids">
<tree string="KPI's" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="description"/>
<field name="description" on_change="onchange_description(description, context)"/>
<field name="name" on_change="onchange_name(name, context)"/>
<field name="expression"/>
<field name="type"/>
<field name="dp"/>
<field name="divider"/>
<field name="type" on_change="onchange_type(type, context)"/>
<field name="dp" attrs="{'invisible': [('type', '=', 'str')]}"/>
<field name="divider" attrs="{'invisible': [('type', '=', 'str')]}"/>
<field name="suffix"/>
<field name="compare_method"/>
<field name="compare_method" attrs="{'invisible': [('type', '=', 'str')]}"/>
</tree>
</field>
</group>
@ -99,6 +99,7 @@
<field name="report_id"/>
<field name="name"/>
<field name="description"/>
<field name="target_move"/>
</tree>
</field>
</record>
@ -125,6 +126,7 @@
<group col="2">
<field name="description"/>
<field name="report_id"/>
<field name="target_move"/>
<field name="date"/>
<field name="period_ids">
<tree string="KPI's" editable="bottom">

Loading…
Cancel
Save