diff --git a/mis_builder/__openerp__.py b/mis_builder/__openerp__.py index 7de33a40..8b91ac49 100644 --- a/mis_builder/__openerp__.py +++ b/mis_builder/__openerp__.py @@ -33,6 +33,7 @@ 'data': [ 'wizard/mis_builder_dashboard.xml', 'views/mis_builder.xml', + 'security/ir.model.access.csv', ], 'test': [ ], diff --git a/mis_builder/models/mis_builder.py b/mis_builder/models/mis_builder.py index 9ebca2c6..d19073a8 100644 --- a/mis_builder/models/mis_builder.py +++ b/mis_builder/models/mis_builder.py @@ -48,7 +48,7 @@ def _get_selection_label(selection, value): return '' -def utc_midnight(d, add_day=0): +def _utc_midnight(d, add_day=0): d = datetime.strptime(d, tools.DEFAULT_SERVER_DATE_FORMAT) if add_day: d = d + timedelta(days=add_day) @@ -57,6 +57,10 @@ def utc_midnight(d, add_day=0): return datetime.strftime(d_utc_midnight, tools.DEFAULT_SERVER_DATETIME_FORMAT) +def _clean(varStr): + return re.sub('\W|^(?=\d)', '_', varStr) + + class mis_report_kpi(orm.Model): """ A KPI is an element of a MIS report. @@ -106,6 +110,7 @@ class mis_report_kpi(orm.Model): 'divider': '1', 'dp': 0, 'compare_method': 'pct', + 'sequence': 1, } _order = 'sequence' @@ -127,12 +132,11 @@ class mis_report_kpi(orm.Model): 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): + def onchange_description(self, cr, uid, ids, description, name, context=None): # construct name from description - clean = lambda varStr: re.sub('\W|^(?=\d)', '_', varStr) res = {} - if description: - res = {'value': {'name': clean(description)}} + if description and not name: + res = {'value': {'name': _clean(description)}} return res def onchange_type(self, cr, uid, ids, kpi_type, context=None): @@ -152,7 +156,7 @@ class mis_report_kpi(orm.Model): kpi.divider, kpi.dp, kpi.suffix) elif kpi.type == 'pct': return self._render_num(value, - 100, kpi.dp, '%') + 0.01, kpi.dp, '%') else: return unicode(value) @@ -170,7 +174,7 @@ class mis_report_kpi(orm.Model): kpi.divider, kpi.dp, kpi.suffix, sign='+') elif kpi.compare_method == 'pct' and base_value != 0: - return self._render_num(value / base_value - base_value, + return self._render_num(value / base_value - 1, 0.01, kpi.dp, '%', sign='+') return '' @@ -192,6 +196,15 @@ class mis_report_query(orm.Model): _name = 'mis.report.query' + def _get_field_names(self, cr, uid, ids, name, args, context=None): + res = {} + for query in self.browse(cr, uid, ids, context=context): + field_name = [] + for field in query.field_ids: + field_name.append(field.name) + res[query.id] = ', '.join(field_name) + return res + _columns = { 'name': fields.char(size=32, required=True, string='Name'), @@ -199,6 +212,9 @@ 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', + store={'mis.report.query': + (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'))]), @@ -259,11 +275,6 @@ 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': @@ -280,17 +291,16 @@ 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: date! period_obj = self.pool['account.period'] all_period_ids = period_obj.search(cr, uid, - [('special', '=', False), ('company_id', '=', company_id)], + [('special', '=', False), ('company_id', '=', c.company_id.id)], order='date_start', context=context) current_period_ids = period_obj.search(cr, uid, [('special', '=', False), ('date_start', '<=', d), ('date_stop', '>=', d), - ('company_id', '=', company_id)], + ('company_id', '=', c.company_id.id)], context=context) if not current_period_ids: raise orm.except_orm(_("Error!"), @@ -350,12 +360,18 @@ 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) } _defaults = { 'offset': -1, 'duration': 1, + 'sequence': 1, + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, + 'mis.report.instance.period', + context=c) } _order = 'sequence' @@ -383,18 +399,11 @@ class mis_report_instance_period(orm.Model): 'date_to': c.date_to}) # TODO: initial balance? - 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_ids = account_obj.search(cr, uid, [('company_id', '=', c.company_id.id)], context=context) account_datas = account_obj.read(cr, uid, account_ids, ['code', 'balance'], context=search_ctx) balances = {} - clean = lambda varStr: re.sub('\W|^(?=\d)', '_', varStr) for account_data in account_datas: - # TODO: company_id in key - key = 'bal' + clean(account_data['code']) + key = 'bal' + _clean(account_data['code']) assert key not in balances balances[key] = account_data['balance'] @@ -404,11 +413,6 @@ 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 [] @@ -416,11 +420,12 @@ 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) - datetime_to = utc_midnight(c.date_to, add_day=1) + datetime_from = _utc_midnight(c.date_from) + datetime_to = _utc_midnight(c.date_to, add_day=1) domain.extend([(query.date_field.name, '>=', datetime_from), (query.date_field.name, '<', datetime_to)]) - domain.extend([('company_id', '=', company_id)]) + if obj._columns.get('company_id', False): + domain.extend([('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) @@ -516,8 +521,17 @@ class mis_report_instance(orm.Model): 'target_move': 'posted', } + 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) + def compute(self, cr, uid, _ids, context=None): assert isinstance(_ids, (int, long)) + if context is None: + context = {} r = self.browse(cr, uid, _ids, context=context) context['state'] = r.target_move @@ -533,8 +547,16 @@ class mis_report_instance(orm.Model): report_instance_period_obj = self.pool.get('mis.report.instance.period') kpi_obj = self.pool.get('mis.report.kpi') for period in r.period_ids: - current_col = dict(name=period.name, values=report_instance_period_obj._compute(cr, uid, period, context=context)) + current_col = dict(name=period.name, + values=report_instance_period_obj._compute(cr, uid, period, context=context), + date=period.duration > 1 and + _('from %s to %s' % (period.period_from and period.period_from.name + 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) cols.append(current_col) + # add comparison column for compare_col in period.comparison_column_ids: column1 = current_col for col in cols: @@ -543,8 +565,12 @@ class mis_report_instance(orm.Model): continue compare_values = {} for kpi in r.report_id.kpi_ids: - compare_values[kpi.name] = {'val_r': kpi_obj._render_comparison(kpi, column1['values'][kpi.name]['val'], column2['values'][kpi.name]['val'])} - cols.append(dict(name='%s - %s' % (period.name, compare_col.name), values=compare_values)) + compare_values[kpi.name] = {'val_r': kpi_obj._render_comparison(kpi, + column1['values'][kpi.name]['val'], + column2['values'][kpi.name]['val'])} + cols.append(dict(name='%s - %s' % (period.name, compare_col.name), + values=compare_values, + date='')) res['cols'] = cols return res diff --git a/mis_builder/security/ir.model.access.csv b/mis_builder/security/ir.model.access.csv index 8d4ee863..d8f5c6ae 100644 --- a/mis_builder/security/ir.model.access.csv +++ b/mis_builder/security/ir.model.access.csv @@ -1 +1,11 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +manage_mis_report_kpi,manage_mis_report_kpi,model_mis_report_kpi,account.group_account_manager,1,1,1,1 +access_mis_report_kpi,access_mis_report_kpi,model_mis_report_kpi,account.group_account_invoice,1,0,0,0 +manage_mis_report_query,manage_mis_report_query,model_mis_report_query,account.group_account_manager,1,1,1,1 +access_mis_report_query,access_mis_report_query,model_mis_report_query,account.group_account_invoice,1,0,0,0 +manage_mis_report,manage_mis_report,model_mis_report,account.group_account_manager,1,1,1,1 +access_mis_report,access_mis_report,model_mis_report,account.group_account_invoice,1,0,0,0 +manage_mis_report_instance_period,manage_mis_report_instance_period,model_mis_report_instance_period,account.group_account_manager,1,1,1,1 +access_mis_report_instance_period,access_mis_report_instance_period,model_mis_report_instance_period,account.group_account_invoice,1,0,0,0 +manage_mis_report_instance,manage_mis_report_instance,model_mis_report_instance,account.group_account_manager,1,1,1,1 +access_mis_report_instance,access_mis_report_instance,model_mis_report_instance,account.group_account_invoice,1,0,0,0 diff --git a/mis_builder/static/src/css/custom.css b/mis_builder/static/src/css/custom.css new file mode 100644 index 00000000..361333f1 --- /dev/null +++ b/mis_builder/static/src/css/custom.css @@ -0,0 +1,3 @@ +.openerp .rallign { + text-align: right; +} \ No newline at end of file diff --git a/mis_builder/static/src/xml/mis_widget.xml b/mis_builder/static/src/xml/mis_widget.xml index 43b92c42..c5aae3c8 100644 --- a/mis_builder/static/src/xml/mis_widget.xml +++ b/mis_builder/static/src/xml/mis_widget.xml @@ -5,10 +5,13 @@ - +
+
+ +
@@ -19,10 +22,12 @@ - + + diff --git a/mis_builder/tests/mis.report.csv b/mis_builder/tests/mis.report.csv index 651fef30..01c83128 100644 --- a/mis_builder/tests/mis.report.csv +++ b/mis_builder/tests/mis.report.csv @@ -1,2 +1,3 @@ "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" +"mis_report_test","","mis_report_kpi_test","Test report","mis_report_query_test" diff --git a/mis_builder/tests/mis.report.instance.csv b/mis_builder/tests/mis.report.instance.csv index 0bd8b0c5..3aeb1498 100644 --- a/mis_builder/tests/mis.report.instance.csv +++ b/mis_builder/tests/mis.report.instance.csv @@ -1,2 +1,3 @@ -"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" +"id","description","name","period_ids/id","report_id/id" +"mis_report_instance","","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" +"mis_report_instance_test","","Test-report-instance without company","mis_report_instance_period_test","mis_report_test" diff --git a/mis_builder/tests/mis.report.instance.period.csv b/mis_builder/tests/mis.report.instance.period.csv index 35f5a634..22c328b3 100644 --- a/mis_builder/tests/mis.report.instance.period.csv +++ b/mis_builder/tests/mis.report.instance.period.csv @@ -1,6 +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" +"id","duration","name","offset","type","sequence","company_id/id" +"mis_report_instance_period_1","1","today","","Day","","base.main_company" +"mis_report_instance_period_2","1","yesterday","-1","Day","","base.main_company" +"mis_report_instance_period_3","1","last week","-1","Week","2","base.main_company" +"mis_report_instance_period_4","2","last 2 period","-2","Fiscal Period","3","base.main_company" +"mis_report_instance_period_5","2","last 2 week","-2","Week","4","base.main_company" +"mis_report_instance_period_test","1","today","","Day","","base.main_company" diff --git a/mis_builder/tests/mis.report.kpi.csv b/mis_builder/tests/mis.report.kpi.csv index ab5c60bf..18984f59 100644 --- a/mis_builder/tests/mis.report.kpi.csv +++ b/mis_builder/tests/mis.report.kpi.csv @@ -5,3 +5,4 @@ "mis_report_kpi_4","Difference","margin","profit/ca","","margin","","4","Percentage","%" "mis_report_kpi_5","None","couleur","'vert' if profit > 0 else 'rouge'","","couleur","","5","String","" "mis_report_kpi_6","Percentage","total invoice","len(inv)","","total_invoice","","6","Numeric","€" +"mis_report_kpi_test","Percentage","total test","len(test)","","total_test","","1","Numeric","" diff --git a/mis_builder/tests/mis.report.query.csv b/mis_builder/tests/mis.report.query.csv index 8aad4d28..2c80b546 100644 --- a/mis_builder/tests/mis.report.query.csv +++ b/mis_builder/tests/mis.report.query.csv @@ -1,2 +1,3 @@ "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" +"mis_report_query_test","account.field_account_analytic_balance_date1","","account.field_account_analytic_balance_empty_acc","account.model_account_analytic_balance","test" diff --git a/mis_builder/tests/mis_builder_test.py b/mis_builder/tests/mis_builder_test.py index 8b12842b..2df67af8 100644 --- a/mis_builder/tests/mis_builder_test.py +++ b/mis_builder/tests/mis_builder_test.py @@ -36,9 +36,16 @@ class mis_builder_test(common.TransactionCase): def test_datetime_conversion(self): date_to_convert = '2014-07-05' - date_time_convert = models.mis_builder.utc_midnight(date_to_convert) + date_time_convert = models.mis_builder._utc_midnight(date_to_convert) self.assertEqual(date_time_convert, '2014-07-05 00:00:00', 'The converted date time convert must contains hour') - date_time_convert = models.mis_builder.utc_midnight(date_to_convert, add_day=1) + date_time_convert = models.mis_builder._utc_midnight(date_to_convert, add_day=1) self.assertEqual(date_time_convert, '2014-07-06 00:00:00', 'The converted date time convert must contains hour') + def test_fetch_query(self): + # create a report on a model without company_id field : account.analytic.balance + data = self.registry('mis.report.instance').compute(self.cr, self.uid, self.ref('mis_builder.mis_report_instance_test')) + self.assertDictContainsSubset({'rows': [{'description': u'total test', 'name': u'total_test'}], + 'cols': [{'values': {u'total_test': {'val_c': None, 'val': 0, 'val_r': '0'}}, + 'name': u'today'}]}, data) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/mis_builder/views/mis_builder.xml b/mis_builder/views/mis_builder.xml index 03fee575..0b343256 100644 --- a/mis_builder/views/mis_builder.xml +++ b/mis_builder/views/mis_builder.xml @@ -27,6 +27,7 @@ + @@ -34,7 +35,7 @@ - + @@ -51,7 +52,7 @@ - mis.report.view.action + Mis report mis.report form @@ -66,24 +67,13 @@
- -
-

- -

-
- - - - - -
+
- mis.report.instance.result.view.action + Preview mis.report.instance form @@ -142,6 +132,7 @@ +
@@ -151,7 +142,7 @@ - mis.report.instance.view.action + Mis report instance mis.report.instance form