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()