Merge
[openerp-client.git] / bin / widget / model / record.py
blob440e96dd15ddf1c77139208bc92e3f040e2d1470
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.resource.startswith('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 res = self.rpc.read([self.id], self.mgroup.mfields.keys(), c)
219 if res:
220 value = res[0]
221 self.read_time= time.time()
222 self.set(value)
224 def expr_eval(self, dom, check_load=True):
225 if not isinstance(dom, basestring):
226 return dom
227 if check_load:
228 self._check_load()
229 d = {}
230 for name, mfield in self.mgroup.mfields.items():
231 d[name] = mfield.get(self, check_load=check_load)
233 d['current_date'] = time.strftime('%Y-%m-%d')
234 d['time'] = time
235 d['context'] = self.context_get()
236 d['active_id'] = self.id
237 if self.parent:
238 d['parent'] = EvalEnvironment(self.parent)
239 val = tools.expr_eval(dom, d)
240 return val
242 #XXX Shoud use changes of attributes (ro, ...)
243 def on_change(self, callback):
244 match = re.match('^(.*?)\((.*)\)$', callback)
245 if not match:
246 raise Exception, 'ERROR: Wrong on_change trigger: %s' % callback
247 func_name = match.group(1)
248 arg_names = [n.strip() for n in match.group(2).split(',')]
249 args = [self.expr_eval(arg) for arg in arg_names]
250 ids = self.id and [self.id] or []
251 response = getattr(self.rpc, func_name)(ids, *args)
252 if response:
253 self.set(response.get('value', {}), modified=True)
254 if 'domain' in response:
255 for fieldname, value in response['domain'].items():
256 if fieldname not in self.mgroup.mfields:
257 continue
258 self.mgroup.mfields[fieldname].attrs['domain'] = value
259 self.signal('record-changed')
261 def on_change_attrs(self, callback):
262 self.signal('attrs-changed')
264 def cond_default(self, field, value):
265 ir = RPCProxy('ir.values')
266 values = ir.get('default', '%s=%s' % (field, value),
267 [(self.resource, False)], False, {})
268 data = {}
269 for index, fname, value in values:
270 data[fname] = value
271 self.set_default(data)
273 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: