1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from osv import fields, osv
23 from tools.translate import _
24 from tools import ustr
26 class report_result(osv.osv):
30 _name = "base_report_creator_report.result"
31 _description = "Report"
33 # Should request only used fields
36 def fields_get(self, cr, user, fields=None, context=None):
39 @param cr: the current row, from the database cursor,
40 @param user: the current user’s ID for security checks,
41 @param Fields: List of field of customer reports form.
42 @return: Dictionary of Fields
47 data = context and context.get('report_id', False) or False
48 if (not context) or 'report_id' not in context:
49 return super(report_result, self).fields_get(cr, user, fields, context)
51 report = self.pool.get('base_report_creator.report').browse(cr, user, data,context=context)
54 for model in report.model_ids:
55 models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
59 for f in report.field_ids:
61 fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
64 fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
67 def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
69 Overrides orm field_view_get.
70 @param cr: the current row, from the database cursor,
71 @param user: the current user’s ID for security checks,
72 @return: Dictionary of Fields, arch and toolbar.
77 data = context and context.get('report_id', False) or False
78 if (not context) or 'report_id' not in context:
79 return super(report_result, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
80 report = self.pool.get('base_report_creator.report').browse(cr, user, context.get('report_id'), context=context)
82 for model in report.model_ids:
83 models[model.model] = self.pool.get(model.model).fields_get(cr, user, context=context)
86 for f in report.field_ids:
88 fields['field'+str(i)] = models[f.field_id.model][f.field_id.name]
91 fields['column_count'] = {'readonly': True, 'type': 'integer', 'string': 'Count', 'size': 64, 'name': 'column_count'}
93 arch = '<?xml version="1.0"?>'
94 if view_type == 'graph':
95 orientation_eval = {'horz':'horizontal','vert' :'vertical'}
96 orientation = eval(report.view_graph_orientation,orientation_eval)
97 arch +='<graph string="%s" type="%s" orientation="%s">' % (report.name, report.view_graph_type, orientation)
100 for f in report.field_ids:
101 if f.graph_mode == val:
103 arch += '<field name="%s" select="1"/>' % ('field'+str(i),)
106 arch += '<field name="%s" select="1"/>' % ('column_count',)
108 elif view_type == 'calendar':
109 required_types = ['date_start', 'date_delay', 'color']
110 set_dict = {'view_type':view_type, 'string':report.name}
113 for f in report.field_ids:
114 if f.calendar_mode and f.calendar_mode in required_types:
116 field_cal = 'field'+str(i)
119 field_cal = 'column_count'
120 set_dict[f.calendar_mode] = field_cal
121 del required_types[required_types.index(f.calendar_mode)]
125 temp_list.append('''<field name = "%(name)s" select = "1"/>''' % {'name': 'field' + str(i)})
128 temp_list.append('''<field name="%(name)s" select="1"/>''' % {'name':'column_count'})
130 arch += '''<% (view_type)s string = "%(string)s" date_start = "%(date_start)s" ''' % set_dict
131 if set_dict.get('date_delay', False):
132 arch += ''' date_delay = "%(date_delay)s" ''' % set_dict
134 if set_dict.get('date_stop', False):
135 arch += ''' date_stop="%(date_stop)s" '''%set_dict
137 if set_dict.get('color', False):
138 arch += ''' color="%(color)s"'''%set_dict
140 arch += ''.join(temp_list)
142 arch += '<%s string="%s">' % (view_type, report.name)
144 for f in report.field_ids:
146 arch += '<field name="%s"/>' % ('field' + str(i),)
149 arch += '<field name="%s"/>' % ('column_count',)
150 arch += '</%s>' % (view_type,)
155 result['toolbar'] = {
162 def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
164 overrides orm Read method.Read List of fields for report creator.
165 @param cr: the current row, from the database cursor,
166 @param user: the current user’s ID for security checks,
167 @param ids: List of report creator's id.
168 @param fields: List of fields.
169 @return: List of Dictionary of form [{‘name_of_the_field’: value, ...}, ...]
173 report_id = context.get('active_id', False)
174 report = self.pool.get('base_report_creator.report').browse(cr, user, context.get('report_id'), context=context)
175 cr.execute(report.sql_query)
176 result = cr.dictfetchall()
179 def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
181 overrides orm search method.
182 @param cr: the current row, from the database cursor,
183 @param user: the current user’s ID for security checks,
184 @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
189 context_id = context.get('report_id', False)
191 if (not context) or 'report_id' not in context:
194 report = self.pool.get('base_report_creator.report').browse(cr, user, context_id, context=context)
197 for f in report.field_ids:
199 fields['field'+str(i)] = (f.field_id.model, f.field_id.name)
202 fields['column_count'] = (False, 'Count')
206 model = self.pool.get(fields[a[0]][0])
207 if a[1] in ('like', 'ilike', 'not like', 'not ilike'):
208 right = '%%%s%%' % (a[2],)
211 newargs.append(str(model._table+"."+fields[a[0]][1] + " " +a[1] + " '" + right)+"'")
214 report_pool = self.pool.get('base_report_creator.report')
215 reports = report_pool._sql_query_get(cr, user, [context_id], 'sql_query', None, ctx, where_plus=newargs, limit=limit, offset=offset)
216 cr.execute(reports[context_id])
217 result = cr.fetchall()
218 return map(lambda x: x[0], result)
223 class report_creator(osv.osv):
227 _name = "base_report_creator.report"
228 _description = "Report"
231 # Should request only used fields
233 def export_data(self, cr, uid, ids, fields_to_export, context=None):
236 data_l = self.read(cr, uid, ids, ['sql_query'], context=context)
239 for record in data_l:
241 for key in fields_to_export:
242 value = record.get(key,'')
243 if isinstance(value, tuple):
244 datas.append(ustr(value[1]))
246 datas.append(ustr(value))
247 final_datas += [datas]
249 return {'datas': final_datas}
252 def _path_get(self, cr, uid, models, filter_ids=[]):
254 @param cr: the current row, from the database cursor,
255 @param uid: the current user’s ID for security checks,
256 @param models: List of object.
258 self.model_set_id = False
264 model_dict[model.model] = self.pool.get(model.model)._table
266 model_list = model_dict.keys()
267 reference_model_dict = {}
268 for model in model_dict:
269 from_list.append(model_dict.get(model))
270 rest_list = model_dict.keys()
271 rest_list.remove(model)
272 model_pool = self.pool.get(model)
273 fields_get = model_pool.fields_get(cr, uid)
276 def _get_inherit_fields(obj):
277 pool_model = self.pool.get(obj)
278 #Adding the columns of the model itself
279 model_columns.update(pool_model._columns)
280 #Adding the columns of its _inherits
281 for record in pool_model._inherits.keys():
282 _get_inherit_fields(record)
284 _get_inherit_fields(model)
286 fields_filter = dict(filter(lambda x:x[1].get('relation', False)
287 and x[1].get('relation') in rest_list
288 and x[1].get('type') == 'many2one'
289 and not (isinstance(model_columns[x[0]], fields.function) or isinstance(model_columns[x[0]], fields.related)), fields_get.items()))
291 model in model_list and model_list.remove(model)
292 model_count = reference_model_dict.get(model, False)
294 reference_model_dict[model] = model_count +1
296 reference_model_dict[model] = 1
297 for k, v in fields_filter.items():
298 v.get('relation') in model_list and model_list.remove(v.get('relation'))
299 relation_count = reference_model_dict.get(v.get('relation'), False)
301 reference_model_dict[v.get('relation')] = relation_count+1
303 reference_model_dict[v.get('relation')]=1
304 if k in self.pool.get(model)._columns:
305 str_where = model_dict.get(model)+"."+ k + "=" + model_dict.get(v.get('relation'))+'.id'
306 where_list.append(str_where)
308 if reference_model_dict:
309 self.model_set_id = model_dict.get(reference_model_dict.keys()[reference_model_dict.values().index(min(reference_model_dict.values()))])
310 if model_list and not len(model_dict.keys()) == 1:
311 raise osv.except_osv(_('No Related Models!!'), _('These is/are model(s) (%s) in selection which is/are not related to any other model') % ','.join(model_list))
313 if filter_ids and where_list <> []:
314 filter_list.append(' and ')
315 filter_list.append(' ')
317 for filter_id in filter_ids:
318 filter_list.append(filter_id.expression)
319 filter_list.append(' ')
320 filter_list.append(filter_id.condition)
322 if len(from_list) == 1 and filter_ids:
323 from_list.append(' ')
324 ret_str = "\n where \n".join(from_list)
326 ret_str = ",\n".join(from_list)
329 where_list = list(set(where_list))
330 ret_str += "\n where \n"+" and\n".join(where_list)
331 ret_str = ret_str.strip()
333 ret_str += "\n".join(filter_list)
334 if ret_str.endswith('and'):
335 ret_str = ret_str[0:len(ret_str)-3]
336 if ret_str.endswith('or'):
337 ret_str = ret_str[0:len(ret_str)-2]
338 ret_str = ret_str.strip()
340 return ret_str % {'uid': uid}
342 def _id_get(self, cr, uid, id, context):
346 return self.model_set_id and 'min('+self.model_set_id+'.id)'
348 def _sql_query_get(self, cr, uid, ids, prop, unknow_none, context=None, where_plus=[], limit=None, offset=None):
350 Get sql query which return on sql_query field.
351 @return: Dictionary of sql query.
354 for obj in self.browse(cr, uid, ids, context=context):
358 for f in obj.field_ids:
359 # Allowing to use count(*)
360 if not f.field_id.model and f.group_method == 'count':
361 fields.insert(0, ('count(*) as column_count'))
363 t = self.pool.get(f.field_id.model_id.model)._table
364 if f.group_method == 'group':
365 fields.append('\t'+t+'.'+f.field_id.name+' as field'+str(i))
366 groupby.append(t+'.'+f.field_id.name)
368 fields.append('\t'+f.group_method+'('+t+'.'+f.field_id.name+')'+' as field'+str(i))
371 models = self._path_get(cr, uid, obj.model_ids, obj.filter_ids)
372 check = self._id_get(cr, uid, ids[0], context)
374 fields.insert(0, (check + ' as id'))
377 result[obj.id] = """select
381 """ % (',\n'.join(fields), models)
383 result[obj.id] += "group by\n\t"+', '.join(groupby)
385 result[obj.id] += "\nhaving \n\t"+"\n\t and ".join(where_plus)
387 result[obj.id] += " limit "+str(limit)
389 result[obj.id] += " offset "+str(offset)
391 result[obj.id] = False
395 'name': fields.char('Report Name', size=64, required=True),
396 'type': fields.selection([('list', 'Rows And Columns Report'), ], 'Report Type', required=True), #('sum','Summation Report')
397 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the report without removing it."),
398 'view_type1': fields.selection([('form', 'Form'),
401 ('calendar', 'Calendar')], 'First View', required=True),
402 'view_type2': fields.selection([('', '/'),
406 ('calendar', 'Calendar')], 'Second View'),
407 'view_type3': fields.selection([('', '/'),
411 ('calendar', 'Calendar')], 'Third View'),
412 'view_graph_type': fields.selection([('pie', 'Pie Chart'),
413 ('bar', 'Bar Chart')], 'Graph Type', required=True),
414 'view_graph_orientation': fields.selection([('horz', 'Horizontal'),
415 ('vert', 'Vertical')], 'Graph Orientation', required=True),
416 'model_ids': fields.many2many('ir.model', 'base_report_creator_report_model_rel', 'report_id', 'model_id', 'Reported Objects'),
417 'field_ids': fields.one2many('base_report_creator.report.fields', 'report_id', 'Fields to Display'),
418 'filter_ids': fields.one2many('base_report_creator.report.filter', 'report_id', 'Filters'),
419 'sql_query': fields.function(_sql_query_get, method=True, type="text", string='SQL Query', store=True),
420 'group_ids': fields.many2many('res.groups', 'base_report_creator_group_rel', 'report_id', 'group_id', 'Authorized Groups'),
421 'menu_id': fields.many2one('ir.ui.menu', "Menu", readonly=True),
424 'type': lambda *args: 'list',
425 'active': lambda *args: True,
426 'view_type1': lambda *args: 'tree',
427 'view_type2': lambda *args: 'graph',
428 'view_graph_type': lambda *args: 'bar',
429 'view_graph_orientation': lambda *args: 'horz',
432 def open_report(self, cr, uid, ids, context=None):
434 This Function opens base creator report view
435 @param self: The object pointer
436 @param cr: the current row, from the database cursor,
437 @param uid: the current user’s ID for security checks,
438 @param ids: List of report open's IDs
439 @param context: A standard dictionary for contextual values
440 @return : Dictionary value for base creator report form
445 rep = self.browse(cr, uid, ids, context=context)
450 view_mode = rep.view_type1
452 view_mode += ',' + rep.view_type2
454 view_mode += ',' + rep.view_type3
458 'view_mode': view_mode,
459 'res_model': 'base_report_creator_report.result',
460 'type': 'ir.actions.act_window',
461 'context': "{'report_id':%d}" % (rep.id,),
467 def _function_field(self, cr, uid, ids, context=None):
469 constraints function which specify that
470 not display field which are not stored in Database.
471 @param cr: the current row, from the database cursor,
472 @param uid: the current user’s ID for security checks,
473 @param ids: List of Report creator's id.
474 @return: True if display field which are stored in database.
475 or false if display field which are not store in dtabase.
477 this_objs = self.browse(cr, uid, ids, context=context)
478 for obj in this_objs:
479 for fld in obj.field_ids:
480 # Allowing to use count(*)
481 if not fld.field_id.model and fld.group_method == 'count':
483 model_column = self.pool.get(fld.field_id.model)._columns[fld.field_id.name]
484 if (isinstance(model_column, fields.function) or isinstance(model_column, fields.related)) and not model_column.store:
488 def _aggregation_error(self, cr, uid, ids, context=None):
490 constraints function which specify that
491 aggregate function to the non calculated field..
492 @param cr: the current row, from the database cursor,
493 @param uid: the current user’s ID for security checks,
494 @param ids: List of Report creator's id.
495 @return: True if model colume type is in integer or float.
496 or false model colume type is not in integer or float.
498 aggregate_columns = ('integer', 'float')
499 apply_functions = ('sum', 'min', 'max', 'avg', 'count')
500 this_objs = self.browse(cr, uid, ids, context=context)
501 for obj in this_objs:
502 for fld in obj.field_ids:
503 # Allowing to use count(*)
504 if not fld.field_id.model and fld.group_method == 'count':
506 model_column = self.pool.get(fld.field_id.model)._columns[fld.field_id.name]
507 if model_column._type not in aggregate_columns and fld.group_method in apply_functions:
511 def _calander_view_error(self, cr, uid, ids, context=None):
513 this_objs = self.browse(cr, uid, ids, context=context)
514 for obj in this_objs:
515 if obj.view_type1 == 'calendar' or obj.view_type2 == 'calendar' or obj.view_type3 == 'calendar':
516 for fld in obj.field_ids:
517 # Allowing to use count(*)
518 if not fld.field_id.model and fld.group_method == 'count':
520 model_column = self.pool.get(fld.field_id.model)._columns[fld.field_id.name]
521 if fld.calendar_mode in ('date_start','date_end') and model_column._type not in ('date','datetime'):
523 elif fld.calendar_mode=='date_delay' and model_column._type not in ('int','float'):
526 required_types.append(fld.calendar_mode)
527 if 'date_start' not in required_types:
533 (_function_field, 'You can not display field which are not stored in Database.', ['field_ids']),
534 (_aggregation_error, 'You can apply aggregate function to the non calculated field.', ['field_ids']),
535 (_calander_view_error, "You must have to give calendar view's color,start date and delay.", ['field_ids']),
540 class report_creator_field(osv.osv):
544 _name = "base_report_creator.report.fields"
545 _description = "Display Fields"
546 _rec_name = 'field_id'
547 _order = "sequence,id"
549 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of fields."),
550 'field_id': fields.many2one('ir.model.fields', 'Field'),
551 'report_id': fields.many2one('base_report_creator.report', 'Report', on_delete='cascade'),
552 'group_method': fields.selection([('group', 'Grouped'),
557 ('avg', 'Average')], 'Grouping Method', required=True),
558 'graph_mode': fields.selection([('', '/'),
560 ('y', 'Y Axis')], 'Graph Mode'),
561 'calendar_mode': fields.selection([('', '/'),
562 ('date_start', 'Starting Date'),
563 ('date_end', 'Ending Date'), ('date_delay', 'Delay'), ('date_stop', 'End Date'), ('color', 'Unique Colors')], 'Calendar Mode'),
566 'group_method': lambda *args: 'group',
567 'graph_mode': lambda *args: '',
569 report_creator_field()
572 class report_creator_filter(osv.osv):
574 Report Creator Filter
576 _name = "base_report_creator.report.filter"
577 _description = "Report Filters"
579 'name': fields.char('Filter Name', size=64, required=True),
580 'expression': fields.text('Value', required=True, help='Provide an expression for the field based on which you want to filter the records.\n e.g. res_partner.id=3'),
581 'report_id': fields.many2one('base_report_creator.report', 'Report', on_delete='cascade'),
582 'condition': fields.selection([('and', 'AND'),
583 ('or', 'OR')], 'Condition')
586 'condition': lambda *args: 'and',
588 report_creator_filter()
589 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: