00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 import re
00030 import time
00031 import exceptions
00032 from Koo import Rpc
00033 from Koo.Common import Notifier
00034 from Koo.Rpc import RpcProxy
00035 from Field import ToManyField
00036 import gettext
00037
00038 from PyQt4.QtCore import *
00039
00040
00041 ConcurrencyCheckField = 'read_delta'
00042
00043 class EvalEnvironment(object):
00044 def __init__(self, parent):
00045 self.parent = parent
00046
00047 def __getattr__(self, item):
00048 if item=='parent' and self.parent.parent:
00049 return EvalEnvironment(self.parent.parent)
00050 if item=="current_date":
00051 return time.strftime('%Y-%m-%d')
00052 if item=="time":
00053 return time
00054 return self.parent.get(includeid=True)[item]
00055
00056 def __del__(self):
00057 self.parent = None
00058
00059
00060 class Record(QObject):
00061 def __init__(self, id, group, parent=None, new=False ):
00062 QObject.__init__(self, group)
00063 self.rpc = group.rpc
00064 self.id = id
00065 self._loaded = False
00066 self.parent = parent
00067 self.group = group
00068 self.values = {}
00069 self._stateAttributes = {}
00070 self.modified = False
00071 self.modified_fields = {}
00072 self.invalidFields = []
00073 self.read_time = time.time()
00074 self.new = new
00075
00076 def __del__(self):
00077 self.rpc = None
00078 self.modified_fields = None
00079 self.parent = None
00080 self.setParent( None )
00081 for key, value in self.values.iteritems():
00082 from Group import RecordGroup
00083 if isinstance(value, RecordGroup):
00084
00085 value.__del__()
00086 self.values = {}
00087 self.invalidFields = []
00088 from Koo.Common import Debug
00089 if self.id == 2:
00090 Debug.printReferrers( self )
00091 self.group = None
00092
00093 def _getModified(self):
00094 return self._modified
00095 def _setModified(self,value):
00096 self._modified = value
00097 modified=property(_getModified,_setModified)
00098
00099 def __getitem__(self, name):
00100 return self.group.fieldObjects.get(name, False)
00101
00102 def __repr__(self):
00103
00104 return '<Record %s>' % self.id
00105
00106
00107 def setValue(self, fieldName, value):
00108 if not fieldName in self.values:
00109 self.group.ensureModelLoaded( self )
00110 self.group.fieldObjects[fieldName].set_client(self, value)
00111
00112
00113 def value(self, fieldName):
00114 if not fieldName in self.values:
00115 self.group.ensureModelLoaded( self )
00116 return self.group.fieldObjects[fieldName].get_client(self)
00117
00118
00119 def setDefault(self, fieldName, value):
00120 self.group.fieldObjects[fieldName].set_client(self, value)
00121
00122
00123 def default(self, fieldName):
00124 return self.group.fieldObjects[fieldName].default(self)
00125
00126
00127 def domain(self, fieldName):
00128 return self.group.fieldObjects[fieldName].domain(self)
00129
00130
00131 def fieldContext(self, fieldName):
00132
00133
00134
00135 return self.group.fieldObjects[fieldName].context(self, checkLoad=False)
00136
00137
00138 def isModified(self):
00139 return self.modified
00140
00141 def fields(self):
00142 return self.group.fieldObjects
00143
00144 def modifiedFields(self):
00145 return self.modified_fields.keys()
00146
00147 def stateAttributes(self, fieldName):
00148 if fieldName not in self._stateAttributes:
00149 if fieldName in self.group.fieldObjects:
00150 self._stateAttributes[fieldName] = self.group.fieldObjects[fieldName].attrs.copy()
00151 else:
00152 self._stateAttributes[fieldName] = {}
00153 return self._stateAttributes[fieldName]
00154
00155 def setStateAttributes(self, fieldName, state='draft'):
00156 field = self.group.fieldObjects[fieldName]
00157 stateChanges = dict(field.attrs.get('states',{}).get(state,[]))
00158 for key in ('readonly', 'required'):
00159 if key in stateChanges:
00160 self.stateAttributes(fieldName)[key] = stateChanges[key]
00161 else:
00162 self.stateAttributes(fieldName)[key] = field.attrs.get(key, False)
00163
00164 def updateStateAttributes(self):
00165 state = self.values.get('state', 'draft')
00166 for key in self.group.fieldObjects:
00167 self.setStateAttributes( key, state )
00168
00169 def updateAttributes(self):
00170 self.updateStateAttributes()
00171 for fieldName in self.group.fieldObjects:
00172 attributes = self.group.fieldObjects[fieldName].attrs.get( 'attrs', '{}' )
00173 try:
00174 attributeChanges = eval( attributes )
00175 except:
00176 attributeChanges = eval( attributes, self.value( fieldName ) )
00177
00178 for attribute, condition in attributeChanges.items():
00179 for i in range(0, len(condition)):
00180 if condition[i][2] and isinstance(condition[i][2], list):
00181 attributeChanges[attribute][i] = (condition[i][0], condition[i][1], condition[i][2][0])
00182 for attribute, condition in attributeChanges.iteritems():
00183 value = self.evaluateCondition( condition )
00184 if value:
00185 self.stateAttributes( fieldName )[ attribute ] = value
00186
00187 def isFieldReadOnly(self, fieldName):
00188 readOnly = self.stateAttributes( fieldName ).get('readonly', False)
00189 if isinstance(readOnly, bool):
00190 return readOnly
00191 if isinstance(readOnly, str) or isinstance(readOnly, unicode):
00192 readOnly = readOnly.strip()
00193 if readOnly.lower() == 'true' or readOnly == '1':
00194 return True
00195 if readOnly.lower() == 'false' or readOnly == '0':
00196 return False
00197 return bool(readOnly)
00198
00199 def isFieldRequired(self, fieldName):
00200 required = self.stateAttributes( fieldName ).get('required', False)
00201 if isinstance(required, bool):
00202 return required
00203 if isinstance(required, str) or isinstance(required, unicode):
00204 required = required.strip()
00205 if required.lower() == 'true' or required == '1':
00206 return True
00207 if required.lower() == 'false' or required == '0':
00208 return False
00209 return bool(required)
00210
00211 def fieldExists(self, fieldName):
00212 return fieldName in self.group.fieldObjects
00213
00214
00215 def ensureIsLoaded(self):
00216 if not self._loaded:
00217 self.reload()
00218 return True
00219 return False
00220
00221 def get(self, get_readonly=True, includeid=False, checkLoad=True, get_modifiedonly=False):
00222 if checkLoad:
00223 self.ensureIsLoaded()
00224 value = {}
00225
00226
00227
00228 for name in self.group.fields:
00229 field = self.group.fieldObjects[ name ]
00230
00231
00232
00233 if not name in self.values:
00234 continue
00235 if not name in self.values:
00236 continue
00237 if (get_readonly or not self.isFieldReadOnly(name)) \
00238 and (not get_modifiedonly or field.name in self.modified_fields):
00239 value[name] = field.get(self, readonly=get_readonly, modified=get_modifiedonly)
00240 if includeid:
00241 value['id'] = self.id
00242 return value
00243
00244
00245 def cancel(self):
00246 self._modified = False
00247 self._loaded = False
00248
00249
00250 def save(self, reload=True):
00251 self.ensureIsLoaded()
00252 if not self.id:
00253 value = self.get(get_readonly=False)
00254 self.id = self.rpc.create(value, self.context())
00255 else:
00256 if not self.isModified():
00257 return self.id
00258 value = self.get(get_readonly=False, get_modifiedonly=True)
00259 context= self.context()
00260 context= context.copy()
00261 context[ConcurrencyCheckField]= time.time()-self.read_time
00262 if not self.rpc.write([self.id], value, context):
00263 return False
00264 self._loaded = False
00265 if reload:
00266 self.reload()
00267 if self.group:
00268 self.group.written(self.id)
00269 return self.id
00270
00271
00272
00273 def fillWithDefaults(self, domain=None, context=None):
00274 if domain is None:
00275 domain = []
00276 if context is None:
00277 context = {}
00278 if len(self.group.fields):
00279 val = self.rpc.default_get(self.group.fields.keys(), context)
00280 for d in domain:
00281 if d[0] in self.group.fields:
00282 if d[1] == '=':
00283 val[d[0]] = d[2]
00284 elif d[1] == 'in' and len(d[2]) == 1:
00285 val[d[0]] = d[2][0]
00286 self.setDefaults(val)
00287 self.updateAttributes()
00288
00289
00290
00291 def name(self):
00292 name = self.rpc.name_get([self.id], Rpc.session.context)[0]
00293 return name
00294
00295 def setFieldValid(self, field, value):
00296 if value:
00297 if field in self.invalidFields:
00298 self.invalidFields.remove( field )
00299 else:
00300 if not field in self.invalidFields:
00301 self.invalidFields.append( field )
00302
00303 def isFieldValid(self, field):
00304 if field in self.invalidFields:
00305 return False
00306 else:
00307 return True
00308
00309 def setValidate(self):
00310 change = self.ensureIsLoaded()
00311 self.invalidFields = []
00312 for fname in self.group.fieldObjects:
00313 change = change or not self.isFieldValid( fname )
00314 self.setFieldValid( fname, True )
00315 if change:
00316 self.emit(SIGNAL('recordChanged( PyQt_PyObject )'), self )
00317 return change
00318
00319
00320 def validate(self):
00321 self.ensureIsLoaded()
00322 ok = True
00323 for name in self.group.fieldObjects:
00324
00325
00326
00327 if not name in self.values:
00328 continue
00329 if not self.group.fieldObjects[name].validate(self):
00330 self.setFieldValid( name, False )
00331 ok = False
00332 else:
00333 self.setFieldValid( name, True )
00334 return ok
00335
00336
00337 def context(self):
00338 return self.group.context()
00339
00340
00341
00342 def defaults(self):
00343 self.ensureIsLoaded()
00344 value = dict([(name, field.default(self))
00345 for name, field in self.group.fieldObjects.items()])
00346 return value
00347
00348
00349
00350 def setDefaults(self, val):
00351 self.createMissingFields()
00352 for fieldname, value in val.items():
00353 if fieldname not in self.group.fieldObjects:
00354 continue
00355 self.group.fieldObjects[fieldname].setDefault(self, value)
00356 self._loaded = True
00357 self.emit(SIGNAL('recordChanged( PyQt_PyObject )'), self)
00358 self.emit(SIGNAL('recordModified( PyQt_PyObject )'), self)
00359
00360
00361
00362
00363
00364 def changed(self):
00365 self.updateAttributes()
00366 self.emit(SIGNAL('recordChanged( PyQt_PyObject )'), self)
00367 self.emit(SIGNAL('recordModified( PyQt_PyObject )'), self)
00368
00369 def set(self, val, modified=False, signal=True):
00370
00371 self.createMissingFields()
00372
00373 later={}
00374 for fieldname, value in val.items():
00375 if fieldname not in self.group.fieldObjects:
00376 continue
00377 if isinstance(self.group.fieldObjects[fieldname], ToManyField):
00378 later[fieldname]=value
00379 continue
00380 self.group.fieldObjects[fieldname].set(self, value, modified=modified)
00381 for fieldname, value in later.items():
00382 self.group.fieldObjects[fieldname].set(self, value, modified=modified)
00383
00384 self.updateAttributes()
00385
00386 self._loaded = True
00387 self.modified = modified
00388 if not self.modified:
00389 self.modified_fields = {}
00390 self.emit(SIGNAL('recordChanged( PyQt_PyObject )'), self)
00391 if signal:
00392 self.emit(SIGNAL('recordModified( PyQt_PyObject )'), self)
00393
00394 def reload(self):
00395 if not self.id:
00396 return
00397 c= Rpc.session.context.copy()
00398 c.update(self.context())
00399 c['bin_size'] = True
00400 res = self.rpc.read([self.id], self.group.allFieldNames(), c)
00401 if res:
00402
00403 value = res[0]
00404 self.read_time= time.time()
00405
00406
00407 self.set(value, signal=False)
00408
00409
00410
00411
00412
00413 def evaluateExpression(self, dom, checkLoad=True, firstTry=True):
00414 if not isinstance(dom, basestring):
00415 return dom
00416 if checkLoad:
00417 self.ensureIsLoaded()
00418 d = {}
00419 for name in self.values:
00420 d[name] = self.group.fieldObjects[name].get(self, checkLoad=checkLoad)
00421
00422 d['current_date'] = time.strftime('%Y-%m-%d')
00423 d['time'] = time
00424 d['context'] = self.context()
00425
00426
00427 d['active_id'] = self.id or False
00428
00429
00430
00431 d['id'] = self.id or False
00432 if self.parent:
00433 d['parent'] = EvalEnvironment(self.parent)
00434 try:
00435 val = Rpc.session.evaluateExpression(dom, d)
00436 except NameError, exception:
00437
00438
00439
00440
00441
00442
00443 if firstTry:
00444 self.group.ensureModelLoaded( self )
00445 val = self.evaluateExpression( dom, checkLoad, firstTry=False )
00446 else:
00447 raise exception
00448 return val
00449
00450
00451
00452
00453 def evaluateCondition(self, condition):
00454
00455 if isinstance(condition, list):
00456 result = True
00457 for c in condition:
00458 result = result and self.evaluateCondition( c )
00459 return result
00460
00461 if not self.fieldExists( condition[0] ):
00462 return False
00463 value = self.value( condition[0] )
00464 from Group import RecordGroup
00465 if isinstance(value, RecordGroup):
00466 value = value.ids()
00467 if condition[1] in ('=', '=='):
00468 if value == condition[2]:
00469 return True
00470 elif condition[1] in ('!=', '<>'):
00471 if value != condition[2]:
00472 return True
00473 elif condition[1] == '<':
00474 if value < condition[2]:
00475 return True
00476 elif condition[1] == '>':
00477 if value > condition[2]:
00478 return True
00479 elif condition[1] == '<=':
00480 if value <= condition[2]:
00481 return True
00482 elif condition[1] == '>=':
00483 if value >= condition[2]:
00484 return True
00485 elif condition[1].lower() == 'in':
00486 for cond in condition[2]:
00487 if value == cond:
00488 return True
00489 elif condition[1].lower() == 'not in':
00490 for cond in condition[2]:
00491 if value == cond:
00492 return False
00493 return True
00494 return False
00495
00496
00497
00498
00499
00500
00501
00502 def callOnChange(self, callback):
00503 match = re.match('^(.*?)\((.*)\)$', callback)
00504 if not match:
00505 raise Exception, 'ERROR: Wrong on_change trigger: %s' % callback
00506 func_name = match.group(1)
00507 arg_names = [n.strip() for n in match.group(2).split(',')]
00508 args = [self.evaluateExpression(arg) for arg in arg_names]
00509 ids = self.id and [self.id] or []
00510 response = getattr(self.rpc, func_name)(ids, *args)
00511 if response:
00512 self.set(response.get('value', {}), modified=True)
00513 if 'domain' in response:
00514 for fieldname, value in response['domain'].items():
00515 if fieldname not in self.group.fieldObjects:
00516 continue
00517 self.group.fieldObjects[fieldname].attrs['domain'] = value
00518 warning = response.get('warning',{})
00519 if warning:
00520 Notifier.notifyWarning(warning['title'], warning['message'])
00521 if 'focus' in response:
00522 self.emit(SIGNAL('setFocus(QString)'), response['focus'])
00523
00524
00525
00526
00527
00528
00529
00530
00531 def setConditionalDefaults(self, field, value):
00532 ir = RpcProxy('ir.values')
00533 values = ir.get('default', '%s=%s' % (field, value),
00534 [(self.group.resource, False)], False, {})
00535 data = {}
00536 for index, fname, value in values:
00537 data[fname] = value
00538 self.setDefaults(data)
00539
00540
00541
00542 def isFullyLoaded(self):
00543 if not self._loaded:
00544 return False
00545 if set(self.values.keys()) == set(self.group.fieldObjects.keys()):
00546 return True
00547 else:
00548 return False
00549
00550
00551 def isWizard(self):
00552 return self.group.isWizard()
00553
00554
00555
00556 def missingFields(self):
00557 return list( set(self.group.fieldObjects.keys()) - set(self.values.keys()) )
00558
00559
00560
00561 def createMissingFields(self):
00562
00563
00564 if len(self.group.fieldObjects) == len(self.values):
00565 return
00566 for key in self.missingFields():
00567 val = self.group.fieldObjects[key]
00568 self.values[key] = val.create(self)
00569 if (self.new and val.attrs['type']=='one2many') and (val.attrs.get('mode','tree,form').startswith('form')):
00570 self.values[key].create()