merge
[openerp-client.git] / bin / widget / model / record.py
blobc075f3ad0c158d135a90f8e3453df93c8043ea89
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 = 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 fields_get(self):
89 return self.mgroup.mfields
91 def _check_load(self):
92 if not self._loaded:
93 self.reload()
94 return True
95 return False
97 def get(self, get_readonly=True, includeid=False, check_load=True, get_modifiedonly=False):
98 if check_load:
99 self._check_load()
100 value = []
101 for name, field in self.mgroup.mfields.items():
102 if (get_readonly or not field.get_state_attrs(self).get('readonly', False)) \
103 and (not get_modifiedonly or (field.name in self.modified_fields or isinstance(field, O2MField))):
104 value.append((name, field.get(self, readonly=get_readonly,
105 modified=get_modifiedonly)))
106 value = dict(value)
107 if includeid:
108 value['id'] = self.id
109 return value
111 def cancel(self):
112 self._loaded = False
113 self.reload()
115 def save(self, reload=True):
116 self._check_load()
117 if not self.id:
118 value = self.get(get_readonly=False)
119 self.id = self.rpc.create(value, self.context_get())
120 else:
121 if not self.is_modified():
122 return self.id
123 value = self.get(get_readonly=False, get_modifiedonly=True)
124 context= self.context_get()
125 context= context.copy()
126 context['read_delta']= time.time()-self.read_time
127 if not rpc.session.rpc_exec_auth('/object', 'execute', self.resource, 'write', [self.id], value, context):
128 return False
129 self._loaded = False
130 if reload:
131 self.reload()
132 return self.id
134 def default_get(self, domain=[], context={}):
135 if len(self.mgroup.fields):
136 val = self.rpc.default_get(self.mgroup.fields.keys(), context)
137 for d in domain:
138 if d[0] in self.mgroup.fields:
139 if d[1] == '=':
140 val[d[0]] = d[2]
141 if d[1] == 'in' and len(d[2]) == 1:
142 val[d[0]] = d[2][0]
143 self.set_default(val)
145 def name_get(self):
146 name = self.rpc.name_get([self.id], rpc.session.context)[0]
147 return name
149 def validate_set(self):
150 change = self._check_load()
151 for fname in self.mgroup.mfields:
152 field = self.mgroup.mfields[fname]
153 change = change or not field.get_state_attrs(self).get('valid', True)
154 field.get_state_attrs(self)['valid'] = True
155 if change:
156 self.signal('record-changed')
157 return change
159 def validate(self):
160 self._check_load()
161 ok = True
162 for fname in self.mgroup.mfields:
163 if not self.mgroup.mfields[fname].validate(self):
164 ok = False
165 return ok
167 def _get_invalid_fields(self):
168 res = []
169 for fname, field in self.mgroup.mfields.items():
170 if not field.get_state_attrs(self).get('valid', True):
171 res.append((fname, field.attrs['string']))
172 return dict(res)
173 invalid_fields = property(_get_invalid_fields)
175 def context_get(self):
176 return self.mgroup.context
178 def get_default(self):
179 self._check_load()
180 value = dict([(name, field.get_default(self))
181 for name, field in self.mgroup.mfields.items()])
182 return value
184 def set_default(self, val):
185 for fieldname, value in val.items():
186 if fieldname not in self.mgroup.mfields:
187 continue
188 self.mgroup.mfields[fieldname].set_default(self, value)
189 self._loaded = True
190 self.signal('record-changed')
192 def set(self, val, modified=False, signal=True):
193 later={}
194 for fieldname, value in val.items():
195 if fieldname not in self.mgroup.mfields:
196 continue
197 if isinstance(self.mgroup.mfields[fieldname], field.O2MField):
198 later[fieldname]=value
199 continue
200 self.mgroup.mfields[fieldname].set(self, value, modified=modified)
201 for fieldname, value in later.items():
202 self.mgroup.mfields[fieldname].set(self, value, modified=modified)
203 self._loaded = True
204 self.modified = modified
205 if not self.modified:
206 self.modified_fields = {}
207 if signal:
208 self.signal('record-changed')
210 def reload(self):
211 if not self.id:
212 return
213 c= rpc.session.context.copy()
214 c.update(self.context_get())
215 res = self.rpc.read([self.id], self.mgroup.mfields.keys(), c)
216 if res:
217 value = res[0]
218 self.read_time= time.time()
219 self.set(value)
221 def expr_eval(self, dom, check_load=True):
222 if not isinstance(dom, basestring):
223 return dom
224 if check_load:
225 self._check_load()
226 d = {}
227 for name, mfield in self.mgroup.mfields.items():
228 d[name] = mfield.get(self, check_load=check_load)
230 d['current_date'] = time.strftime('%Y-%m-%d')
231 d['time'] = time
232 d['context'] = self.context_get()
233 d['active_id'] = self.id
234 if self.parent:
235 d['parent'] = EvalEnvironment(self.parent)
236 val = tools.expr_eval(dom, d)
237 return val
239 #XXX Shoud use changes of attributes (ro, ...)
240 def on_change(self, callback):
241 match = re.match('^(.*?)\((.*)\)$', callback)
242 if not match:
243 raise Exception, 'ERROR: Wrong on_change trigger: %s' % callback
244 func_name = match.group(1)
245 arg_names = [n.strip() for n in match.group(2).split(',')]
246 args = [self.expr_eval(arg) for arg in arg_names]
247 ids = self.id and [self.id] or []
248 response = getattr(self.rpc, func_name)(ids, *args)
249 if response:
250 self.set(response.get('value', {}), modified=True)
251 if 'domain' in response:
252 for fieldname, value in response['domain'].items():
253 if fieldname not in self.mgroup.mfields:
254 continue
255 self.mgroup.mfields[fieldname].attrs['domain'] = value
256 self.signal('record-changed')
258 def on_change_attrs(self, callback):
259 self.signal('attrs-changed')
261 def cond_default(self, field, value):
262 ir = RPCProxy('ir.values')
263 values = ir.get('default', '%s=%s' % (field, value),
264 [(self.resource, False)], False, {})
265 data = {}
266 for index, fname, value in values:
267 data[fname] = value
268 self.set_default(data)
270 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: