@@ -286,7 +286,8 @@ class act_window_view(osv.osv):
|
||||
('form', 'Form'),
|
||||
('graph', 'Graph'),
|
||||
('calendar', 'Calendar'),
|
||||
('gantt', 'Gantt')), string='View Type', required=True),
|
||||
('gantt', 'Gantt'),
|
||||
('gallery', 'Gallery')), string='View Type', required=True),
|
||||
'act_window_id': fields.many2one('ir.actions.act_window', 'Action', ondelete='cascade'),
|
||||
'multi': fields.boolean('On Multiple Doc.',
|
||||
help="If set to true, the action will not be displayed on the right toolbar of a form view."),
|
||||
|
||||
@@ -64,6 +64,7 @@ class view(osv.osv):
|
||||
('calendar', 'Calendar'),
|
||||
('diagram','Diagram'),
|
||||
('gantt', 'Gantt'),
|
||||
('gallery', 'Gallery'),
|
||||
('search','Search')), 'View Type', required=True),
|
||||
'arch': fields.text('View Architecture', required=True),
|
||||
'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade'),
|
||||
|
||||
@@ -203,7 +203,7 @@ class res_company(osv.osv):
|
||||
return """
|
||||
<header>
|
||||
<pageTemplate>
|
||||
<frame id="first" x1="25" y1="25" width="1070" height="717"/>
|
||||
<frame id="first" x1="22.0" y1="22.0" width="1080" height="700"/>
|
||||
<pageGraphics>
|
||||
<fill color="black"/>
|
||||
<stroke color="black"/>
|
||||
|
||||
@@ -412,8 +412,14 @@ class users(osv.osv):
|
||||
return self.name_get(cr, user, ids)
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context={}):
|
||||
login = self.read(cr, uid, [id], ['login'])[0]['login']
|
||||
default.update({'login': login+' (copy)'})
|
||||
user2copy = self.read(cr, uid, [id], ['login','name'])[0]
|
||||
if default is None:
|
||||
default = {}
|
||||
copy_pattern = _("%s (copy)")
|
||||
default.update(login=(copy_pattern % user2copy['login']),
|
||||
name=(copy_pattern % user2copy['name']),
|
||||
address_id=False, # avoid sharing the address of the copied user!
|
||||
)
|
||||
return super(users, self).copy(cr, uid, id, default, context)
|
||||
|
||||
def context_get(self, cr, uid, context=None):
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:ref name="any" />
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:ref name="any" />
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:ref name="any" />
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:ref name="any" />
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:ref name="any" />
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:ref name="any" />
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -397,6 +397,7 @@
|
||||
<rng:ref name="button"/>
|
||||
<rng:ref name="group"/>
|
||||
<rng:ref name="graph"/>
|
||||
<rng:ref name="gallery"/>
|
||||
<rng:ref name="html"/>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
</rng:choice>
|
||||
@@ -440,6 +441,7 @@
|
||||
<rng:optional><rng:attribute name="required"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="readonly"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="view_mode"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="search_mode"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="widget"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="context"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="states"/></rng:optional>
|
||||
@@ -470,6 +472,7 @@
|
||||
<rng:ref name="button"/>
|
||||
<rng:ref name="group"/>
|
||||
<rng:ref name="graph"/>
|
||||
<rng:ref name="gallery"/>
|
||||
<rng:ref name="filter"/>
|
||||
<rng:ref name="notebook"/>
|
||||
<rng:ref name="html"/>
|
||||
@@ -532,6 +535,18 @@
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="gallery">
|
||||
<rng:element name="gallery">
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="width"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="height"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="col"/></rng:optional>
|
||||
<rng:oneOrMore>
|
||||
<rng:ref name="field"/>
|
||||
</rng:oneOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="button">
|
||||
<rng:element name="button">
|
||||
<rng:ref name="overload"/>
|
||||
@@ -561,6 +576,7 @@
|
||||
<rng:ref name="graph" />
|
||||
<rng:ref name="calendar" />
|
||||
<rng:ref name="gantt" />
|
||||
<rng:ref name="gallery" />
|
||||
<rng:ref name="xpath" />
|
||||
<rng:ref name="page" />
|
||||
<rng:ref name="separator"/>
|
||||
@@ -629,6 +645,7 @@
|
||||
<rng:ref name="calendar" />
|
||||
<rng:ref name="data" />
|
||||
<rng:ref name="gantt" />
|
||||
<rng:ref name="gallery" />
|
||||
<rng:ref name="xpath" />
|
||||
<rng:ref name="page" />
|
||||
<rng:ref name="separator"/>
|
||||
|
||||
+90
-30
@@ -2215,7 +2215,7 @@ class orm(orm_template):
|
||||
# Take care of adding join(s) if groupby is an '_inherits'ed field
|
||||
groupby_list = groupby
|
||||
if groupby:
|
||||
if groupby and isinstance(groupby, list):
|
||||
if isinstance(groupby, list):
|
||||
groupby = groupby[0]
|
||||
tables, where_clause, qfield = self._inherits_join_calc(groupby, tables, where_clause)
|
||||
|
||||
@@ -2234,7 +2234,7 @@ class orm(orm_template):
|
||||
flist = ''
|
||||
group_by = groupby
|
||||
if groupby:
|
||||
if fget.get(groupby, False):
|
||||
if fget.get(groupby):
|
||||
if fget[groupby]['type'] in ('date', 'datetime'):
|
||||
flist = "to_char(%s,'yyyy-mm') as %s " % (groupby, groupby)
|
||||
groupby = "to_char(%s,'yyyy-mm')" % (groupby)
|
||||
@@ -2256,10 +2256,7 @@ class orm(orm_template):
|
||||
flist += ','
|
||||
flist += operator+'('+f+') as '+f
|
||||
|
||||
if groupby:
|
||||
gb = ' group by '+groupby
|
||||
else:
|
||||
gb = ''
|
||||
gb = groupby and (' group by '+groupby) or ''
|
||||
cr.execute('select min(%s.id) as id,' % self._table + flist + ' from ' + ','.join(tables) + where_clause + gb + limit_str + offset_str, where_clause_params)
|
||||
alldata = {}
|
||||
groupby = group_by
|
||||
@@ -2268,7 +2265,15 @@ class orm(orm_template):
|
||||
if val == None: r[fld] = False
|
||||
alldata[r['id']] = r
|
||||
del r['id']
|
||||
data = self.read(cr, uid, alldata.keys(), groupby and [groupby] or ['id'], context=context)
|
||||
if groupby and fget[groupby]['type'] == 'many2one':
|
||||
data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=groupby, context=context)
|
||||
data_read = self.read(cr, uid, data_ids, groupby and [groupby] or ['id'], context=context)
|
||||
# restore order of the search as read() uses the default _order:
|
||||
data = [data_read[id] for id in data_ids]
|
||||
else:
|
||||
data = self.read(cr, uid, alldata.keys(), groupby and [groupby] or ['id'], context=context)
|
||||
if groupby:
|
||||
data.sort(lambda x,y:cmp(x[groupby],y[groupby]))
|
||||
for d in data:
|
||||
if groupby:
|
||||
d['__domain'] = [(groupby, '=', alldata[d['id']][groupby] or False)] + domain
|
||||
@@ -2314,7 +2319,7 @@ class orm(orm_template):
|
||||
:param tables: list of table._table names enclosed in double quotes as returned
|
||||
by _where_calc()
|
||||
:param where_clause: current list of WHERE clause params
|
||||
:return: (table, where_clause, qualified_field) where ``table`` and ``where_clause`` are the updated
|
||||
:return: (tables, where_clause, qualified_field) where ``tables`` and ``where_clause`` are the updated
|
||||
versions of the parameters, and ``qualified_field`` is the qualified name of ``field``
|
||||
in the form ``table.field``, to be referenced in queries.
|
||||
"""
|
||||
@@ -3926,7 +3931,7 @@ class orm(orm_template):
|
||||
|
||||
def _check_qorder(self, word):
|
||||
if not regex_order.match(word):
|
||||
raise except_orm(_('AccessError'), _('Bad query.'))
|
||||
raise except_orm(_('AccessError'), _('Invalid "order" specified. A valid "order" specification is a comma-separated list of valid field names (optionally followed by asc/desc for the direction)'))
|
||||
return True
|
||||
|
||||
def _apply_ir_rules(self, cr, uid, where_clause, where_clause_params, tables, mode='read', model_name=None, context=None):
|
||||
@@ -3952,6 +3957,79 @@ class orm(orm_template):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _generate_m2o_order_by(self, order_field, tables, where_clause):
|
||||
"""
|
||||
Add possibly missing JOIN and generate the ORDER BY clause for m2o fields,
|
||||
either native m2o fields or function/related fields that are stored, including
|
||||
intermediate JOINs for inheritance if required.
|
||||
"""
|
||||
if order_field not in self._columns and order_field in self._inherit_fields:
|
||||
# also add missing joins for reaching the table containing the m2o field
|
||||
tables, where_clause, qualified_field = self._inherits_join_calc(order_field, tables, where_clause)
|
||||
order_field_column = self._inherit_fields[order_field][2]
|
||||
else:
|
||||
qualified_field = '"%s"."%s"' % (self._table, order_field)
|
||||
order_field_column = self._columns[order_field]
|
||||
|
||||
assert order_field_column._type == 'many2one', 'Invalid field passed to _generate_m2o_order_by()'
|
||||
assert order_field_column._classic_write or getattr(order_field_column, 'store', False), "Many2one function/related fields must be stored to be used as ordering fields"
|
||||
|
||||
# figure out the applicable order_by for the m2o
|
||||
dest_model = self.pool.get(order_field_column._obj)
|
||||
m2o_order = dest_model._order
|
||||
if not regex_order.match(m2o_order):
|
||||
# _order is complex, can't use it here, so we default to _rec_name
|
||||
m2o_order = dest_model._rec_name
|
||||
else:
|
||||
# extract the first field name, to be able to qualify it and add desc/asc
|
||||
m2o_order = m2o_order.split(",",1)[0].strip().split(" ",1)[0]
|
||||
|
||||
# the perhaps missing join:
|
||||
quoted_model_table = '"%s"' % dest_model._table
|
||||
if quoted_model_table not in tables:
|
||||
tables.append(quoted_model_table)
|
||||
where_clause.append('%s = %s.id' % (qualified_field, quoted_model_table))
|
||||
|
||||
return ('%s.%s' % (quoted_model_table, m2o_order), tables, where_clause)
|
||||
|
||||
|
||||
def _generate_order_by(self, order_spec, tables, where_clause):
|
||||
"""
|
||||
Attempt to consruct an appropriate ORDER BY clause based on order_spec, which must be
|
||||
a comma-separated list of valid field names, optionally followed by an ASC or DESC direction.
|
||||
|
||||
:raise" except_orm in case order_spec is malformed
|
||||
"""
|
||||
order_by_clause = self._order
|
||||
if order_spec:
|
||||
order_by_elements = []
|
||||
self._check_qorder(order_spec)
|
||||
for order_part in order_spec.split(','):
|
||||
order_split = order_part.strip().split(' ')
|
||||
order_field = order_split[0].strip()
|
||||
order_direction = order_split[1].strip() if len(order_split) == 2 else ''
|
||||
if order_field in self._columns:
|
||||
order_column = self._columns[order_field]
|
||||
if order_column._classic_read:
|
||||
order_by_clause = '"%s"."%s"' % (self._table, order_field)
|
||||
elif order_column._type == 'many2one':
|
||||
order_by_clause, tables, where_clause = self._generate_m2o_order_by(order_field, tables, where_clause)
|
||||
else:
|
||||
continue # ignore non-readable or "non-joignable" fields
|
||||
elif order_field in self._inherit_fields:
|
||||
parent_obj = self.pool.get(self._inherit_fields[order_field][0])
|
||||
order_column = parent_obj._columns[order_field]
|
||||
if order_column._classic_read:
|
||||
tables, where_clause, order_by_clause = self._inherits_join_calc(order_field, tables, where_clause)
|
||||
elif order_column._type == 'many2one':
|
||||
order_by_clause, tables, where_clause = self._generate_m2o_order_by(order_field, tables, where_clause)
|
||||
else:
|
||||
continue # ignore non-readable or "non-joignable" fields
|
||||
order_by_elements.append("%s %s" % (order_by_clause, order_direction))
|
||||
order_by_clause = ",".join(order_by_elements)
|
||||
|
||||
return order_by_clause and (' ORDER BY %s ' % order_by_clause) or ''
|
||||
|
||||
def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
|
||||
"""
|
||||
Private implementation of search() method, allowing specifying the uid to use for the access right check.
|
||||
@@ -3982,35 +4060,17 @@ class orm(orm_template):
|
||||
tables = list(set(tables).union(set(previous_tables)))
|
||||
|
||||
where = where_clause
|
||||
|
||||
order_by = self._order
|
||||
if order:
|
||||
self._check_qorder(order)
|
||||
o = order.split(' ')[0]
|
||||
if (o in self._columns):
|
||||
# we can only do efficient sort if the fields is stored in database
|
||||
if getattr(self._columns[o], '_classic_read'):
|
||||
order_by = order
|
||||
elif (o in self._inherit_fields):
|
||||
parent_obj = self.pool.get(self._inherit_fields[o][0])
|
||||
if getattr(parent_obj._columns[o], '_classic_read'):
|
||||
# Allowing inherits'ed field for server side sorting, if they can be sorted by the dbms
|
||||
inherited_tables, inherit_join, order_by = self._inherits_join_calc(o, tables, where_clause)
|
||||
|
||||
order_by = self._generate_order_by(order, tables, where_clause)
|
||||
limit_str = limit and ' limit %d' % limit or ''
|
||||
offset_str = offset and ' offset %d' % offset or ''
|
||||
|
||||
if where:
|
||||
where_str = " WHERE %s" % " AND ".join(where)
|
||||
else:
|
||||
where_str = ""
|
||||
where_str = where and (" WHERE %s" % " AND ".join(where)) or ''
|
||||
|
||||
if count:
|
||||
cr.execute('select count(%s.id) from ' % self._table +
|
||||
','.join(tables) + where_str + limit_str + offset_str, where_clause_params)
|
||||
res = cr.fetchall()
|
||||
return res[0][0]
|
||||
cr.execute('select %s.id from ' % self._table + ','.join(tables) + where_str +' order by '+order_by+limit_str+offset_str, where_clause_params)
|
||||
cr.execute('select %s.id from ' % self._table + ','.join(tables) + where_str + order_by + limit_str+offset_str, where_clause_params)
|
||||
res = cr.fetchall()
|
||||
return [x[0] for x in res]
|
||||
|
||||
|
||||
+10
-3
@@ -65,12 +65,19 @@ class osv_pool(netsvc.Service):
|
||||
for key in self._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
self.abortResponse(1, _('Constraint Error'), 'warning', _(self._sql_error[key]))
|
||||
if inst.pgcode == errorcodes.NOT_NULL_VIOLATION:
|
||||
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
|
||||
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
|
||||
self.logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
context = inst.pgerror.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
errortxt = inst.pgerror.replace('«','"').replace('»','"')
|
||||
if '"public".' in errortxt:
|
||||
context = errortxt.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
else:
|
||||
last_quote_end = errortxt.rfind('"')
|
||||
last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
|
||||
model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
|
||||
print "MODEL", last_quote_begin, last_quote_end, model_name
|
||||
model = table.replace("_",".")
|
||||
model_obj = self.get(model)
|
||||
if model_obj:
|
||||
|
||||
@@ -64,7 +64,7 @@ class _rml_styles(object,):
|
||||
for style in node.findall('paraStyle'):
|
||||
sname = style.get('name')
|
||||
self.styles[sname] = self._para_style_update(style)
|
||||
|
||||
|
||||
self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname])
|
||||
|
||||
for variable in node.findall('initialize'):
|
||||
@@ -809,6 +809,8 @@ class _rml_template(object):
|
||||
if story_cnt > 0:
|
||||
fis.append(platypus.PageBreak())
|
||||
fis += r.render(node_story)
|
||||
# Reset Page Number with new story tag
|
||||
fis.append(PageReset())
|
||||
story_cnt += 1
|
||||
if self.localcontext:
|
||||
fis.append(PageCount())
|
||||
|
||||
@@ -68,6 +68,24 @@ def _obj(pool, cr, uid, model_str, context=None):
|
||||
model = pool.get(model_str)
|
||||
return lambda x: model.browse(cr, uid, x, context=context)
|
||||
|
||||
def _fix_multiple_roots(node):
|
||||
"""
|
||||
Surround the children of the ``node`` element of an XML field with a
|
||||
single root "data" element, to prevent having a document with multiple
|
||||
roots once parsed separately.
|
||||
|
||||
XML nodes should have one root only, but we'd like to support
|
||||
direct multiple roots in our partial documents (like inherited view architectures).
|
||||
As a convention we'll surround multiple root with a container "data" element, to be
|
||||
ignored later when parsing.
|
||||
"""
|
||||
|
||||
if len(node) > 1:
|
||||
data_node = etree.Element("data")
|
||||
for child in node:
|
||||
data_node.append(child)
|
||||
node.append(data_node)
|
||||
|
||||
def _eval_xml(self, node, pool, cr, uid, idref, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
@@ -115,6 +133,7 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
|
||||
if not id in idref:
|
||||
idref[id]=self.id_get(cr, False, id)
|
||||
return s % idref
|
||||
_fix_multiple_roots(node)
|
||||
return '<?xml version="1.0"?>\n'\
|
||||
+_process("".join([etree.tostring(n, encoding='utf-8')
|
||||
for n in node]),
|
||||
|
||||
@@ -70,14 +70,16 @@ def is_url(node):
|
||||
|
||||
def is_eval(node):
|
||||
return isinstance(node, yaml_tag.Eval)
|
||||
|
||||
|
||||
def is_ref(node):
|
||||
return isinstance(node, yaml_tag.Ref) \
|
||||
or _is_yaml_mapping(node, yaml_tag.Ref)
|
||||
|
||||
|
||||
def is_ir_set(node):
|
||||
return _is_yaml_mapping(node, yaml_tag.IrSet)
|
||||
|
||||
def is_string(node):
|
||||
return isinstance(node, basestring)
|
||||
|
||||
class TestReport(object):
|
||||
def __init__(self):
|
||||
@@ -374,6 +376,14 @@ class YamlInterpreter(object):
|
||||
elif column._type == "many2many":
|
||||
ids = [self.get_id(xml_id) for xml_id in expression]
|
||||
value = [(6, 0, ids)]
|
||||
elif column._type == "date" and is_string(expression):
|
||||
# enforce ISO format for string date values, to be locale-agnostic during tests
|
||||
time.strptime(expression, misc.DEFAULT_SERVER_DATE_FORMAT)
|
||||
value = expression
|
||||
elif column._type == "datetime" and is_string(expression):
|
||||
# enforce ISO format for string datetime values, to be locale-agnostic during tests
|
||||
time.strptime(expression, misc.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
value = expression
|
||||
else: # scalar field
|
||||
if is_eval(expression):
|
||||
value = self.process_eval(expression)
|
||||
|
||||
Reference in New Issue
Block a user