def _get_colors(self, models, color_field, color_field_custom): auto_color_count = 0 # how many color do we need to generate auto. colors = {} for model in models: name = value = key = model.value[color_field] if isinstance(key, (tuple, list)): value, name = key key = tuple(key) if key in colors: # already present skip continue # if field is many2one, try to get color from object # 'color' field field_color = None field_widget = model.mgroup.mfields.get(color_field, False) if field_widget and field_widget.attrs['type'] == 'many2one': fproxy = RPCProxy(field_widget.attrs['relation']) try: fdata = fproxy.read(value, [color_field_custom]) if fdata: field_color = fdata.get(color_field_custom) and str(fdata.get(color_field_custom)) or None except Exception, e: #TODO: Need to limit exception self.log.exception(e) pass if not field_color: # increment total color to generate auto_color_count += 1 colors[key] = (name, value, field_color)
class ModelRecord(signal_event.signal_event): def __init__(self, resource, id, group=None, parent=None, new=False): super(ModelRecord, self).__init__() self.resource = str(resource) self.rpc = RPCProxy(self.resource) self.id = id self._loaded = False self.parent = parent self.mgroup = group self.value = {} self.state_attrs = {} self.modified = False self.modified_fields = {} self._concurrency_check_data = False for key, val in self.mgroup.mfields.items(): self.value[key] = val.create(self) if (new and val.attrs['type'] == 'one2many') and (val.attrs.get( 'mode', 'tree,form').startswith('form')): mod = self.value[key].model_new() self.value[key].model_add(mod) def __getitem__(self, name): return self.mgroup.mfields.get(name, False) def __repr__(self): return '<ModelRecord %s@%s>' % (self.id, self.resource) def is_modified(self): return self.modified def is_wizard(self): return self.mgroup.is_wizard def fields_get(self): return self.mgroup.mfields def _check_load(self): if not self._loaded: self.reload() return True return False def update_context_with_concurrency_check_data(self, context): if self.id and self.is_modified(): context.setdefault( CONCURRENCY_CHECK_FIELD, {})["%s,%d" % (self.resource, self.id)] = self._concurrency_check_data for name, field in self.mgroup.mfields.items(): if isinstance(field, O2MField): if field.name not in self.value.keys(): continue v = self.value[field.name] from itertools import chain for m in chain(v.models, v.models_removed): m.update_context_with_concurrency_check_data(context) def get(self, get_readonly=True, includeid=False, check_load=True, get_modifiedonly=False): if check_load: self._check_load() value = [] for name, field in self.mgroup.mfields.items(): if (get_readonly or not field.get_state_attrs(self).get('readonly', False)) \ and (not get_modifiedonly or (field.name in self.modified_fields or isinstance(field, O2MField))): value.append((name, field.get(self, readonly=get_readonly, modified=get_modifiedonly))) value = dict(value) if includeid: value['id'] = self.id return value def cancel(self): self._loaded = False self.reload() def failed_validation(self): invalid_fields = self.rpc.get_invalid_fields() for item in invalid_fields: if item in self.mgroup.mfields: self.mgroup.mfields[item].get_state_attrs( self)['valid'] = False def save(self, reload=True): self._check_load() if not self.id: value = self.get(get_readonly=False) self.id = self.rpc.create(value, self.context_get()) if not self.id: self.failed_validation() else: if not self.is_modified(): return self.id value = self.get(get_readonly=False, get_modifiedonly=True) context = self.context_get().copy() self.update_context_with_concurrency_check_data(context) if not self.rpc.write([self.id], value, context): self.failed_validation() return False self._loaded = False if reload: self.reload() else: # just reload the CONCURRENCY_CHECK_FIELD self._reload([CONCURRENCY_CHECK_FIELD]) return self.id def default_get(self, domain=[], context={}): if len(self.mgroup.fields): val = self.rpc.default_get(self.mgroup.fields.keys(), context) for d in domain: if d[0] in self.mgroup.fields: if d[1] == '=': val[d[0]] = d[2] if d[1] == 'in' and len(d[2]) == 1: val[d[0]] = d[2][0] self.set_default(val) def name_get(self): name = self.rpc.name_get([self.id], rpc.session.context)[0] return name def validate_set(self): change = self._check_load() for fname in self.mgroup.mfields: field = self.mgroup.mfields[fname] change = change or not field.get_state_attrs(self).get( 'valid', True) field.get_state_attrs(self)['valid'] = True if change: self.signal('record-changed') self.reload() return change def validate(self): self._check_load() ok = True mfields_with_errors = [] for fname, _object in self.mgroup.mfields.iteritems(): if not self.mgroup.mfields[fname].validate(self): print("Not valid field: %s" % (fname)) mfields_with_errors.append(_object.attrs.get('string', fname)) ok = False if mfields_with_errors: common.warning( _('Invalid form, correct following fields:\n') + '\n'.join(mfields_with_errors)) return False return True def _get_invalid_fields(self): res = [] for fname, field in self.mgroup.mfields.items(): if not field.get_state_attrs(self).get('valid', True): res.append((fname, field.attrs['string'])) return dict(res) invalid_fields = property(_get_invalid_fields) def context_get(self): return self.mgroup.context def get_default(self): self._check_load() value = dict([(name, field.get_default(self)) for name, field in self.mgroup.mfields.items()]) return value def set_default(self, val): for fieldname, value in val.items(): if fieldname not in self.mgroup.mfields: continue self.mgroup.mfields[fieldname].set_default(self, value) self._loaded = True self.signal('record-changed') def set(self, val, modified=False, signal=True): later = {} for fieldname, value in val.items(): if fieldname == CONCURRENCY_CHECK_FIELD: self._concurrency_check_data = value if fieldname not in self.mgroup.mfields: continue if isinstance(self.mgroup.mfields[fieldname], field.O2MField): later[fieldname] = value continue self.mgroup.mfields[fieldname].set(self, value, modified=modified) for fieldname, value in later.items(): self.mgroup.mfields[fieldname].set(self, value, modified=modified) self._loaded = True self.modified = modified if not self.modified: self.modified_fields = {} if signal: self.signal('record-changed') def reload(self): return self._reload(self.mgroup.mfields.keys() + [CONCURRENCY_CHECK_FIELD]) def _reload(self, fields): if not self.id: return c = rpc.session.context.copy() c.update(self.context_get()) c['bin_size'] = True res = self.rpc.read([self.id], fields, c) if res: value = res[0] if self.parent: self.set(value, signal=False) else: self.set(value) def expr_eval(self, dom, check_load=True): if not isinstance(dom, basestring): return dom if check_load: self._check_load() d = {} for name, mfield in self.mgroup.mfields.items(): d[name] = mfield.get(self, check_load=check_load) d['current_date'] = time.strftime('%Y-%m-%d') d['time'] = time d['context'] = self.context_get() d['active_id'] = self.id if self.parent: d['parent'] = EvalEnvironment(self.parent) val = tools.expr_eval(dom, d) return val #XXX Shoud use changes of attributes (ro, ...) def on_change(self, callback): match = re.match('^(.*?)\((.*)\)$', callback) if not match: raise Exception, 'ERROR: Wrong on_change trigger: %s' % callback func_name = match.group(1) arg_names = [n.strip() for n in match.group(2).split(',') if n.strip()] args = [self.expr_eval(arg) for arg in arg_names] ids = self.id and [self.id] or [] response = getattr(self.rpc, func_name)(ids, *args) if response: self.set(response.get('value', {}), modified=True) if 'domain' in response: for fieldname, value in response['domain'].items(): if fieldname not in self.mgroup.mfields: continue self.mgroup.mfields[fieldname].attrs['domain'] = value warning = response.get('warning', {}) if warning: common.warning(warning['message'], warning['title']) self.signal('record-changed') def on_change_attrs(self, callback): self.signal('attrs-changed') def cond_default(self, field, value): ir = RPCProxy('ir.values') values = ir.get('default', '%s=%s' % (field, value), [(self.resource, False)], False, {}) data = {} for index, fname, value in values: data[fname] = value self.set_default(data)
class ModelRecordGroup(signal_event.signal_event): def __init__(self, resource, fields, ids=[], parent=None, context={}, is_wizard=False, screen=None): super(ModelRecordGroup, self).__init__() self._readonly = False self.parent = parent self._context = context self._context.update(rpc.session.context) self.resource = resource self.rpc = RPCProxy(resource) self.fields = fields self.mfields = {} self.mfields_load(fields.keys(), self) self.screen = screen self.models = ModelList(self) self.current_idx = None self.load(ids) self.models_removed = [] self.on_write = '' self.is_wizard = is_wizard self.list_parent = False self.list_group = False def destroy(self): for field in self.mfields.values(): field.destroy() if self.list_group: self.list_group.destroy() del self.mfields del self.fields del self.list_group del self.models def index(self, model): return self.models.index(model) def mfields_load(self, fkeys, models): for fname in fkeys: fvalue = models.fields[fname] modelfield = field.ModelField(fvalue['type']) fvalue['name'] = fname models.mfields[fname] = modelfield(models, fvalue) def model_move(self, model, position=0): self.models.move(model, position) def set_sequence(self, get_id, rec_id, field='sequence'): seq_ids = [] index = 0 for model in self.models: seq_ids += [model[field].get(model)] index = index + 1 set_list = list(set(seq_ids)) if len(seq_ids) != len(set_list): set_list.sort() repeat = set_list[-1] mod_list = seq_ids[len(set_list):] for e in range(len(mod_list)): repeat = repeat + 1 mod_list[e] = repeat final_list = set_list + mod_list index = 0 for model in self.models: model[field].set(model, final_list[index], modified=True) if model.id: model.save() index = index + 1 else: seq_id = [] if get_id < rec_id: for x in range(get_id, rec_id): seq_id += [self.models[x][field].get(self.models[x])] sort_seq = [seq_id[-1]] + seq_id[:-1] index = 0 for x in range(get_id, rec_id): self.models[x][field].set(self.models[x], sort_seq[index], modified=True) if self.models[x].id: self.models[x].save() index = index + 1 else: for x in range(rec_id, get_id + 1): seq_id += [self.models[x][field].get(self.models[x])] sort_seq = seq_id[1:] + [seq_id[0]] index = 0 for x in range(rec_id, get_id + 1): self.models[x][field].set(self.models[x], sort_seq[index], modified=True) if self.models[x].id: self.models[x].save() index = index + 1 def save(self): for model in self.models: saved = model.save() self.writen(saved) def writen(self, edited_id): if not self.on_write: return new_ids = getattr(self.rpc, self.on_write)(edited_id, self.context) model_idx = self.models.index(self[edited_id]) result = False for id in new_ids: cont = False for m in self.models: if m.id == id: cont = True m.reload() if cont: continue newmod = ModelRecord(self.resource, id, parent=self.parent, group=self) newmod.reload() if not result: result = newmod new_index = min(model_idx, len(self.models) - 1) self.model_add(newmod, new_index) return result def pre_load(self, ids, display=True): if not ids: return True self.models.lock_signal = True for id in ids: newmod = ModelRecord(self.resource, id, parent=self.parent, group=self) self.model_add(newmod) #if display: # self.signal('model-changed', newmod) self.models.lock_signal = False self.signal('record-cleared') return True def load_for(self, values): self.models.lock_signal = True for value in values: newmod = ModelRecord(self.resource, value['id'], parent=self.parent, group=self) newmod.set(value) self.models.append(newmod) newmod.signal_connect(self, 'record-changed', self._record_changed) self.models.lock_signal = False self.signal('record-cleared') def load(self, ids, display=True, context={}): if not ids: return True if not self.fields: return self.pre_load(ids, display) c = rpc.session.context.copy() c.update(self.context) c.update(context) c['bin_size'] = True values = self.rpc.read( ids, self.fields.keys() + [rpc.CONCURRENCY_CHECK_FIELD], c) if not values: return False vdict = dict(map(lambda x: (x['id'], x), values)) v2 = [] for id in ids: if id in vdict: v2.append(vdict[id]) self.load_for(v2) self.current_idx = 0 return True def clear(self): self.models.clear() self.models_removed = [] def setContext(self, ctx): self._context.update(ctx) def getContext(self): ctx = {} ctx.update(self._context) #ctx['active_ids'] = [model.id for model in self.models if model.id] #if self.current_idx is not None: # ctx['active_id'] = self.models[self.current_idx].id or False #else: # ctx['active_id'] = False return ctx context = property(getContext, setContext) def model_add(self, model, position=-1): # # To be checked # if not model.mgroup is self: fields = {} for mf in model.mgroup.fields: fields[model.mgroup.fields[mf] ['name']] = model.mgroup.fields[mf] self.add_fields(fields, self) self.add_fields(self.fields, model.mgroup) model.mgroup = self if position == -1: self.models.append(model) else: self.models.insert(position, model) self.current_idx = position model.parent = self.parent model.signal_connect(self, 'record-changed', self._record_changed) return model def model_new(self, default=True, domain=[], context={}): newmod = ModelRecord(self.resource, None, group=self, parent=self.parent, new=True) newmod.signal_connect(self, 'record-changed', self._record_changed) if default: ctx = context.copy() ctx.update(self.context) newmod.default_get(domain, ctx) self.signal('model-changed', newmod) newmod.list_parent = self.list_parent newmod.list_group = self.list_group return newmod def model_remove(self, model): idx = self.models.index(model) self.models.remove(model) if model.parent: model.parent.modified = True if self.models: self.current_idx = min(idx, len(self.models) - 1) else: self.current_idx = None def _record_changed(self, model, signal_data): self.signal('model-changed', model) def prev(self): if self.models and self.current_idx is not None: self.current_idx = (self.current_idx - 1) % len(self.models) elif self.models: self.current_idx = 0 else: return None return self.models[self.current_idx] def next(self): if self.models and self.current_idx is not None: self.current_idx = (self.current_idx + 1) % len(self.models) elif self.models: self.current_idx = 0 else: return None return self.models[self.current_idx] def remove(self, model): try: idx = self.models.index(model) if self.models[idx].id: self.models_removed.append(self.models[idx]) self.models[idx].modified = True if model.parent: model.parent.modified = True self.models.remove(self.models[idx]) except: pass def add_fields_custom(self, fields, models): to_add = [] for f in fields.keys(): add_field = True if f in models.fields: if fields[f].get('widget', '') == models.fields[f].get('widget', ''): models.fields[f].update(fields[f]) add_field = False if f in models.mfields and fields[f].get('type', '') == 'one2many': add_field = False if add_field: models.fields[f] = fields[f] models.fields[f]['name'] = f to_add.append(f) self.mfields_load(to_add, models) for fname in to_add: for m in models.models: m.value[fname] = self.mfields[fname].create(m) return to_add def add_fields(self, fields, models, context=None): import time ct = time.time() if context is None: context = {} to_add = self.add_fields_custom(fields, models) models = models.models if not len(models): return True old = [] new = [] for model in models: if model.id: old.append(model.id) else: new.append(model) ctx = context.copy() if len(old) and len(to_add): ctx.update(rpc.session.context) ctx.update(self.context) to_add_normal = [rpc.CONCURRENCY_CHECK_FIELD] to_add_binary = [] for f in to_add: if fields[f]['type'] in ('image', 'binary'): to_add_binary.append(f) else: to_add_normal.append(f) if to_add_normal: values = self.rpc.read(old, to_add_normal, ctx) if values: for v in values: id = v['id'] if 'id' not in to_add: del v['id'] self[id].set(v, signal=False) if to_add_binary: data = {}.fromkeys(to_add_binary, None) for m in self.models: m.set(data, signal=False) if len(new) and len(to_add): ctx.update(self.context) values = self.rpc.default_get(to_add, ctx) for t in to_add: if t not in values: values[t] = False for mod in new: mod.set_default(values) def __iter__(self): return iter(self.models) def get_by_id(self, id): for model in self.models: if model.id == id: return model __getitem__ = get_by_id
class ModelRecordGroup(signal_event.signal_event): def __init__(self, resource, fields, ids=[], parent=None, context={}, is_wizard=False, screen=None): super(ModelRecordGroup, self).__init__() self._readonly = False self.parent = parent self._context = context self._context.update(rpc.session.context) self.resource = resource self.rpc = RPCProxy(resource) self.fields = fields self.mfields = {} self.mfields_load(fields.keys(), self) self.screen = screen self.models = ModelList(self) self.current_idx = None self.load(ids) self.models_removed = [] self.on_write = '' self.is_wizard = is_wizard self.list_parent = False self.list_group = False def destroy(self): for field in self.mfields.values(): field.destroy() if self.list_group: self.list_group.destroy() del self.mfields del self.fields del self.list_group del self.models def index(self, model): return self.models.index(model) def mfields_load(self, fkeys, models): for fname in fkeys: fvalue = models.fields[fname] modelfield = field.ModelField(fvalue['type']) fvalue['name'] = fname models.mfields[fname] = modelfield(models, fvalue) def model_move(self, model, position=0): self.models.move(model, position) def set_sequence(self, get_id, rec_id, field='sequence'): seq_ids = [] index = 0 for model in self.models: seq_ids += [model[field].get(model)] index = index +1 set_list = list(set(seq_ids)) if len(seq_ids) != len(set_list): set_list.sort() repeat = set_list[-1] mod_list = seq_ids[len(set_list):] for e in range(len(mod_list)): repeat = repeat + 1 mod_list[e]= repeat final_list = set_list + mod_list index = 0 for model in self.models: model[field].set(model, final_list[index], modified=True) if model.id: model.save() index = index +1 else: seq_id = [] if get_id < rec_id: for x in range(get_id, rec_id): seq_id += [self.models[x][field].get(self.models[x])] sort_seq = [seq_id[-1]] + seq_id[:-1] index = 0 for x in range(get_id, rec_id): self.models[x][field].set(self.models[x], sort_seq[index], modified=True) if self.models[x].id: self.models[x].save() index = index +1 else: for x in range(rec_id,get_id+1): seq_id += [self.models[x][field].get(self.models[x])] sort_seq = seq_id[1:] + [seq_id[0]] index = 0 for x in range(rec_id,get_id+1): self.models[x][field].set(self.models[x], sort_seq[index], modified=True) if self.models[x].id: self.models[x].save() index = index +1 def save(self): for model in self.models: saved = model.save() self.writen(saved) def writen(self, edited_id): if not self.on_write: return new_ids = getattr(self.rpc, self.on_write)(edited_id, self.context) model_idx = self.models.index(self[edited_id]) result = False for id in new_ids: cont = False for m in self.models: if m.id == id: cont = True m.reload() if cont: continue newmod = ModelRecord(self.resource, id, parent=self.parent, group=self) newmod.reload() if not result: result = newmod new_index = min(model_idx, len(self.models)-1) self.model_add(newmod, new_index) return result def pre_load(self, ids, display=True): if not ids: return True self.models.lock_signal = True for id in ids: newmod = ModelRecord(self.resource, id, parent=self.parent, group=self) self.model_add(newmod) #if display: # self.signal('model-changed', newmod) self.models.lock_signal = False self.signal('record-cleared') return True def load_for(self, values): self.models.lock_signal = True for value in values: newmod = ModelRecord(self.resource, value['id'], parent=self.parent, group=self) newmod.set(value) self.models.append(newmod) newmod.signal_connect(self, 'record-changed', self._record_changed) self.models.lock_signal = False self.signal('record-cleared') def load(self, ids, display=True, context={}): if not ids: return True if not self.fields: return self.pre_load(ids, display) c = rpc.session.context.copy() c.update(self.context) c.update(context) c['bin_size'] = True values = self.rpc.read(ids, self.fields.keys() + [rpc.CONCURRENCY_CHECK_FIELD], c) if not values: return False vdict = dict(map(lambda x: (x['id'], x), values)) v2 = [] for id in ids: if id in vdict: v2.append(vdict[id]) self.load_for(v2) self.current_idx = 0 return True def clear(self): self.models.clear() self.models_removed = [] def setContext(self, ctx): self._context.update(ctx) def getContext(self): ctx = {} ctx.update(self._context) #ctx['active_ids'] = [model.id for model in self.models if model.id] #if self.current_idx is not None: # ctx['active_id'] = self.models[self.current_idx].id or False #else: # ctx['active_id'] = False return ctx context = property(getContext, setContext) def model_add(self, model, position=-1): # # To be checked # if not model.mgroup is self: fields = {} for mf in model.mgroup.fields: fields[model.mgroup.fields[mf]['name']] = model.mgroup.fields[mf] self.add_fields(fields, self) self.add_fields(self.fields, model.mgroup) model.mgroup = self if position==-1: self.models.append(model) else: self.models.insert(position, model) self.current_idx = position model.parent = self.parent model.signal_connect(self, 'record-changed', self._record_changed) return model def model_new(self, default=True, domain=[], context={}): newmod = ModelRecord(self.resource, None, group=self, parent=self.parent, new=True) newmod.signal_connect(self, 'record-changed', self._record_changed) if default: ctx=context.copy() ctx.update(self.context) newmod.default_get(domain, ctx) self.signal('model-changed', newmod) newmod.list_parent = self.list_parent newmod.list_group = self.list_group return newmod def model_remove(self, model): idx = self.models.index(model) self.models.remove(model) if model.parent: model.parent.modified = True if self.models: self.current_idx = min(idx, len(self.models)-1) else: self.current_idx = None def _record_changed(self, model, signal_data): self.signal('model-changed', model) def prev(self): if self.models and self.current_idx is not None: self.current_idx = (self.current_idx - 1) % len(self.models) elif self.models: self.current_idx = 0 else: return None return self.models[self.current_idx] def next(self): if self.models and self.current_idx is not None: self.current_idx = (self.current_idx + 1) % len(self.models) elif self.models: self.current_idx = 0 else: return None return self.models[self.current_idx] def remove(self, model): try: idx = self.models.index(model) if self.models[idx].id: self.models_removed.append(self.models[idx]) self.models[idx].modified = True if model.parent: model.parent.modified = True self.models.remove(self.models[idx]) except: pass def add_fields_custom(self, fields, models): to_add = [] for f in fields.keys(): add_field = True if f in models.fields: if fields[f].get('widget','') == models.fields[f].get('widget',''): models.fields[f].update(fields[f]) add_field = False if f in models.mfields and fields[f].get('type','') == 'one2many': add_field = False if add_field: models.fields[f] = fields[f] models.fields[f]['name'] = f to_add.append(f) self.mfields_load(to_add, models) for fname in to_add: for m in models.models: m.value[fname] = self.mfields[fname].create(m) return to_add def add_fields(self, fields, models, context=None): import time ct = time.time() if context is None: context = {} to_add = self.add_fields_custom(fields, models) models = models.models if not len(models): return True old = [] new = [] for model in models: if model.id: old.append(model.id) else: new.append(model) ctx = context.copy() if len(old) and len(to_add): ctx.update(rpc.session.context) ctx.update(self.context) to_add_normal = [rpc.CONCURRENCY_CHECK_FIELD] to_add_binary = [] for f in to_add: if fields[f]['type'] in ('image', 'binary'): to_add_binary.append(f) else: to_add_normal.append(f) if to_add_normal: values = self.rpc.read(old, to_add_normal, ctx) if values: for v in values: id = v['id'] if 'id' not in to_add: del v['id'] self[id].set(v, signal=False) if to_add_binary: data = {}.fromkeys(to_add_binary, None) for m in self.models: m.set(data, signal=False) if len(new) and len(to_add): if self.parent and self.screen: ctx.update(self.parent.expr_eval(self.screen.default_get)) ctx.update(self.context) values = self.rpc.default_get(to_add, ctx) for t in to_add: if t not in values: values[t] = False for mod in new: mod.set_default(values) if values: mod.modified = True def __iter__(self): return iter(self.models) def get_by_id(self, id): for model in self.models: if model.id == id: return model __getitem__ = get_by_id
class ModelRecord(signal_event.signal_event): def __init__(self, resource, id, group=None, parent=None, new=False ): super(ModelRecord, self).__init__() self.resource = str(resource) self.rpc = RPCProxy(self.resource) self.id = id self._loaded = False self.parent = parent self.mgroup = group self.value = {} self.state_attrs = {} self.modified = False self.modified_fields = {} self._concurrency_check_data = False for key,val in self.mgroup.mfields.items(): self.value[key] = val.create(self) if (new and val.attrs['type']=='one2many') and (val.attrs.get('mode','tree,form').startswith('form')): mod = self.value[key].model_new() self.value[key].model_add(mod) def __getitem__(self, name): return self.mgroup.mfields.get(name, False) def __repr__(self): return '<ModelRecord %s@%s>' % (self.id, self.resource) def is_modified(self): return self.modified def is_wizard(self): return self.mgroup.is_wizard def fields_get(self): return self.mgroup.mfields def _check_load(self): if not self._loaded: self.reload() return True return False def update_context_with_concurrency_check_data(self, context): if self.id and self.is_modified(): context.setdefault(CONCURRENCY_CHECK_FIELD, {})["%s,%d" % (self.resource, self.id)] = self._concurrency_check_data for name, field in self.mgroup.mfields.items(): if isinstance(field, O2MField): if field.name not in self.value.keys(): continue v = self.value[field.name] from itertools import chain for m in chain(v.models, v.models_removed): m.update_context_with_concurrency_check_data(context) def get(self, get_readonly=True, includeid=False, check_load=True, get_modifiedonly=False): if check_load: self._check_load() value = [] for name, field in self.mgroup.mfields.items(): if (get_readonly or not field.get_state_attrs(self).get('readonly', False)) \ and (not get_modifiedonly or (field.name in self.modified_fields or isinstance(field, O2MField))): value.append((name, field.get(self, readonly=get_readonly, modified=get_modifiedonly))) value = dict(value) if includeid: value['id'] = self.id return value def cancel(self): self._loaded = False self.reload() def failed_validation(self): invalid_fields = self.rpc.get_invalid_fields() for item in invalid_fields: if item in self.mgroup.mfields: self.mgroup.mfields[item].get_state_attrs(self)['valid'] = False def save(self, reload=True): self._check_load() if not self.id: value = self.get(get_readonly=False) self.id = self.rpc.create(value, self.context_get()) if not self.id: self.failed_validation() else: if not self.is_modified(): return self.id value = self.get(get_readonly=False, get_modifiedonly=True) context = self.context_get().copy() self.update_context_with_concurrency_check_data(context) if not self.rpc.write([self.id], value, context): self.failed_validation() return False self._loaded = False if reload: self.reload() else: # just reload the CONCURRENCY_CHECK_FIELD self._reload([CONCURRENCY_CHECK_FIELD]) return self.id def default_get(self, domain=[], context={}): if len(self.mgroup.fields): val = self.rpc.default_get(self.mgroup.fields.keys(), context) for d in domain: if d[0] in self.mgroup.fields: if d[1] == '=': val[d[0]] = d[2] if d[1] == 'in' and len(d[2]) == 1: val[d[0]] = d[2][0] self.set_default(val) def name_get(self): name = self.rpc.name_get([self.id], rpc.session.context)[0] return name def validate_set(self): change = self._check_load() for fname in self.mgroup.mfields: field = self.mgroup.mfields[fname] change = change or not field.get_state_attrs(self).get('valid', True) field.get_state_attrs(self)['valid'] = True if change: self.signal('record-changed') self.reload() return change def validate(self): self._check_load() ok = True for fname in self.mgroup.mfields: if not self.mgroup.mfields[fname].validate(self): ok = False return ok def _get_invalid_fields(self): res = [] for fname, field in self.mgroup.mfields.items(): if not field.get_state_attrs(self).get('valid', True): res.append((fname, field.attrs['string'])) return dict(res) invalid_fields = property(_get_invalid_fields) def context_get(self): return self.mgroup.context def get_default(self): self._check_load() value = dict([(name, field.get_default(self)) for name, field in self.mgroup.mfields.items()]) return value def set_default(self, val): for fieldname, value in val.items(): if fieldname not in self.mgroup.mfields: continue self.mgroup.mfields[fieldname].set_default(self, value) self._loaded = True self.signal('record-changed') def set(self, val, modified=False, signal=True): later={} for fieldname, value in val.items(): if fieldname == CONCURRENCY_CHECK_FIELD: self._concurrency_check_data = value if fieldname not in self.mgroup.mfields: continue if isinstance(self.mgroup.mfields[fieldname], field.O2MField): later[fieldname]=value continue self.mgroup.mfields[fieldname].set(self, value, modified=modified) for fieldname, value in later.items(): self.mgroup.mfields[fieldname].set(self, value, modified=modified) self._loaded = True self.modified = modified if not self.modified: self.modified_fields = {} if signal: self.signal('record-changed') def reload(self): return self._reload(self.mgroup.mfields.keys() + [CONCURRENCY_CHECK_FIELD]) def _reload(self, fields): if not self.id: return c = rpc.session.context.copy() c.update(self.context_get()) c['bin_size'] = True res = self.rpc.read([self.id], fields, c) if res: value = res[0] if self.parent: self.set(value, signal=False) else: self.set(value) def expr_eval(self, dom, check_load=True): if not isinstance(dom, basestring): return dom if check_load: self._check_load() d = {} for name, mfield in self.mgroup.mfields.items(): d[name] = mfield.get(self, check_load=check_load) d['current_date'] = time.strftime('%Y-%m-%d') d['time'] = time d['context'] = self.context_get() d['active_id'] = self.id if self.parent: d['parent'] = EvalEnvironment(self.parent) val = tools.expr_eval(dom, d) return val #XXX Shoud use changes of attributes (ro, ...) def on_change(self, callback): match = re.match('^(.*?)\((.*)\)$', callback) if not match: raise Exception, 'ERROR: Wrong on_change trigger: %s' % callback func_name = match.group(1) arg_names = [n.strip() for n in match.group(2).split(',') if n.strip()] args = [self.expr_eval(arg) for arg in arg_names] ids = self.id and [self.id] or [] response = getattr(self.rpc, func_name)(ids, *args) if response: self.set(response.get('value', {}), modified=True) if 'domain' in response: for fieldname, value in response['domain'].items(): if fieldname not in self.mgroup.mfields: continue self.mgroup.mfields[fieldname].attrs['domain'] = value warning=response.get('warning',{}) if warning: common.warning(warning['message'], warning['title']) self.signal('record-changed') def on_change_attrs(self, callback): self.signal('attrs-changed') def cond_default(self, field, value): ir = RPCProxy('ir.values') values = ir.get('default', '%s=%s' % (field, value), [(self.resource, False)], False, {}) data = {} for index, fname, value in values: data[fname] = value self.set_default(data)