Set of label improvements to Open ERP Server.
[odoo/odoo.git] / bin / addons / base / ir / ir_report_custom.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution   
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 from osv import fields,osv
24 from osv.orm import browse_null
25 import ir
26 import report.custom
27 from tools.translate import _
28 import netsvc
29
30 class report_custom(osv.osv):
31     _name = 'ir.report.custom'
32     _columns = {
33         'name': fields.char('Report Name', size=64, required=True, translate=True),
34         'model_id': fields.many2one('ir.model','Object', required=True, change_default=True),
35         'type': fields.selection([('table','Tabular'),('pie','Pie Chart'),('bar','Bar Chart'),('line','Line Plot')], "Report Type", size=64, required='True'),
36         'title': fields.char("Report Title", size=64, required='True', translate=True),
37         'print_format': fields.selection((('A4','a4'),('A5','a5')), 'Print format', required=True),
38         'print_orientation': fields.selection((('landscape','Landscape'),('portrait','Portrait')), 'Print orientation', required=True, size=16),
39         'repeat_header': fields.boolean('Repeat Header'),
40         'footer': fields.char('Report Footer', size=64, required=True),
41         'sortby': fields.char('Sorted By', size=64),
42         'fields_child0': fields.one2many('ir.report.custom.fields', 'report_id','Fields', required=True),
43         'field_parent': fields.many2one('ir.model.fields','Child Field'),
44         'state': fields.selection([('unsubscribed','Unsubscribed'),('subscribed','Subscribed')], 'State', size=64),
45         'frequency': fields.selection([('Y','Yearly'),('M','Monthly'),('D','Daily')], 'Frequency', size=64),
46         'limitt': fields.char('Limit', size=9),
47         'menu_id': fields.many2one('ir.ui.menu', 'Menu')
48     }
49     _defaults = {
50         'print_format': lambda *a: 'A4',
51         'print_orientation': lambda *a: 'portrait',
52         'state': lambda *a: 'unsubscribed',
53         'type': lambda *a: 'table',
54         'footer': lambda *a: 'Generated by OpenERP'
55     }
56     
57     def onchange_model_id(self, cr, uid, ids, model_id):
58         if not(model_id):
59             return {}
60         return {'domain': {'field_parent': [('model_id','=',model_id)]}}
61
62     def unsubscribe(self, cr, uid, ids, context={}):
63 #TODO: should delete the ir.actions.report.custom for these reports and do an ir_del
64         self.write(cr, uid, ids, {'state':'unsubscribed'})
65         return True
66
67     def subscribe(self, cr, uid, ids, context={}):
68         for report in self.browse(cr, uid, ids):
69             report.fields_child0.sort(lambda x,y : x.sequence - y.sequence)
70         
71             # required on field0 does not seem to work( cause we use o2m_l ?)
72             if not report.fields_child0:
73                 raise osv.except_osv(_('Invalid operation'), _('Enter at least one field !'))
74             
75             if report.type in ['pie', 'bar', 'line'] and report.field_parent:
76                 raise osv.except_osv(_('Invalid operation'), _('Tree can only be used in tabular reports'))
77             
78             # Otherwise it won't build a good tree. See level.pop in custom.py.
79             if report.type == 'table' and report.field_parent and report.fields_child0 and not report.fields_child0[0].groupby:
80                 raise osv.except_osv('Invalid operation :', 'When creating tree (field child) report, data must be group by the first field')
81
82             if report.type == 'pie':
83                 if len(report.fields_child0) != 2:
84                     raise osv.except_osv(_('Invalid operation'), _('Pie charts need exactly two fields'))
85                 else:
86                     c_f = {}
87                     for i in range(2):
88                         c_f[i] = []
89                         tmp = report.fields_child0[i]
90                         for j in range(3):
91                             c_f[i].append((not isinstance(eval('tmp.field_child'+str(j)), browse_null) and eval('tmp.field_child'+str(j)+'.ttype')) or None)
92                     if not reduce(lambda x,y : x or y, map(lambda x: x in ['integer', 'float'], c_f[1])):
93                         raise osv.except_osv(_('Invalid operation'), _('Second field should be figures'))
94                     
95             if report.type == 'bar':
96                 if len(report.fields_child0) < 2:
97                     raise osv.except_osv(_('Invalid operation'), _('Bar charts need at least two fields'))
98                 else:
99                     c_f = {}
100                     for i in range(len(report.fields_child0)):
101                         c_f[i] = []
102                         tmp = report.fields_child0[i]
103                         for j in range(3):
104                             c_f[i].append((not isinstance(eval('tmp.field_child'+str(j)), browse_null) and eval('tmp.field_child'+str(j)+'.ttype')) or None)
105
106                         if i == 0:
107                             pass
108                         else:
109                             if not reduce(lambda x,y : x or y, map(lambda x: x in ['integer', 'float'], c_f[i])):
110                                 raise osv.except_osv(_('Invalid operation'), _('Field %d should be a figure') %(i,))
111
112             if report.state=='subscribed':
113                 continue
114
115             name = report.name
116             model = report.model_id.model
117
118             action_def = {'report_id':report.id, 'type':'ir.actions.report.custom', 'model':model, 'name':name}
119             id = self.pool.get('ir.actions.report.custom').create(cr, uid, action_def)
120             m_id = report.menu_id.id
121             action = "ir.actions.report.custom,%d" % (id,)
122             if not report.menu_id:
123                 ir.ir_set(cr, uid, 'action', 'client_print_multi', name, [(model, False)], action, False, True)
124             else:
125                 ir.ir_set(cr, uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(m_id))], action, False, True)
126
127             self.write(cr, uid, [report.id], {'state':'subscribed'}, context)
128         return True
129 report_custom()
130
131
132 class report_custom_fields(osv.osv):
133     _name = 'ir.report.custom.fields'
134     _columns = {
135         'name': fields.char('Name', size=64, required=True),
136         'report_id': fields.many2one('ir.report.custom', 'Report Ref', select=True),
137         'field_child0': fields.many2one('ir.model.fields', 'Field child0', required=True),
138         'fc0_operande': fields.many2one('ir.model.fields', 'Constraint'), 
139         'fc0_condition': fields.char('Condition', size=64),
140         'fc0_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'),
141         'field_child1': fields.many2one('ir.model.fields', 'Field child1'),
142         'fc1_operande': fields.many2one('ir.model.fields', 'Constraint'), 
143         'fc1_condition': fields.char('condition', size=64),
144         'fc1_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'),
145         'field_child2': fields.many2one('ir.model.fields', 'Field child2'),
146         'fc2_operande': fields.many2one('ir.model.fields', 'Constraint'), 
147         'fc2_condition': fields.char('condition', size=64),
148         'fc2_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'),
149         'field_child3': fields.many2one('ir.model.fields', 'Field child3'),
150         'fc3_operande': fields.many2one('ir.model.fields', 'Constraint'), 
151         'fc3_condition': fields.char('condition', size=64),
152         'fc3_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'),
153         'alignment':  fields.selection((('left','left'),('right','right'),('center','center')), 'Alignment', required=True),
154         'sequence': fields.integer('Sequence', required=True),
155         'width': fields.integer('Fixed Width'),
156         'operation': fields.selection((('none', 'None'),('calc_sum','Calculate Sum'),('calc_avg','Calculate Average'),('calc_count','Calculate Count'),('calc_max', 'Get Max'))),
157         'groupby' : fields.boolean('Group By'),
158         'bgcolor': fields.char('Background Color', size=64),
159         'fontcolor': fields.char('Font color', size=64),
160         'cumulate': fields.boolean('Accumulate')
161     }
162     _defaults = {
163         'alignment': lambda *a: 'left',
164         'bgcolor': lambda *a: 'white',
165         'fontcolor': lambda *a: 'black',
166         'operation': lambda *a: 'none',
167     }
168     _order = "sequence"
169
170     def onchange_any_field_child(self, cr, uid, ids, field_id, level):
171         if not(field_id):
172             return {}
173         next_level_field_name = 'field_child%d' % (level+1)
174         next_level_operande = 'fc%d_operande' % (level+1)
175         field = self.pool.get('ir.model.fields').browse(cr, uid, [field_id])[0]
176         res = self.pool.get(field.model).fields_get(cr, uid, field.name)
177         if res[field.name].has_key('relation'):
178             cr.execute('select id from ir_model where model=%s', (res[field.name]['relation'],))
179             (id,) = cr.fetchone() or (False,)
180             if id:
181                 return {
182                     'domain': {
183                         next_level_field_name: [('model_id', '=', id)], 
184                         next_level_operande: [('model_id', '=', id)]
185                     }, 
186                     'required': {
187                         next_level_field_name: True
188                     }
189                 }
190             else:
191                 netsvc.Logger().notifyChannel('web-services', netsvc.LOG_WARNING, _("Using a relation field which uses an unknown object"))
192                 return {'required': {next_level_field_name: True}}
193         else:
194             return {'domain': {next_level_field_name: []}}
195             
196     def get_field_child_onchange_method(level):
197         return lambda self, cr, uid, ids, field_id: self.onchange_any_field_child(cr, uid, ids, field_id, level)
198
199     onchange_field_child0 = get_field_child_onchange_method(0)  
200     onchange_field_child1 = get_field_child_onchange_method(1)  
201     onchange_field_child2 = get_field_child_onchange_method(2)  
202 report_custom_fields()
203
204
205 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
206