bugfixed for menu disable
[openerp-client.git] / bin / widget / model / record.py
blobe740c099d2458c46a3c37864a4e8b34fbe94a99c
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
6 # $Id$
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
13 # Service Company
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
31 import re
32 import time
33 import common
34 import rpc
35 from rpc import RPCProxy
36 import field
37 import signal_event
38 import gtk
39 import gettext
40 import service
41 from gtk import glade
42 import tools
43 from field import O2MField
45 class EvalEnvironment(object):
46 def __init__(self, parent):
47 self.parent = parent
49 def __getattr__(self, item):
50 if item=='parent' and self.parent.parent:
51 return EvalEnvironment(self.parent.parent)
52 if item=="current_date":
53 return time.strftime('%Y-%m-%d')
54 if item=="time":
55 return time
56 return self.parent.get(includeid=True)[item]
59 class ModelRecord(signal_event.signal_event):
60 def __init__(self, resource, id, group=None, parent=None, new=False ):
61 super(ModelRecord, self).__init__()
62 self.resource = str(resource)
63 self.rpc = RPCProxy(self.resource)
64 self.id = id
65 self._loaded = False
66 self.parent = parent
67 self.mgroup = group
68 self.value = {}
69 self.state_attrs = {}
70 self.modified = False
71 self.modified_fields = {}
72 self.read_time = time.time()
73 for key,val in self.mgroup.mfields.items():
74 self.value[key] = val.create(self)
75 if (new and val.attrs['type']=='one2many') and (val.attrs.get('mode','tree,form').startswith('form')):
76 mod = self.value[key].model_new()
77 self.value[key].model_add(mod)
79 def __getitem__(self, name):
80 return self.mgroup.mfields.get(name, False)
82 def __repr__(self):
83 return '<ModelRecord %s@%s>' % (self.id, self.resource)
85 def is_modified(self):
86 return self.modified
88 def is_wizard(self):
89 return self.mgroup.is_wizard
91 def fields_get(self):
92 return self.mgroup.mfields
94 def _check_load(self):
95 if not self._loaded:
96 self.reload()
97 return True
98 return False
100 def get(self, get_readonly=True, includeid=False, check_load=True, get_modifiedonly=False):
101 if check_load:
102 self._check_load()
103 value = []
104 for name, field in self.mgroup.mfields.items():
105 if (get_readonly or not field.get_state_attrs(self).get('readonly', False)) \
106 and (not get_modifiedonly or (field.name in self.modified_fields or isinstance(field, O2MField))):
107 value.append((name, field.get(self, readonly=get_readonly,
108 modified=get_modifiedonly)))
109 value = dict(value)
110 if includeid:
111 value['id'] = self.id
112 return value
114 def cancel(self):
115 self._loaded = False
116 self.reload()
118 def save(self, reload=True):
119 self._check_load()
120 if not self.id:
121 value = self.get(get_readonly=False)
122 self.id = self.rpc.create(value, self.context_get())
123 else:
124 if not self.is_modified():
125 return self.id
126 value = self.get(get_readonly=False, get_modifiedonly=True)
127 context= self.context_get()
128 context= context.copy()
129 context['read_delta']= time.time()-self.read_time
130 if not rpc.session.rpc_exec_auth('/object', 'execute', self.resource, 'write', [self.id], value, context):
131 return False
132 self._loaded = False
133 if reload:
134 self.reload()
135 return self.id
137 def default_get(self, domain=[], context={}):
138 if len(self.mgroup.fields):
139 val = self.rpc.default_get(self.mgroup.fields.keys(), context)
140 for d in domain:
141 if d[0] in self.mgroup.fields:
142 if d[1] == '=':
143 val[d[0]] = d[2]
144 if d[1] == 'in' and len(d[2]) == 1:
145 val[d[0]] = d[2][0]
146 self.set_default(val)
148 def name_get(self):
149 name = self.rpc.name_get([self.id], rpc.session.context)[0]
150 return name
152 def validate_set(self):
153 change = self._check_load()
154 for fname in self.mgroup.mfields:
155 field = self.mgroup.mfields[fname]
156 change = change or not field.get_state_attrs(self).get('valid', True)
157 field.get_state_attrs(self)['valid'] = True
158 if change:
159 self.signal('record-changed')
160 return change
162 def validate(self):
163 self._check_load()
164 ok = True
165 for fname in self.mgroup.mfields:
166 if not self.mgroup.mfields[fname].validate(self):
167 ok = False
168 return ok
170 def _get_invalid_fields(self):
171 res = []
172 for fname, field in self.mgroup.mfields.items():
173 if not field.get_state_attrs(self).get('valid', True):
174 res.append((fname, field.attrs['string']))
175 return dict(res)
176 invalid_fields = property(_get_invalid_fields)
178 def context_get(self):
179 return self.mgroup.context
181 def get_default(self):
182 self._check_load()
183 value = dict([(name, field.get_default(self))
184 for name, field in self.mgroup.mfields.items()])
185 return value
187 def set_default(self, val):
188 for fieldname, value in val.items():
189 if fieldname not in self.mgroup.mfields:
190 continue
191 self.mgroup.mfields[fieldname].set_default(self, value)
192 self._loaded = True
193 self.signal('record-changed')
195 def set(self, val, modified=False, signal=True):
196 later={}
197 for fieldname, value in val.items():
198 if fieldname not in self.mgroup.mfields:
199 continue
200 if isinstance(self.mgroup.mfields[fieldname], field.O2MField):
201 later[fieldname]=value
202 continue
203 self.mgroup.mfields[fieldname].set(self, value, modified=modified)
204 for fieldname, value in later.items():
205 self.mgroup.mfields[fieldname].set(self, value, modified=modified)
206 self._loaded = True
207 self.modified = modified
208 if not self.modified:
209 self.modified_fields = {}
210 if signal:
211 self.signal('record-changed')
213 def reload(self):
214 if not self.id:
215 return
216 c= rpc.session.context.copy()
217 c.update(self.context_get())
218 c['bin_size'] = True
219 res = self.rpc.read([self.id], self.mgroup.mfields.keys(), c)
220 if res:
221 value = res[0]
222 self.read_time= time.time()
223 self.set(value)
225 def expr_eval(self, dom, check_load=True):
226 if not isinstance(dom, basestring):
227 return dom
228 if check_load:
229 self._check_load()
230 d = {}
231 for name, mfield in self.mgroup.mfields.items():
232 d[name] = mfield.get(self, check_load=check_load)
234 d['current_date'] = time.strftime('%Y-%m-%d')
235 d['time'] = time
236 d['context'] = self.context_get()
237 d['active_id'] = self.id
238 if self.parent:
239 d['parent'] = EvalEnvironment(self.parent)
240 val = tools.expr_eval(dom, d)
241 return val
243 #XXX Shoud use changes of attributes (ro, ...)
244 def on_change(self, callback):
245 match = re.match('^(.*?)\((.*)\)$', callback)
246 if not match:
247 raise Exception, 'ERROR: Wrong on_change trigger: %s' % callback
248 func_name = match.group(1)
249 arg_names = [n.strip() for n in match.group(2).split(',')]
250 args = [self.expr_eval(arg) for arg in arg_names]
251 ids = self.id and [self.id] or []
252 response = getattr(self.rpc, func_name)(ids, *args)
253 if response:
254 self.set(response.get('value', {}), modified=True)
255 if 'domain' in response:
256 for fieldname, value in response['domain'].items():
257 if fieldname not in self.mgroup.mfields:
258 continue
259 self.mgroup.mfields[fieldname].attrs['domain'] = value
260 warning=response.get('warning',{})
261 if warning:
262 common.warning(warning['message'], warning['title'])
263 self.signal('record-changed')
265 def on_change_attrs(self, callback):
266 self.signal('attrs-changed')
268 def cond_default(self, field, value):
269 ir = RPCProxy('ir.values')
270 values = ir.get('default', '%s=%s' % (field, value),
271 [(self.resource, False)], False, {})
272 data = {}
273 for index, fname, value in values:
274 data[fname] = value
275 self.set_default(data)
277 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: