diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index d69688bf6fc..641e9d30006 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -415,7 +415,8 @@ class ir_model_data(osv.osv): _defaults = { 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), 'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'noupdate': lambda *a: False + 'noupdate': lambda *a: False, + 'module': lambda *a: '' } _sql_constraints = [ ('module_name_uniq', 'unique(name, module)', 'You can not have multiple records with the same id for the same module'), diff --git a/bin/addons/base/module/report/ir_module_reference_print.py b/bin/addons/base/module/report/ir_module_reference_print.py index 14f23a57541..7b379f2af68 100644 --- a/bin/addons/base/module/report/ir_module_reference_print.py +++ b/bin/addons/base/module/report/ir_module_reference_print.py @@ -25,7 +25,7 @@ from report import report_sxw class ir_module_reference_print(report_sxw.rml_parse): def __init__(self, cr, uid, name, context): - super(ir_module_reference_print, self).__init__(cr, uid, name, context) + super(ir_module_reference_print, self).__init__(cr, uid, name, context=context) self.localcontext.update({ 'time': time, 'findobj': self._object_find, diff --git a/bin/osv/expression.py b/bin/osv/expression.py index b488cea535c..a70b3afa883 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -44,18 +44,28 @@ class expression(object): and (((not internal) and element[1] in OPS) \ or (internal and element[1] in INTERNAL_OPS)) - def __execute_recursive_in(self, cr, s, f, w, ids): + def __execute_recursive_in(self, cr, s, f, w, ids, op, type): # todo: merge into parent query as sub-query res = [] - for i in range(0, len(ids), cr.IN_MAX): - subids = ids[i:i+cr.IN_MAX] - cr.execute('SELECT "%s"' \ - ' FROM "%s"' \ + if ids: + if op in ['<','>','>=','<=']: + cr.execute('SELECT "%s"' \ + ' FROM "%s"' \ + ' WHERE "%s" %s %s' % (s, f, w, op, ids[0])) + res.extend([r[0] for r in cr.fetchall()]) + else: + for i in range(0, len(ids), cr.IN_MAX): + subids = ids[i:i+cr.IN_MAX] + cr.execute('SELECT "%s"' \ + ' FROM "%s"' \ ' WHERE "%s" = ANY (%%s)' % (s, f, w), (subids,)) + res.extend([r[0] for r in cr.fetchall()]) + else: + cr.execute('SELECT distinct("%s")' \ + ' FROM "%s" where "%s" is not null' % (s, f, s)), res.extend([r[0] for r in cr.fetchall()]) return res - def __init__(self, exp): # check if the expression is valid if not reduce(lambda acc, val: acc and (self._is_operator(val) or self._is_leaf(val)), exp, True): @@ -101,7 +111,6 @@ class expression(object): if self._is_operator(e) or e == self.__DUMMY_LEAF: continue left, operator, right = e - working_table = table main_table = table fargs = left.split('.', 1) @@ -154,14 +163,32 @@ class expression(object): elif field._type == 'one2many': - if isinstance(right, basestring): - ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None)] - else: - ids2 = list(right) - if not ids2: - self.__exp[i] = ('id', '=', '0') - else: - self.__exp[i] = ('id', 'in', self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2)) + call_null = True + + if right: + if isinstance(right, basestring): + ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None)] + operator = 'in' + else: + if not isinstance(right,list): + ids2 = [right] + else: + ids2 = right + if not ids2: + call_null = True + operator = 'in' # operator changed because ids are directly related to main object + else: + call_null = False + o2m_op = 'in' + if operator in ['not like','not ilike','not in','<>','!=']: + o2m_op = 'not in' + self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2, operator, field._type)) + + if call_null: + o2m_op = 'not in' + if operator in ['not like','not ilike','not in','<>','!=']: + o2m_op = 'in' + self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', [], operator, field._type) or [0]) elif field._type == 'many2many': #FIXME @@ -174,17 +201,38 @@ class expression(object): def _rec_convert(ids): if field_obj == table: return ids - return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids) + return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids, operator, field._type) dom = _rec_get(ids2, field_obj) ids2 = field_obj.search(cr, uid, dom, context=context) self.__exp[i] = ('id', 'in', _rec_convert(ids2)) else: - if isinstance(right, basestring): - res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator)] - else: - res_ids = list(right) - self.__exp[i] = ('id', 'in', self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids) or [0]) + call_null_m2m = True + if right: + if isinstance(right, basestring): + res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator)] + operator = 'in' + else: + if not isinstance(right, list): + res_ids = [right] + else: + res_ids = right + if not res_ids: + call_null_m2m = True + operator = 'in' # operator changed because ids are directly related to main object + else: + call_null_m2m = False + m2m_op = 'in' + if operator in ['not like','not ilike','not in','<>','!=']: + m2m_op = 'not in' + + self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids, operator, field._type) or [0]) + if call_null_m2m: + m2m_op = 'not in' + if operator in ['not like','not ilike','not in','<>','!=']: + m2m_op = 'in' + self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, [], operator, field._type) or [0]) + elif field._type == 'many2one': if operator == 'child_of': if isinstance(right, basestring): diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 7ef12a727e5..1b7c32d1bdc 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -459,6 +459,15 @@ class orm_template(object): return False return '' + def selection_field(in_field): + col_obj = self.pool.get(in_field.keys()[0]) + if f[i] in col_obj._columns.keys(): + return col_obj._columns[f[i]] + elif f[i] in col_obj._inherits.keys(): + selection_field(col_obj._inherits) + else: + return False + lines = [] data = map(lambda x: '', range(len(fields))) done = [] @@ -482,7 +491,19 @@ class orm_template(object): else: break else: - r = r[f[i]] + r = r[f[i]] + # To display external name of selection field when its exported + if not context.get('import_comp',False):# Allow external name only if its not import compatible + cols = False + if f[i] in self._columns.keys(): + cols = self._columns[f[i]] + elif f[i] in self._inherit_fields.keys(): + cols = selection_field(self._inherits) + if cols and cols._type == 'selection': + sel_list = cols.selection + if type(sel_list) == type([]): + r = [x[1] for x in sel_list if r==x[0]][0] + if not r: if f[i] in self._columns: r = check_type(self._columns[f[i]]._type) @@ -588,6 +609,9 @@ class orm_template(object): continue field = fields[i] + if prefix and not prefix[0] in field: + continue + if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':db_id'): # Database ID res = False @@ -646,7 +670,7 @@ class orm_template(object): ['res_id']) if res_res_id: res_id = res_res_id[0]['res_id'] - row[field[0][:-3]] = res_id or False + row[field[-1][:-3]] = res_id or False continue if (len(field) == len(prefix)+1) and \ len(field[len(prefix)].split(':lang=')) == 2: @@ -682,7 +706,7 @@ class orm_template(object): try: _check_db_id(self, model_name, line[i]) data_res_id = is_db_id = int(line[i]) - except Exception,e: + except Exception,e: warning += [tools.exception_to_unicode(e)] logger.notifyChannel("import", netsvc.LOG_ERROR, tools.exception_to_unicode(e)) @@ -1071,19 +1095,37 @@ class orm_template(object): buttons = (n for n in node.getElementsByTagName('button') if n.getAttribute('type') != 'object') for button in buttons: - ok = True + can_click = True if user != 1: # admin user has all roles user_roles = usersobj.read(cr, user, [user], ['roles_id'])[0]['roles_id'] - cr.execute("select role_id from wkf_transition where signal=%s", (button.getAttribute('name'),)) + # TODO handle the case of more than one workflow for a model + cr.execute("""SELECT DISTINCT t.role_id + FROM wkf + INNER JOIN wkf_activity a ON a.wkf_id = wkf.id + INNER JOIN wkf_transition t ON (t.act_to = a.id) + WHERE wkf.osv = %s + AND t.signal = %s + """, (self._name, button.getAttribute('name'),)) roles = cr.fetchall() - for role in roles: - if role[0]: - ok = ok and rolesobj.check(cr, user, user_roles, role[0]) + + # draft -> valid = signal_next (role X) + # draft -> cancel = signal_cancel (no role) + # + # valid -> running = signal_next (role Y) + # valid -> cancel = signal_cancel (role Z) + # + # running -> done = signal_next (role Z) + # running -> cancel = signal_cancel (role Z) + - if not ok: - button.setAttribute('readonly', '1') - else: - button.setAttribute('readonly', '0') + # As we don't know the object state, in this scenario, + # the button "signal_cancel" will be always shown as there is no restriction to cancel in draft + # the button "signal_next" will be show if the user has any of the roles (X Y or Z) + # The verification will be made later in workflow process... + if roles: + can_click = any((not role) or rolesobj.check(cr, user, user_roles, role) for (role,) in roles) + + button.setAttribute('readonly', str(int(not can_click))) arch = node.toxml(encoding="utf-8").replace('\t', '') fields = self.fields_get(cr, user, fields_def.keys(), context) @@ -1221,6 +1263,15 @@ class orm_template(object): model = True sql_res = False while ok: + view_ref = context.get(view_type + '_view_ref', False) + if view_ref: + if '.' in view_ref: + module, view_ref = view_ref.split('.', 1) + cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref)) + view_ref_res = cr.fetchone() + if view_ref_res: + view_id = view_ref_res[0] + if view_id: where = (model and (" and model='%s'" % (self._name,))) or '' cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%s'+where, (view_id,)) @@ -1462,6 +1513,8 @@ class orm_memory(orm_template): return result def write(self, cr, user, ids, vals, context=None): + if not ids: + return True vals2 = {} upd_todo = [] for field in vals: @@ -1556,7 +1609,7 @@ class orm_memory(orm_template): # get the default values from the context for key in context or {}: - if key.startswith('default_'): + if key.startswith('default_') and (key[8:] in fieds_list): value[key[8:]] = context[key] return value @@ -2122,10 +2175,12 @@ class orm(orm_template): else: select = map(int,ids) result = self._read_flat(cr, user, select, fields, context, load) + for r in result: for key, v in r.items(): if v == None: r[key] = False + if isinstance(ids, (int, long)): return result and result[0] or False return result diff --git a/bin/report/render/rml2pdf/trml2pdf.py b/bin/report/render/rml2pdf/trml2pdf.py index dcaf6fe03fd..eb8d3488da1 100644 --- a/bin/report/render/rml2pdf/trml2pdf.py +++ b/bin/report/render/rml2pdf/trml2pdf.py @@ -475,10 +475,10 @@ class _rml_flowable(object): if key in ('rml_except', 'rml_loop', 'rml_tag'): del txt_n.attrib[key] if True or not self._textual(n).isspace(): - txt_n.text = utils.xml2str(self._textual(n)) - txt_n.tail = '' + if not n.tag == 'bullet': + txt_n.text = utils.xml2str(self._textual(n)) + txt_n.tail = n.tail and utils._process_text(self, n.tail.replace('\n','')) or '' rc1 += etree.tostring(txt_n) - #rc1 += utils._process_text(self, node.tail or '') return rc1 def _table(self, node): diff --git a/bin/report/render/rml2pdf/utils.py b/bin/report/render/rml2pdf/utils.py index 07bbbbcd05e..abc421a0f2f 100644 --- a/bin/report/render/rml2pdf/utils.py +++ b/bin/report/render/rml2pdf/utils.py @@ -43,6 +43,8 @@ from lxml import etree import copy import tools import locale +import netsvc +import traceback, sys _regex = re.compile('\[\[(.+?)\]\]') @@ -108,9 +110,13 @@ def _process_text(self, txt): result += self.localcontext.get('translate', lambda x:x)(sps.pop(0)) if sps: try: - txt = eval(sps.pop(0),self.localcontext) - except: - pass + expr = sps.pop(0) + txt = eval(expr,self.localcontext) + except Exception,e: + tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR, + 'report :\n%s\n%s\nexpr: %s' % (tb_s, str(e), + expr.encode('utf-8'))) if type(txt)==type('') or type(txt)==type(u''): txt2 = str2xml(txt) result += tools.ustr(txt2) @@ -173,7 +179,7 @@ def attr_get(node, attrs, dict={}): res[key] = int(node.get(key)) elif dict[key]=='unit': res[key] = unit_get(node.get(key)) - elif dict[key] == 'float' : + elif dict[key] == 'float' : res[key] = float(node.get(key)) return res