[MERGE] complete migration of module base to the new API

This commit is contained in:
Raphael Collet
2016-05-11 12:06:08 +02:00
53 changed files with 3355 additions and 4018 deletions
+5 -6
View File
@@ -12,18 +12,17 @@ class TestServerActionsEmail(TestServerActionsBase):
'name': 'TestTemplate',
'email_from': 'myself@example.com',
'email_to': 'brigitte@example.com',
'partner_to': '%s' % self.test_partner_id,
'model_id': self.res_partner_model_id,
'partner_to': '%s' % self.test_partner.id,
'model_id': self.res_partner_model.id,
'subject': 'About ${object.name}',
'body_html': '<p>Dear ${object.name}, your parent is ${object.parent_id and object.parent_id.name or "False"}</p>',
})
action = self.env['ir.actions.server'].browse(self.act_id)
action.write({'state': 'email', 'template_id': email_template.id})
run_res = action.with_context(self.context).run()
self.action.write({'state': 'email', 'template_id': email_template.id})
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: email server action correctly finished should return False')
# check an email is waiting for sending
mail = self.env['mail.mail'].search([('subject', '=', 'About TestingPartner')])
self.assertEqual(len(mail.ids), 1, 'ir_actions_server: TODO')
self.assertEqual(len(mail), 1, 'ir_actions_server: TODO')
# check email content
self.assertEqual(mail.body, '<p>Dear TestingPartner, your parent is False</p>',
'ir_actions_server: TODO')
+8 -6
View File
@@ -10,10 +10,12 @@ import tests
def post_init(cr, registry):
"""Rewrite ICP's to force groups"""
from openerp import SUPERUSER_ID
from openerp import api, SUPERUSER_ID
from openerp.addons.base.ir.ir_config_parameter import _default_parameters
ICP = registry['ir.config_parameter']
for k, func in _default_parameters.items():
v = ICP.get_param(cr, SUPERUSER_ID, k)
_, g = func()
ICP.set_param(cr, SUPERUSER_ID, k, v, g)
env = api.Environment(cr, SUPERUSER_ID, {})
ICP = env['ir.config_parameter']
for key, func in _default_parameters.iteritems():
val = ICP.get_param(key)
_, groups = func()
ICP.set_param(key, val, groups)
+1 -5
View File
@@ -72,11 +72,7 @@ The kernel of Odoo, needed for all installation.
'res/res_partner_demo.yml',
'res/res_partner_image_demo.xml',
],
'test': [
'tests/base_test.yml',
'tests/test_osv_expression.yml',
'tests/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
],
'test': [],
'installable': True,
'auto_install': True,
'post_init_hook': 'post_init',
+326 -380
View File
@@ -1,40 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from operator import attrgetter
import importlib
import logging
import os
import shutil
import tempfile
import urllib2
import urlparse
import zipfile
from docutils import nodes
from docutils.core import publish_string
from docutils.transforms import Transform, writer_aux
from docutils.writers.html4css1 import Writer
import importlib
import logging
from operator import attrgetter
import os
import re
import shutil
import tempfile
import urllib
import urllib2
import urlparse
import zipfile
import zipimport
import lxml.html
from openerp.exceptions import UserError
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO # NOQA
import openerp
import openerp.exceptions
from openerp import modules, tools
from openerp.modules.db import create_categories
from openerp.modules import get_module_resource
from openerp.tools import ormcache
from openerp.tools.parse_version import parse_version
from openerp.tools.translate import _
from openerp.tools import html_sanitize
from openerp.osv import osv, orm, fields
from openerp import api, fields as fields2
import odoo
from odoo import api, fields, models, modules, tools, _
from odoo.exceptions import AccessDenied, UserError
from odoo.tools.parse_version import parse_version
_logger = logging.getLogger(__name__)
@@ -61,42 +52,45 @@ def backup(path, raise_exception=True):
cnt += 1
class module_category(osv.osv):
class ModuleCategory(models.Model):
_name = "ir.module.category"
_description = "Application"
_order = 'name'
def _module_nbr(self, cr, uid, ids, prop, unknow_none, context):
@api.depends('module_ids')
def _compute_module_nr(self):
cr = self._cr
cr.execute('SELECT category_id, COUNT(*) \
FROM ir_module_module \
WHERE category_id IN %(ids)s \
OR category_id IN (SELECT id \
FROM ir_module_category \
WHERE parent_id IN %(ids)s) \
GROUP BY category_id', {'ids': tuple(ids)}
GROUP BY category_id', {'ids': tuple(self.ids)}
)
result = dict(cr.fetchall())
for id in ids:
cr.execute('select id from ir_module_category where parent_id=%s', (id,))
result[id] = sum([result.get(c, 0) for (c,) in cr.fetchall()],
result.get(id, 0))
return result
for cat in self.filtered('id'):
cr.execute('SELECT id FROM ir_module_category WHERE parent_id=%s', (cat.id,))
cat.module_nr = sum([result.get(c, 0) for (c,) in cr.fetchall()], result.get(cat.id, 0))
_columns = {
'name': fields.char("Name", required=True, translate=True, select=True),
'parent_id': fields.many2one('ir.module.category', 'Parent Application', select=True),
'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Child Applications'),
'module_nr': fields.function(_module_nbr, string='Number of Apps', type='integer'),
'module_ids': fields.one2many('ir.module.module', 'category_id', 'Modules'),
'description': fields.text("Description", translate=True),
'sequence': fields.integer('Sequence'),
'visible': fields.boolean('Visible'),
'xml_id': fields.function(osv.osv.get_external_id, type='char', string="External ID"),
}
_order = 'name'
name = fields.Char(string='Name', required=True, translate=True, index=True)
parent_id = fields.Many2one('ir.module.category', string='Parent Application', index=True)
child_ids = fields.One2many('ir.module.category', 'parent_id', string='Child Applications')
module_nr = fields.Integer(string='Number of Apps', compute='_compute_module_nr')
module_ids = fields.One2many('ir.module.module', 'category_id', string='Modules')
description = fields.Text(string='Description', translate=True)
sequence = fields.Integer(string='Sequence')
visible = fields.Boolean(string='Visible', default=True)
xml_id = fields.Char(string='External ID', compute='_compute_xml_id')
def _compute_xml_id(self):
xml_ids = defaultdict(list)
domain = [('model', '=', self._name), ('res_id', 'in', self.ids)]
for data in self.env['ir.model.data'].search_read(domain, ['module', 'name', 'res_id']):
xml_ids[data['res_id']].append("%s.%s" % (data['module'], data['name']))
for cat in self:
cat.xml_id = xml_ids.get(cat.id, [''])[0]
_defaults = {
'visible': 1,
}
class MyFilterMessages(Transform):
"""
@@ -107,7 +101,6 @@ class MyFilterMessages(Transform):
the `settings_override` dictionary, but if we use it, we can't see them
and generate warnings.)
"""
default_priority = 870
def apply(self):
@@ -115,43 +108,52 @@ class MyFilterMessages(Transform):
_logger.warning("docutils' system message present: %s", str(node))
node.parent.remove(node)
class MyWriter(Writer):
"""
Custom docutils html4ccs1 writer that doesn't add the warnings to the
output document.
"""
def get_transforms(self):
return [MyFilterMessages, writer_aux.Admonitions]
class module(osv.osv):
STATES = [
('uninstallable', 'Uninstallable'),
('uninstalled', 'Not Installed'),
('installed', 'Installed'),
('to upgrade', 'To be upgraded'),
('to remove', 'To be removed'),
('to install', 'To be installed'),
]
class Module(models.Model):
_name = "ir.module.module"
_rec_name = "shortdesc"
_description = "Module"
_order = 'sequence,name'
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
res = super(module, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
result = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'action_server_module_immediate_install')[1]
if view_type == 'form':
if res.get('toolbar',False):
list = [rec for rec in res['toolbar']['action'] if rec.get('id', False) != result]
res['toolbar'] = {'action': list}
return res
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
res = super(Module, self).fields_view_get(view_id, view_type, toolbar=toolbar, submenu=False)
if view_type == 'form' and res.get('toolbar',False):
install_id = self.env.ref('base.action_server_module_immediate_install').id
action = [rec for rec in res['toolbar']['action'] if rec.get('id', False) != install_id]
res['toolbar'] = {'action': action}
return res
@classmethod
def get_module_info(cls, name):
info = {}
try:
info = modules.load_information_from_description_file(name)
return modules.load_information_from_description_file(name)
except Exception:
_logger.debug('Error when trying to fetch information for '
'module %s', name, exc_info=True)
return info
_logger.debug('Error when trying to fetch information for module %s', name, exc_info=True)
return {}
def _get_desc(self, cr, uid, ids, field_name=None, arg=None, context=None):
res = dict.fromkeys(ids, '')
for module in self.browse(cr, uid, ids, context=context):
path = get_module_resource(module.name, 'static/description/index.html')
@api.depends('name', 'description')
def _get_desc(self):
for module in self:
path = modules.get_module_resource(module.name, 'static/description/index.html')
if path:
with tools.file_open(path, 'rb') as desc_file:
doc = desc_file.read()
@@ -159,7 +161,7 @@ class module(osv.osv):
for element, attribute, link, pos in html.iterlinks():
if element.get('src') and not '//' in element.get('src') and not 'static/' in element.get('src'):
element.set('src', "/%s/static/description/%s" % (module.name, element.get('src')))
res[module.id] = html_sanitize(lxml.html.tostring(html))
module.description_html = tools.html_sanitize(lxml.html.tostring(html))
else:
overrides = {
'embed_stylesheet': False,
@@ -168,170 +170,115 @@ class module(osv.osv):
'xml_declaration': False,
}
output = publish_string(source=module.description or '', settings_overrides=overrides, writer=MyWriter())
res[module.id] = html_sanitize(output)
return res
module.description_html = tools.html_sanitize(output)
def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context=None):
@api.depends('name')
def _get_latest_version(self):
default_version = modules.adapt_version('1.0')
res = dict.fromkeys(ids, default_version)
for m in self.browse(cr, uid, ids):
res[m.id] = self.get_module_info(m.name).get('version', default_version)
return res
for module in self:
module.installed_version = self.get_module_info(module.name).get('version', default_version)
def _get_views(self, cr, uid, ids, field_name=None, arg=None, context=None):
res = {}
model_data_obj = self.pool.get('ir.model.data')
dmodels = []
if field_name is None or 'views_by_module' in field_name:
dmodels.append('ir.ui.view')
if field_name is None or 'reports_by_module' in field_name:
dmodels.append('ir.actions.report.xml')
if field_name is None or 'menus_by_module' in field_name:
dmodels.append('ir.ui.menu')
assert dmodels, "no models for %s" % field_name
for module_rec in self.browse(cr, uid, ids, context=context):
res_mod_dic = res[module_rec.id] = {
'menus_by_module': [],
'reports_by_module': [],
'views_by_module': []
}
@api.depends('name', 'state')
def _get_views(self):
IrModelData = self.env['ir.model.data'].with_context(active_test=True)
dmodels = ['ir.ui.view', 'ir.actions.report.xml', 'ir.ui.menu']
for module in self:
# Skip uninstalled modules below, no data to find anyway.
if module_rec.state not in ('installed', 'to upgrade', 'to remove'):
if module.state not in ('installed', 'to upgrade', 'to remove'):
module.views_by_module = ""
module.reports_by_module = ""
module.menus_by_module = ""
continue
# then, search and group ir.model.data records
imd_models = dict([(m, []) for m in dmodels])
imd_ids = model_data_obj.search(cr, uid, [
('module', '=', module_rec.name),
('model', 'in', tuple(dmodels))
])
for imd_res in model_data_obj.read(cr, uid, imd_ids, ['model', 'res_id'], context=context):
imd_models[imd_res['model']].append(imd_res['res_id'])
imd_models = defaultdict(list)
imd_domain = [('module', '=', module.name), ('model', 'in', tuple(dmodels))]
for data in IrModelData.search(imd_domain):
imd_models[data.model].append(data.res_id)
def browse(model):
M = self.pool[model]
# as this method is called before the module update, some xmlid may be invalid at this stage
# explictly filter records before reading them
ids = M.exists(cr, uid, imd_models.get(model, []), context)
return M.browse(cr, uid, ids, context)
# as this method is called before the module update, some xmlid
# may be invalid at this stage; explictly filter records before
# reading them
return self.env[model].browse(imd_models[model]).exists()
def format_view(v):
aa = v.inherit_id and '* INHERIT ' or ''
return '%s%s (%s)' % (aa, v.name, v.type)
return '%s%s (%s)' % (v.inherit_id and '* INHERIT ' or '', v.name, v.type)
res_mod_dic['views_by_module'] = map(format_view, browse('ir.ui.view'))
res_mod_dic['reports_by_module'] = map(attrgetter('name'), browse('ir.actions.report.xml'))
res_mod_dic['menus_by_module'] = map(attrgetter('complete_name'), browse('ir.ui.menu'))
module.views_by_module = "\n".join(sorted(map(format_view, browse('ir.ui.view'))))
module.reports_by_module = "\n".join(sorted(map(attrgetter('name'), browse('ir.actions.report.xml'))))
module.menus_by_module = "\n".join(sorted(map(attrgetter('complete_name'), browse('ir.ui.menu'))))
for key in res.iterkeys():
for k, v in res[key].iteritems():
res[key][k] = "\n".join(sorted(v))
return res
def _get_icon_image(self, cr, uid, ids, field_name=None, arg=None, context=None):
res = dict.fromkeys(ids, '')
for module in self.browse(cr, uid, ids, context=context):
path = get_module_resource(module.name, 'static', 'description', 'icon.png')
@api.depends('name')
def _get_icon_image(self):
for module in self:
module.icon_image = ''
path = modules.get_module_resource(module.name, 'static', 'description', 'icon.png')
if path:
image_file = tools.file_open(path, 'rb')
try:
res[module.id] = image_file.read().encode('base64')
finally:
image_file.close()
return res
with tools.file_open(path, 'rb') as image_file:
module.icon_image = image_file.read().encode('base64')
_columns = {
'name': fields.char("Technical Name", readonly=True, required=True, select=True),
'category_id': fields.many2one('ir.module.category', 'Category', readonly=True, select=True),
'shortdesc': fields.char('Module Name', readonly=True, translate=True),
'summary': fields.char('Summary', readonly=True, translate=True),
'description': fields.text("Description", readonly=True, translate=True),
'description_html': fields.function(_get_desc, string='Description HTML', type='html', method=True, readonly=True),
'author': fields.char("Author", readonly=True),
'maintainer': fields.char('Maintainer', readonly=True),
'contributors': fields.text('Contributors', readonly=True),
'website': fields.char("Website", readonly=True),
name = fields.Char('Technical Name', readonly=True, required=True, index=True)
category_id = fields.Many2one('ir.module.category', string='Category', readonly=True, index=True)
shortdesc = fields.Char('Module Name', readonly=True, translate=True)
summary = fields.Char('Summary', readonly=True, translate=True)
description = fields.Text('Description', readonly=True, translate=True)
description_html = fields.Html('Description HTML', compute='_get_desc')
author = fields.Char("Author", readonly=True)
maintainer = fields.Char('Maintainer', readonly=True)
contributors = fields.Text('Contributors', readonly=True)
website = fields.Char("Website", readonly=True)
# attention: Incorrect field names !!
# installed_version refers the latest version (the one on disk)
# latest_version refers the installed version (the one in database)
# published_version refers the version available on the repository
'installed_version': fields.function(_get_latest_version, string='Latest Version', type='char'),
'latest_version': fields.char('Installed Version', readonly=True),
'published_version': fields.char('Published Version', readonly=True),
# attention: Incorrect field names !!
# installed_version refers the latest version (the one on disk)
# latest_version refers the installed version (the one in database)
# published_version refers the version available on the repository
installed_version = fields.Char('Latest Version', compute='_get_latest_version')
latest_version = fields.Char('Installed Version', readonly=True)
published_version = fields.Char('Published Version', readonly=True)
'url': fields.char('URL', readonly=True),
'sequence': fields.integer('Sequence'),
'dependencies_id': fields.one2many('ir.module.module.dependency', 'module_id', 'Dependencies', readonly=True),
'auto_install': fields.boolean('Automatic Installation',
help='An auto-installable module is automatically installed by the '
'system when all its dependencies are satisfied. '
'If the module has no dependency, it is always installed.'),
'state': fields.selection([
('uninstallable', 'Not Installable'),
('uninstalled', 'Not Installed'),
('installed', 'Installed'),
('to upgrade', 'To be upgraded'),
('to remove', 'To be removed'),
('to install', 'To be installed')
], string='Status', readonly=True, select=True),
'demo': fields.boolean('Demo Data', readonly=True),
'license': fields.selection([
('GPL-2', 'GPL Version 2'),
('GPL-2 or any later version', 'GPL-2 or later version'),
('GPL-3', 'GPL Version 3'),
('GPL-3 or any later version', 'GPL-3 or later version'),
('AGPL-3', 'Affero GPL-3'),
('LGPL-3', 'LGPL Version 3'),
('Other OSI approved licence', 'Other OSI Approved Licence'),
('OEEL-1', 'Odoo Enterprise Edition License v1.0'),
('Other proprietary', 'Other Proprietary')
], string='License', readonly=True),
'menus_by_module': fields.function(_get_views, string='Menus', type='text', multi="meta", store=True),
'reports_by_module': fields.function(_get_views, string='Reports', type='text', multi="meta", store=True),
'views_by_module': fields.function(_get_views, string='Views', type='text', multi="meta", store=True),
'application': fields.boolean('Application', readonly=True),
'icon': fields.char('Icon URL'),
'icon_image': fields.function(_get_icon_image, string='Icon', type="binary"),
}
_defaults = {
'state': 'uninstalled',
'sequence': 100,
'demo': False,
'license': 'LGPL-3',
}
_order = 'sequence,name'
def _name_uniq_msg(self, cr, uid, ids, context=None):
return _('The name of the module must be unique !')
url = fields.Char('URL', readonly=True)
sequence = fields.Integer('Sequence', default=100)
dependencies_id = fields.One2many('ir.module.module.dependency', 'module_id',
string='Dependencies', readonly=True)
auto_install = fields.Boolean('Automatic Installation',
help='An auto-installable module is automatically installed by the '
'system when all its dependencies are satisfied. '
'If the module has no dependency, it is always installed.')
state = fields.Selection(STATES, string='Status', default='uninstalled', readonly=True, index=True)
demo = fields.Boolean('Demo Data', default=False, readonly=True)
license = fields.Selection([
('GPL-2', 'GPL Version 2'),
('GPL-2 or any later version', 'GPL-2 or later version'),
('GPL-3', 'GPL Version 3'),
('GPL-3 or any later version', 'GPL-3 or later version'),
('AGPL-3', 'Affero GPL-3'),
('LGPL-3', 'LGPL Version 3'),
('Other OSI approved licence', 'Other OSI Approved Licence'),
('OEEL-1', 'Odoo Enterprise Edition License v1.0'),
('Other proprietary', 'Other Proprietary')
], string='License', default='LGPL-3', readonly=True)
menus_by_module = fields.Text(string='Menus', compute='_get_views', store=True)
reports_by_module = fields.Text(string='Reports', compute='_get_views', store=True)
views_by_module = fields.Text(string='Views', compute='_get_views', store=True)
application = fields.Boolean('Application', readonly=True)
icon = fields.Char('Icon URL')
icon_image = fields.Binary(string='Icon', compute='_get_icon_image')
_sql_constraints = [
('name_uniq', 'UNIQUE (name)', _name_uniq_msg),
('name_uniq', 'UNIQUE (name)', 'The name of the module must be unique!'),
]
def unlink(self, cr, uid, ids, context=None):
if not ids:
@api.multi
def unlink(self):
if not self:
return True
if isinstance(ids, (int, long)):
ids = [ids]
mod_names = []
for mod in self.read(cr, uid, ids, ['state', 'name'], context):
if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'):
for module in self:
if module.state in ('installed', 'to upgrade', 'to remove', 'to install'):
raise UserError(_('You try to remove a module that is installed or will be installed'))
mod_names.append(mod['name'])
#Removing the entry from ir_model_data
#ids_meta = self.pool.get('ir.model.data').search(cr, uid, [('name', '=', 'module_meta_information'), ('module', 'in', mod_names)])
#if ids_meta:
# self.pool.get('ir.model.data').unlink(cr, uid, ids_meta, context)
self.clear_caches()
return super(module, self).unlink(cr, uid, ids, context=context)
return super(Module, self).unlink()
@staticmethod
def _check_external_dependencies(terp):
@@ -447,163 +394,166 @@ class module(osv.osv):
) % ("\n".join(themes_list)))
return dict(ACTION_DICT, name=_('Install'))
def button_immediate_install(self, cr, uid, ids, context=None):
@api.multi
def button_immediate_install(self):
""" Installs the selected module(s) immediately and fully,
returns the next res.config action to execute
:param ids: identifiers of the modules to install
:returns: next res.config item to execute
:rtype: dict[str, object]
"""
return self._button_immediate_function(cr, uid, ids, self.button_install, context=context)
return self._button_immediate_function(type(self).button_install)
def button_install_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo': False})
@api.multi
def button_install_cancel(self):
self.write({'state': 'uninstalled', 'demo': False})
return True
def module_uninstall(self, cr, uid, ids, context=None):
"""Perform the various steps required to uninstall a module completely
@api.multi
def module_uninstall(self):
""" Perform the various steps required to uninstall a module completely
including the deletion of all database structures created by the module:
tables, columns, constraints, etc."""
ir_model_data = self.pool.get('ir.model.data')
modules_to_remove = [m.name for m in self.browse(cr, uid, ids, context)]
ir_model_data._module_data_uninstall(cr, uid, modules_to_remove, context)
self.write(cr, uid, ids, {'state': 'uninstalled', 'latest_version': False})
tables, columns, constraints, etc.
"""
modules_to_remove = self.mapped('name')
self.env['ir.model.data']._module_data_uninstall(modules_to_remove)
self.write({'state': 'uninstalled', 'latest_version': False})
return True
def downstream_dependencies(self, cr, uid, ids, known_dep_ids=None,
exclude_states=['uninstalled', 'uninstallable', 'to remove'],
context=None):
"""Return the ids of all modules that directly or indirectly depend
on the given module `ids`, and that satisfy the `exclude_states`
filter"""
if not ids:
return []
known_dep_ids = set(known_dep_ids or [])
cr.execute('''SELECT DISTINCT m.id
FROM
ir_module_module_dependency d
JOIN
ir_module_module m ON (d.module_id=m.id)
WHERE
d.name IN (SELECT name from ir_module_module where id in %s) AND
m.state NOT IN %s AND
m.id NOT IN %s ''',
(tuple(ids), tuple(exclude_states), tuple(known_dep_ids or ids)))
new_dep_ids = set([m[0] for m in cr.fetchall()])
missing_mod_ids = new_dep_ids - known_dep_ids
known_dep_ids |= new_dep_ids
if missing_mod_ids:
known_dep_ids |= set(self.downstream_dependencies(cr, uid, list(missing_mod_ids),
known_dep_ids, exclude_states, context))
return list(known_dep_ids)
@api.multi
@api.returns('self')
def downstream_dependencies(self, known_deps=None,
exclude_states=('uninstalled', 'uninstallable', 'to remove')):
""" Return the modules that directly or indirectly depend on the modules
in `self`, and that satisfy the `exclude_states` filter.
"""
if not self:
return self
known_deps = known_deps or self.browse()
query = """ SELECT DISTINCT m.id
FROM ir_module_module_dependency d
JOIN ir_module_module m ON (d.module_id=m.id)
WHERE
d.name IN (SELECT name from ir_module_module where id in %s) AND
m.state NOT IN %s AND
m.id NOT IN %s """
self._cr.execute(query, (tuple(self.ids), tuple(exclude_states), tuple(known_deps.ids or self.ids)))
new_deps = self.browse([row[0] for row in self._cr.fetchall()])
missing_mods = new_deps - known_deps
known_deps |= new_deps
if missing_mods:
known_deps |= missing_mods.downstream_dependencies(known_deps, exclude_states)
return known_deps
def upstream_dependencies(self, cr, uid, ids, known_dep_ids=None,
exclude_states=['installed', 'uninstallable', 'to remove'],
context=None):
""" Return the dependency tree of modules of the given `ids`, and that
satisfy the `exclude_states` filter """
if not ids:
return []
known_dep_ids = set(known_dep_ids or [])
cr.execute('''SELECT DISTINCT m.id
FROM
ir_module_module_dependency d
JOIN
ir_module_module m ON (d.module_id=m.id)
WHERE
m.name IN (SELECT name from ir_module_module_dependency where module_id in %s) AND
m.state NOT IN %s AND
m.id NOT IN %s ''',
(tuple(ids), tuple(exclude_states), tuple(known_dep_ids or ids)))
new_dep_ids = set([m[0] for m in cr.fetchall()])
missing_mod_ids = new_dep_ids - known_dep_ids
known_dep_ids |= new_dep_ids
if missing_mod_ids:
known_dep_ids |= set(self.upstream_dependencies(cr, uid, list(missing_mod_ids),
known_dep_ids, exclude_states, context))
return list(known_dep_ids)
@api.multi
@api.returns('self')
def upstream_dependencies(self, known_deps=None,
exclude_states=('installed', 'uninstallable', 'to remove')):
""" Return the dependency tree of modules of the modules in `self`, and
that satisfy the `exclude_states` filter.
"""
if not self:
return self
known_deps = known_deps or self.browse()
query = """ SELECT DISTINCT m.id
FROM ir_module_module_dependency d
JOIN ir_module_module m ON (d.module_id=m.id)
WHERE
m.name IN (SELECT name from ir_module_module_dependency where module_id in %s) AND
m.state NOT IN %s AND
m.id NOT IN %s """
self._cr.execute(query, (tuple(self.ids), tuple(exclude_states), tuple(known_deps.ids or self.ids)))
new_deps = self.browse([row[0] for row in self._cr.fetchall()])
missing_mods = new_deps - known_deps
known_deps |= new_deps
if missing_mods:
known_deps |= missing_mods.upstream_dependencies(known_deps, exclude_states)
return known_deps
def _button_immediate_function(self, cr, uid, ids, function, context=None):
function(cr, uid, ids, context=context)
@api.multi
def _button_immediate_function(self, function):
function(self)
cr.commit()
self._cr.commit()
api.Environment.reset()
registry = openerp.modules.registry.RegistryManager.new(cr.dbname, update_module=True)
modules.registry.RegistryManager.new(self._cr.dbname, update_module=True)
cr.commit()
config = registry['res.config'].next(cr, uid, [], context=context) or {}
self._cr.commit()
env = api.Environment(self._cr, self._uid, self._context)
config = env['res.config'].next() or {}
if config.get('type') not in ('ir.actions.act_window_close',):
return config
# reload the client; open the first available root menu
menu_obj = registry['ir.ui.menu']
menu_ids = menu_obj.search(cr, uid, [('parent_id', '=', False)], context=context)
menu = env['ir.ui.menu'].search([('parent_id', '=', False)])[:1]
return {
'type': 'ir.actions.client',
'tag': 'reload',
'params': {'menu_id': menu_ids and menu_ids[0] or False}
'params': {'menu_id': menu.id},
}
def button_immediate_uninstall(self, cr, uid, ids, context=None):
@api.multi
def button_immediate_uninstall(self):
"""
Uninstall the selected module(s) immediately and fully,
returns the next res.config action to execute
"""
return self._button_immediate_function(cr, uid, ids, self.button_uninstall, context=context)
return self._button_immediate_function(type(self).button_uninstall)
def button_uninstall(self, cr, uid, ids, context=None):
if any(m.name == 'base' for m in self.browse(cr, uid, ids, context=context)):
@api.multi
def button_uninstall(self):
if 'base' in self.mapped('name'):
raise UserError(_("The `base` module cannot be uninstalled"))
dep_ids = self.downstream_dependencies(cr, uid, ids, context=context)
self.write(cr, uid, ids + dep_ids, {'state': 'to remove'})
deps = self.downstream_dependencies()
(self + deps).write({'state': 'to remove'})
return dict(ACTION_DICT, name=_('Uninstall'))
def button_uninstall_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'installed'})
@api.multi
def button_uninstall_cancel(self):
self.write({'state': 'installed'})
return True
def button_immediate_upgrade(self, cr, uid, ids, context=None):
@api.multi
def button_immediate_upgrade(self):
"""
Upgrade the selected module(s) immediately and fully,
return the next res.config action to execute
"""
return self._button_immediate_function(cr, uid, ids, self.button_upgrade, context=context)
return self._button_immediate_function(type(self).button_upgrade)
def button_upgrade(self, cr, uid, ids, context=None):
depobj = self.pool.get('ir.module.module.dependency')
todo = list(self.browse(cr, uid, ids, context=context))
self.update_list(cr, uid)
@api.multi
def button_upgrade(self):
Dependency = self.env['ir.module.module.dependency']
self.update_list()
todo = list(self)
i = 0
while i < len(todo):
mod = todo[i]
module = todo[i]
i += 1
if mod.state not in ('installed', 'to upgrade'):
raise UserError(_("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
self.check_external_dependencies(mod.name, 'to upgrade')
iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context)
for dep in depobj.browse(cr, uid, iids, context=context):
if module.state not in ('installed', 'to upgrade'):
raise UserError(_("Can not upgrade module '%s'. It is not installed.") % (module.name,))
self.check_external_dependencies(module.name, 'to upgrade')
for dep in Dependency.search([('name', '=', module.name)]):
if dep.module_id.state == 'installed' and dep.module_id not in todo:
todo.append(dep.module_id)
ids = map(lambda x: x.id, todo)
self.write(cr, uid, ids, {'state': 'to upgrade'}, context=context)
self.browse(module.id for module in todo).write({'state': 'to upgrade'})
to_install = []
for mod in todo:
for dep in mod.dependencies_id:
for module in todo:
for dep in module.dependencies_id:
if dep.state == 'unknown':
raise UserError(_('You try to upgrade a module that depends on the module: %s.\nBut this module is not available in your system.') % (dep.name,))
if dep.state == 'uninstalled':
ids2 = self.search(cr, uid, [('name', '=', dep.name)])
to_install.extend(ids2)
to_install += self.search([('name', '=', dep.name)]).ids
self.button_install(cr, uid, to_install, context=context)
self.browse(to_install).button_install()
return dict(ACTION_DICT, name=_('Apply Schedule Upgrade'))
def button_upgrade_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'installed'})
@api.multi
def button_upgrade_cancel(self):
self.write({'state': 'installed'})
return True
@staticmethod
@@ -624,25 +574,27 @@ class module(osv.osv):
'url': terp.get('url') or terp.get('live_test_url', ''),
}
def create(self, cr, uid, vals, context=None):
new_id = super(module, self).create(cr, uid, vals, context=context)
@api.model
def create(self, vals):
new = super(Module, self).create(vals)
module_metadata = {
'name': 'module_%s' % vals['name'],
'model': 'ir.module.module',
'module': 'base',
'res_id': new_id,
'res_id': new.id,
'noupdate': True,
}
self.pool['ir.model.data'].create(cr, uid, module_metadata)
return new_id
self.env['ir.model.data'].create(module_metadata)
return new
# update the list of available packages
def update_list(self, cr, uid, context=None):
@api.model
def update_list(self):
res = [0, 0] # [update, add]
default_version = modules.adapt_version('1.0')
known_mods = self.browse(cr, uid, self.search(cr, uid, []))
known_mods_names = dict([(m.name, m) for m in known_mods])
known_mods = self.search([])
known_mods_names = {mod.name: mod for mod in known_mods}
# iterate through detected modules and update/create them in db
for mod_name in modules.get_modules():
@@ -654,7 +606,7 @@ class module(osv.osv):
updated_values = {}
for key in values:
old = getattr(mod, key)
updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
updated = tools.ustr(values[key]) if isinstance(values[key], basestring) else values[key]
if (old or updated) and updated != old:
updated_values[key] = values[key]
if terp.get('installable', True) and mod.state == 'uninstallable':
@@ -662,43 +614,44 @@ class module(osv.osv):
if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version):
res[0] += 1
if updated_values:
self.write(cr, uid, mod.id, updated_values)
mod.write(updated_values)
else:
mod_path = modules.get_module_path(mod_name)
if not mod_path:
continue
if not terp or not terp.get('installable', True):
continue
id = self.create(cr, uid, dict(name=mod_name, state='uninstalled', **values))
mod = self.browse(cr, uid, id)
mod = self.create(dict(name=mod_name, state='uninstalled', **values))
res[1] += 1
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
mod._update_dependencies(terp.get('depends', []))
mod._update_category(terp.get('category', 'Uncategorized'))
return res
def download(self, cr, uid, ids, download=True, context=None):
@api.multi
def download(self, download=True):
return []
def install_from_urls(self, cr, uid, urls, context=None):
if not self.pool['res.users'].has_group(cr, uid, 'base.group_system'):
raise openerp.exceptions.AccessDenied()
@api.model
def install_from_urls(self, urls):
if not self.env.user.has_group('base.group_system'):
raise AccessDenied()
apps_server = urlparse.urlparse(self.get_apps_server(cr, uid, context=context))
apps_server = urlparse.urlparse(self.get_apps_server())
OPENERP = openerp.release.product_name.lower()
OPENERP = odoo.release.product_name.lower()
tmp = tempfile.mkdtemp()
_logger.debug('Install from url: %r', urls)
try:
# 1. Download & unzip missing modules
for module_name, url in urls.items():
for module_name, url in urls.iteritems():
if not url:
continue # nothing to download, local version is already the last one
up = urlparse.urlparse(url)
if up.scheme != apps_server.scheme or up.netloc != apps_server.netloc:
raise openerp.exceptions.AccessDenied()
raise AccessDenied()
try:
_logger.info('Downloading module `%s` from OpenERP Apps', module_name)
@@ -711,7 +664,7 @@ class module(osv.osv):
assert os.path.isdir(os.path.join(tmp, module_name))
# 2a. Copy/Replace module source in addons path
for module_name, url in urls.items():
for module_name, url in urls.iteritems():
if module_name == OPENERP or not url:
continue # OPENERP is special case, handled below, and no URL means local module
module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False)
@@ -722,7 +675,7 @@ class module(osv.osv):
shutil.rmtree(bck)
# 2b. Copy/Replace server+base module source if downloaded
if urls.get(OPENERP, None):
if urls.get(OPENERP):
# special case. it contains the server and the base module.
# extract path is not the same
base_path = os.path.dirname(modules.get_module_path('base'))
@@ -734,51 +687,51 @@ class module(osv.osv):
shutil.copytree(os.path.join(base_path, d), destdir)
# then replace the server by the new "base" module
server_dir = openerp.tools.config['root_path'] # XXX or dirname()
server_dir = tools.config['root_path'] # XXX or dirname()
bck = backup(server_dir)
_logger.info('Copy downloaded module `openerp` to `%s`', server_dir)
shutil.move(os.path.join(tmp, OPENERP), server_dir)
#if bck:
# shutil.rmtree(bck)
self.update_list(cr, uid, context=context)
self.update_list()
with_urls = [m for m, u in urls.items() if u]
downloaded_ids = self.search(cr, uid, [('name', 'in', with_urls)], context=context)
already_installed = self.search(cr, uid, [('id', 'in', downloaded_ids), ('state', '=', 'installed')], context=context)
with_urls = [module_name for module_name, url in urls.iteritems() if url]
downloaded = self.search([('name', 'in', with_urls)])
installed = self.search([('id', 'in', downloaded.ids), ('state', '=', 'installed')])
to_install_ids = self.search(cr, uid, [('name', 'in', urls.keys()), ('state', '=', 'uninstalled')], context=context)
post_install_action = self.button_immediate_install(cr, uid, to_install_ids, context=context)
to_install = self.search([('name', 'in', urls.keys()), ('state', '=', 'uninstalled')])
post_install_action = to_install.button_immediate_install()
if already_installed:
if installed:
# in this case, force server restart to reload python code...
cr.commit()
openerp.service.server.restart()
self._cr.commit()
odoo.service.server.restart()
return {
'type': 'ir.actions.client',
'tag': 'home',
'params': {'wait': True},
}
return post_install_action
finally:
shutil.rmtree(tmp)
def get_apps_server(self, cr, uid, context=None):
@api.model
def get_apps_server(self):
return tools.config.get('apps_server', 'https://apps.openerp.com/apps')
def _update_dependencies(self, cr, uid, mod_browse, depends=None):
if depends is None:
depends = []
existing = set(x.name for x in mod_browse.dependencies_id)
needed = set(depends)
def _update_dependencies(self, depends=None):
existing = set(dep.name for dep in self.dependencies_id)
needed = set(depends or [])
for dep in (needed - existing):
cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (mod_browse.id, dep))
self._cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (self.id, dep))
for dep in (existing - needed):
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (mod_browse.id, dep))
self.invalidate_cache(cr, uid, ['dependencies_id'], [mod_browse.id])
self._cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (self.id, dep))
self.invalidate_cache(['dependencies_id'], self.ids)
def _update_category(self, cr, uid, mod_browse, category='Uncategorized'):
current_category = mod_browse.category_id
def _update_category(self, category='Uncategorized'):
current_category = self.category_id
current_category_path = []
while current_category:
current_category_path.insert(0, current_category.name)
@@ -786,26 +739,27 @@ class module(osv.osv):
categs = category.split('/')
if categs != current_category_path:
cat_id = create_categories(cr, categs)
mod_browse.write({'category_id': cat_id})
cat_id = modules.db.create_categories(self._cr, categs)
self.write({'category_id': cat_id})
def update_translations(self, cr, uid, ids, filter_lang=None, context=None):
@api.multi
def update_translations(self, filter_lang=None):
if not filter_lang:
res_lang = self.pool.get('res.lang')
lang_ids = res_lang.search(cr, uid, [('translatable', '=', True)])
filter_lang = [lang.code for lang in res_lang.browse(cr, uid, lang_ids)]
langs = self.env['res.lang'].search([('translatable', '=', True)])
filter_lang = [lang.code for lang in langs]
elif not isinstance(filter_lang, (list, tuple)):
filter_lang = [filter_lang]
modules = [m.name for m in self.browse(cr, uid, ids) if m.state in ('installed', 'to install', 'to upgrade')]
self.pool.get('ir.translation').load_module_terms(cr, modules, filter_lang, context=context)
mod_names = [mod.name for mod in self if mod.state in ('installed', 'to install', 'to upgrade')]
self.env['ir.translation'].load_module_terms(mod_names, filter_lang)
def check(self, cr, uid, ids, context=None):
for mod in self.browse(cr, uid, ids, context=context):
if not mod.description:
_logger.warning('module %s: description is empty !', mod.name)
@api.multi
def check(self):
for module in self:
if not module.description:
_logger.warning('module %s: description is empty !', module.name)
@api.model
@ormcache()
@tools.ormcache()
def _installed(self):
""" Return the set of installed modules as a dictionary {name: id} """
return {
@@ -814,29 +768,21 @@ class module(osv.osv):
}
DEP_STATES = [
('uninstallable', 'Uninstallable'),
('uninstalled', 'Not Installed'),
('installed', 'Installed'),
('to upgrade', 'To be upgraded'),
('to remove', 'To be removed'),
('to install', 'To be installed'),
('unknown', 'Unknown'),
]
DEP_STATES = STATES + [('unknown', 'Unknown')]
class module_dependency(osv.Model):
class ModuleDependency(models.Model):
_name = "ir.module.module.dependency"
_description = "Module dependency"
# the dependency name
name = fields2.Char(index=True)
name = fields.Char(index=True)
# the module that depends on it
module_id = fields2.Many2one('ir.module.module', 'Module', ondelete='cascade')
module_id = fields.Many2one('ir.module.module', 'Module', ondelete='cascade')
# the module corresponding to the dependency, and its status
depend_id = fields2.Many2one('ir.module.module', 'Dependency', compute='_compute_depend')
state = fields2.Selection(DEP_STATES, string='Status', compute='_compute_state')
depend_id = fields.Many2one('ir.module.module', 'Dependency', compute='_compute_depend')
state = fields.Selection(DEP_STATES, string='Status', compute='_compute_state')
@api.multi
@api.depends('name')
@@ -1,42 +1,39 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models, api
from odoo import api, models
class IrModelReferenceReport(models.AbstractModel):
_name = 'report.base.report_irmodulereference'
@api.model
def _object_find(self, module):
model_data_obj = self.env['ir.model.data']
models_data = model_data_obj.search([('model','=','ir.model'), ('module','=',module.name)])
res_ids = [model_data.res_id for model_data in models_data]
Data = self.env['ir.model.data']
data = Data.search([('model','=','ir.model'), ('module','=',module.name)])
res_ids = data.mapped('res_id')
return self.env['ir.model'].browse(res_ids)
@api.multi
def _fields_find(self, model, module):
res = []
model_obj = self.env[model]
model_fields = self.env['ir.model.fields']
fname_wildcard = 'field_' + model_obj._name.replace('.', '_') + '_%'
module_fields_ids = self.env['ir.model.data'].search([('model', '=', 'ir.model.fields'), ('module', '=', module.name), ('name', 'like', fname_wildcard)])
module_fields_res_ids = [x.res_id for x in module_fields_ids]
if module_fields_res_ids:
module_fields_names = [x.name for x in model_fields.browse(module_fields_res_ids)]
res = model_obj.fields_get(allfields=module_fields_names).items()
res.sort()
return res
Data = self.env['ir.model.data']
fname_wildcard = 'field_' + model.replace('.', '_') + '_%'
data = Data.search([('model', '=', 'ir.model.fields'), ('module', '=', module.name), ('name', 'like', fname_wildcard)])
if data:
res_ids = data.mapped('res_ids')
fnames = self.env['ir.model.fields'].browse(res_ids).mapped('name')
return sorted(self.env[model].fields_get(fnames).iteritems())
return []
@api.multi
def render_html(self, data=None):
report_obj = self.env['report']
module_report = report_obj._get_report_from_name('base.report_irmodulereference')
Report = self.env['report']
report = Report._get_report_from_name('base.report_irmodulereference')
selected_modules = self.env['ir.module.module'].browse(self.ids)
docargs = {
'doc_ids': self.ids,
'doc_model': module_report.model,
'doc_model': report.model,
'docs': selected_modules,
'findobj': self._object_find,
'findfields': self._fields_find,
}
return report_obj.render('base.report_irmodulereference', docargs)
return Report.render('base.report_irmodulereference', docargs)
@@ -5,58 +5,49 @@ import base64
import contextlib
import cStringIO
from openerp import tools
from openerp.osv import fields,osv
from openerp.tools.translate import _
from openerp.tools.misc import get_iso_codes
from odoo import api, fields, models, tools, _
NEW_LANG_KEY = '__new__'
class base_language_export(osv.osv_memory):
class BaseLanguageExport(models.TransientModel):
_name = "base.language.export"
def _get_languages(self, cr, uid, context):
lang_obj = self.pool.get('res.lang')
ids = lang_obj.search(cr, uid, [('translatable', '=', True)])
langs = lang_obj.browse(cr, uid, ids)
return [(NEW_LANG_KEY, _('New Language (Empty translation template)'))] + [(lang.code, lang.name) for lang in langs]
@api.model
def _get_languages(self):
langs = self.env['res.lang'].search([('translatable', '=', True)])
return [(NEW_LANG_KEY, _('New Language (Empty translation template)'))] + \
[(lang.code, lang.name) for lang in langs]
_columns = {
'name': fields.char('File Name', readonly=True),
'lang': fields.selection(_get_languages, 'Language', required=True),
'format': fields.selection([('csv','CSV File'),
('po','PO File'),
('tgz', 'TGZ Archive')], 'File Format', required=True),
'modules': fields.many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', 'Apps To Export', domain=[('state','=','installed')]),
'data': fields.binary('File', readonly=True),
'state': fields.selection([('choose', 'choose'), # choose language
('get', 'get')]) # get the file
}
_defaults = {
'state': 'choose',
'lang': NEW_LANG_KEY,
'format': 'csv',
}
name = fields.Char('File Name', readonly=True)
lang = fields.Selection(_get_languages, string='Language', required=True, default=NEW_LANG_KEY)
format = fields.Selection([('csv','CSV File'), ('po','PO File'), ('tgz', 'TGZ Archive')],
string='File Format', required=True, default='csv')
modules = fields.Many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id',
string='Apps To Export', domain=[('state','=','installed')])
data = fields.Binary('File', readonly=True)
state = fields.Selection([('choose', 'choose'), ('get', 'get')], # choose language or get the file
default='choose')
def act_getfile(self, cr, uid, ids, context=None):
this = self.browse(cr, uid, ids, context=context)[0]
@api.multi
def act_getfile(self):
this = self[0]
lang = this.lang if this.lang != NEW_LANG_KEY else False
mods = sorted(map(lambda m: m.name, this.modules)) or ['all']
mods = sorted(this.mapped('modules.name')) or ['all']
with contextlib.closing(cStringIO.StringIO()) as buf:
tools.trans_export(lang, mods, buf, this.format, cr)
tools.trans_export(lang, mods, buf, this.format, self._cr)
out = base64.encodestring(buf.getvalue())
filename = 'new'
if lang:
filename = get_iso_codes(lang)
filename = tools.get_iso_codes(lang)
elif len(mods) == 1:
filename = mods[0]
extension = this.format
if not lang and extension == 'po':
extension = 'pot'
name = "%s.%s" % (filename, extension)
this.write({ 'state': 'get', 'data': out, 'name': name })
this.write({'state': 'get', 'data': out, 'name': name})
return {
'type': 'ir.actions.act_window',
'res_model': 'base.language.export',
@@ -1,52 +1,45 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import os
import base64
import logging
import os
from tempfile import TemporaryFile
from openerp import tools
from openerp.osv import osv, fields
from openerp.exceptions import UserError
from openerp.tools.translate import _
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class base_language_import(osv.osv_memory):
""" Language Import """
class BaseLanguageImport(models.TransientModel):
_name = "base.language.import"
_description = "Language Import"
_columns = {
'name': fields.char('Language Name', required=True),
'code': fields.char('ISO Code', size=5, help="ISO Language and Country code, e.g. en_US", required=True),
'data': fields.binary('File', required=True),
'filename': fields.char('File Name', required=True),
'overwrite': fields.boolean('Overwrite Existing Terms',
help="If you enable this option, existing translations (including custom ones) "
"will be overwritten and replaced by those in this file"),
}
def import_lang(self, cr, uid, ids, context=None):
if context is None:
context = {}
this = self.browse(cr, uid, ids[0])
if this.overwrite:
context = dict(context, overwrite=True)
fileobj = TemporaryFile('w+')
try:
fileobj.write(base64.decodestring(this.data))
# now we determine the file format
fileobj.seek(0)
fileformat = os.path.splitext(this.filename)[-1][1:].lower()
tools.trans_load_data(cr, fileobj, fileformat, this.code, lang_name=this.name, context=context)
except Exception, e:
_logger.exception('File unsuccessfully imported, due to format mismatch.')
raise UserError(_('File not imported due to format mismatch or a malformed file. (Valid formats are .csv, .po, .pot)\n\nTechnical Details:\n%s') % tools.ustr(e))
finally:
fileobj.close()
name = fields.Char('Language Name', required=True)
code = fields.Char('ISO Code', size=5, required=True,
help="ISO Language and Country code, e.g. en_US")
data = fields.Binary('File', required=True)
filename = fields.Char('File Name', required=True)
overwrite = fields.Boolean('Overwrite Existing Terms',
help="If you enable this option, existing translations (including custom ones) "
"will be overwritten and replaced by those in this file")
@api.multi
def import_lang(self):
this = self[0]
this = this.with_context(overwrite=this.overwrite)
with TemporaryFile('w+') as buf:
try:
buf.write(base64.decodestring(this.data))
# now we determine the file format
buf.seek(0)
fileformat = os.path.splitext(this.filename)[-1][1:].lower()
tools.trans_load_data(this._cr, buf, fileformat, this.code,
lang_name=this.name, context=this._context)
except Exception as e:
_logger.exception('File unsuccessfully imported, due to format mismatch.')
raise UserError(_('File not imported due to format mismatch or a malformed file. (Valid formats are .csv, .po, .pot)\n\nTechnical Details:\n%s') % tools.ustr(e))
return True
@@ -1,43 +1,30 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from operator import itemgetter
from odoo import api, fields, models, _
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
class base_language_install(osv.osv_memory):
""" Install Language"""
class BaseLanguageInstall(models.TransientModel):
_name = "base.language.install"
_description = "Install Language"
def _languages(self, cr, uid, context=None):
return self.pool['res.lang'].get_available(cr, uid, context=context)
@api.model
def _get_languages(self):
return self.env['res.lang'].get_available()
_columns = {
'lang': fields.selection(_languages, 'Language', required=True),
'overwrite': fields.boolean('Overwrite Existing Terms', help="If you check this box, your customized translations will be overwritten and replaced by the official ones."),
'state':fields.selection([('init','init'),('done','done')], 'Status', readonly=True),
}
lang = fields.Selection(_get_languages, string='Language', required=True)
overwrite = fields.Boolean('Overwrite Existing Terms',
help="If you check this box, your customized translations will be overwritten and replaced by the official ones.")
state = fields.Selection([('init', 'init'), ('done', 'done')],
string='Status', readonly=True, default='init')
_defaults = {
'state': 'init',
'overwrite': False
}
def lang_install(self, cr, uid, ids, context=None):
if context is None:
context = {}
language_obj = self.browse(cr, uid, ids)[0]
lang = language_obj.lang
if lang:
modobj = self.pool.get('ir.module.module')
mids = modobj.search(cr, uid, [('state', '=', 'installed')])
if language_obj.overwrite:
context = {'overwrite': True}
modobj.update_translations(cr, uid, mids, lang, context or {})
self.write(cr, uid, ids, {'state': 'done'}, context=context)
@api.multi
def lang_install(self):
this = self[0]
this = this.with_context(overwrite=this.overwrite)
mods = self.env['ir.module.module'].search([('state', '=', 'installed')])
mods.update_translations(this.lang)
this.state = 'done'
return {
'name': _('Language Pack'),
'view_type': 'form',
@@ -45,8 +32,8 @@ class base_language_install(osv.osv_memory):
'view_id': False,
'res_model': 'base.language.install',
'domain': [],
'context': dict(context, active_ids=ids),
'context': dict(self._context, active_ids=self.ids),
'type': 'ir.actions.act_window',
'target': 'new',
'res_id': ids and ids[0] or False,
'res_id': this.id,
}
@@ -1,33 +1,27 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv
from openerp.tools.translate import _
from odoo import api, models, _
class base_module_configuration(osv.osv_memory):
class BaseModuleConfiguration(models.TransientModel):
_name = "base.module.configuration"
def start(self, cr, uid, ids, context=None):
todo_ids = self.pool.get('ir.actions.todo').search(cr, uid,
['|', ('type','=','recurring'), ('state', '=', 'open')])
if not todo_ids:
@api.multi
def start(self):
todos_domain = ['|', ('type','=','recurring'), ('state', '=', 'open')]
if self.env['ir.actions.todo'].search_count(todos_domain):
# Run the config wizards
return self.env['res.config'].start()
else:
# When there is no wizard todo it will display message
data_obj = self.pool.get('ir.model.data')
result = data_obj._get_id(cr, uid, 'base', 'view_base_module_configuration_form')
view_id = data_obj.browse(cr, uid, result).res_id
value = {
'name': _('System Configuration done'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'base.module.configuration',
'view_id': [view_id],
'type': 'ir.actions.act_window',
'target': 'new'
}
return value
# Run the config wizards
config_pool = self.pool.get('res.config')
return config_pool.start(cr, uid, ids, context=context)
base_module_configuration()
view = self.env.ref('base.view_base_module_configuration_form')
return {
'name': _('System Configuration done'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'base.mdule.configuration',
'view_id': [view.id],
'type': 'ir.actions.act_window',
'target': 'new',
}
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from openerp import models, fields, api
from odoo import api, fields, models
class base_module_update(models.TransientModel):
class BaseModuleUpdate(models.TransientModel):
_name = "base.module.update"
_description = "Update Module"
@@ -10,10 +11,11 @@ class base_module_update(models.TransientModel):
added = fields.Integer('Number of modules added', readonly=True)
state = fields.Selection([('init', 'init'), ('done', 'done')], 'Status', readonly=True, default='init')
@api.one
@api.multi
def update_module(self):
self.updated, self.added = self.env['ir.module.module'].update_list()
self.state = 'done'
for this in self:
updated, added = self.env['ir.module.module'].update_list()
this.write({'updated': updated, 'added': added, 'state': 'done'})
return False
@api.multi
@@ -1,34 +1,37 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.exceptions import UserError
import odoo
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class base_module_upgrade(osv.osv_memory):
""" Module Upgrade """
class BaseModuleUpgrade(models.TransientModel):
_name = "base.module.upgrade"
_description = "Module Upgrade"
_columns = {
'module_info': fields.text('Apps to Update',readonly=True),
}
@api.model
@api.returns('ir.module.module')
def get_module_list(self):
states = ['to upgrade', 'to remove', 'to install']
return self.env['ir.module.module'].search([('state', 'in', states)])
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
res = super(base_module_upgrade, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False)
@api.model
def _default_module_info(self):
return "\n".join("%s: %s" % (mod.name, mod.state) for mod in self.get_module_list())
module_info = fields.Text('Apps to Update', readonly=True, default=_default_module_info)
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
res = super(BaseModuleUpgrade, self).fields_view_get(view_id, view_type, toolbar=toolbar,submenu=False)
if view_type != 'form':
return res
context = {} if context is None else context
record_id = context and context.get('active_id', False) or False
active_model = context.get('active_model')
if (not record_id) or (not active_model):
if not(self._context.get('active_model') and self._context.get('active_id')):
return res
ids = self.get_module_list(cr, uid, context=context)
if not ids:
if not self.get_module_list():
res['arch'] = '''<form string="Upgrade Completed" version="7.0">
<separator string="Upgrade Completed" colspan="4"/>
<footer>
@@ -39,55 +42,41 @@ class base_module_upgrade(osv.osv_memory):
return res
def get_module_list(self, cr, uid, context=None):
mod_obj = self.pool.get('ir.module.module')
ids = mod_obj.search(cr, uid, [
('state', 'in', ['to upgrade', 'to remove', 'to install'])])
return ids
def default_get(self, cr, uid, fields, context=None):
mod_obj = self.pool.get('ir.module.module')
ids = self.get_module_list(cr, uid, context=context)
res = mod_obj.read(cr, uid, ids, ['name','state'], context)
return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res))}
def upgrade_module_cancel(self, cr, uid, ids, context=None):
mod_obj = self.pool.get('ir.module.module')
to_installed_ids = mod_obj.search(cr, uid, [
('state', 'in', ['to upgrade', 'to remove'])])
if to_installed_ids:
mod_obj.write(cr, uid, to_installed_ids, {'state': 'installed'}, context=context)
to_uninstalled_ids = mod_obj.search(cr, uid, [
('state', '=', 'to install')])
if to_uninstalled_ids:
mod_obj.write(cr, uid, to_uninstalled_ids, {'state': 'uninstalled'}, context=context)
@api.multi
def upgrade_module_cancel(self):
Module = self.env['ir.module.module']
to_install = Module.search([('state', 'in', ['to upgrade', 'to remove'])])
to_install.write({'state': 'installed'})
to_uninstall = Module.search([('state', '=', 'to install')])
to_uninstall.write({'state': 'uninstalled'})
return {'type': 'ir.actions.act_window_close'}
def upgrade_module(self, cr, uid, ids, context=None):
ir_module = self.pool.get('ir.module.module')
@api.multi
def upgrade_module(self):
Module = self.env['ir.module.module']
# install/upgrade: double-check preconditions
ids = ir_module.search(cr, uid, [('state', 'in', ['to upgrade', 'to install'])])
if ids:
cr.execute("""SELECT d.name FROM ir_module_module m
JOIN ir_module_module_dependency d ON (m.id = d.module_id)
LEFT JOIN ir_module_module m2 ON (d.name = m2.name)
WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s)""",
(tuple(ids), ('uninstalled',)))
unmet_packages = [x[0] for x in cr.fetchall()]
mods = Module.search([('state', 'in', ['to upgrade', 'to install'])])
if mods:
query = """ SELECT d.name
FROM ir_module_module m
JOIN ir_module_module_dependency d ON (m.id = d.module_id)
LEFT JOIN ir_module_module m2 ON (d.name = m2.name)
WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s) """
self._cr.execute(query, (tuple(mods.ids), ('uninstalled',)))
unmet_packages = [row[0] for row in self._cr.fetchall()]
if unmet_packages:
raise UserError(_('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
raise UserError(_('The following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
ir_module.download(cr, uid, ids, context=context)
mods.download()
# terminate transaction before re-creating cursor below
cr.commit()
openerp.api.Environment.reset()
openerp.modules.registry.RegistryManager.new(cr.dbname, update_module=True)
self._cr.commit()
api.Environment.reset()
odoo.modules.registry.RegistryManager.new(self._cr.dbname, update_module=True)
return {'type': 'ir.actions.act_window_close'}
def config(self, cr, uid, ids, context=None):
return self.pool.get('res.config').next(cr, uid, [], context=context)
@api.multi
def config(self):
return self.env['res.config'].next()
@@ -1,52 +1,43 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import contextlib
import cStringIO
from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.exceptions import UserError
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
class base_update_translations(osv.osv_memory):
def _get_languages(self, cr, uid, context):
lang_obj = self.pool.get('res.lang')
ids = lang_obj.search(cr, uid, ['&', ('active', '=', True), ('translatable', '=', True),])
langs = lang_obj.browse(cr, uid, ids)
class BaseUpdateTranslations(models.TransientModel):
_name = 'base.update.translations'
@api.model
def _get_languages(self):
langs = self.env['res.lang'].search([('active', '=', True), ('translatable', '=', True)])
return [(lang.code, lang.name) for lang in langs]
def _get_lang_name(self, cr, uid, lang_code):
lang_obj = self.pool.get('res.lang')
ids = lang_obj.search(cr, uid, [('code', '=', lang_code)])
if not ids:
@api.model
def _default_language(self):
if self._context.get('active_model') == 'res.lang':
lang = self.env['res.lang'].browse(self._context.get('active_id'))
return lang.code
return False
lang = fields.Selection(_get_languages, 'Language', required=True,
default=_default_language)
@api.model
def _get_lang_name(self, lang_code):
lang = self.env['res.lang'].search([('code', '=', lang_code)], limit=1)
if not lang:
raise UserError(_('No language with code "%s" exists') % lang_code)
lang = lang_obj.browse(cr, uid, ids[0])
return lang.name
def act_update(self, cr, uid, ids, context=None):
this = self.browse(cr, uid, ids)[0]
lang_name = self._get_lang_name(cr, uid, this.lang)
buf = cStringIO.StringIO()
tools.trans_export(this.lang, ['all'], buf, 'csv', cr)
tools.trans_load_data(cr, buf, 'csv', this.lang, lang_name=lang_name)
buf.close()
@api.multi
def act_update(self):
this = self[0]
lang_name = self._get_lang_name(this.lang)
with contextlib.closing(cStringIO.StringIO()) as buf:
tools.trans_export(this.lang, ['all'], buf, 'csv', self._cr)
tools.trans_load_data(self._cr, buf, 'csv', this.lang, lang_name=lang_name)
return {'type': 'ir.actions.act_window_close'}
def default_get(self, cr, uid, fields, context=None):
if context is None:
context = {}
res = super(base_update_translations, self).default_get(cr, uid, fields, context=context)
if context.get('active_model') != "res.lang":
return res
record_id = context.get('active_id', False) or False
if record_id:
lang = self.pool.get('res.lang').browse(cr, uid, record_id).code
res.update(lang=lang)
return res
_name = 'base.update.translations'
_columns = {
'lang': fields.selection(_get_languages, 'Language', required=True),
}
+2
View File
@@ -4,6 +4,7 @@ import test_base
import test_basecase
import test_db_cursor
import test_expression
import test_float
import test_func
import test_ir_actions
import test_ir_attachment
@@ -16,6 +17,7 @@ import test_menu
import test_orm
import test_ormcache
import test_osv
import test_phase
import test_qweb
import test_res_config
import test_res_lang
-337
View File
@@ -1,337 +0,0 @@
- |
To check that common dangerous operations are not allowed by the safe_eval mechanism, attempt to
evaluate unauthorized expressions, and verify that they trigger an error.
-
1. Try a few common expressions to verify they work with safe_eval
-
!python {model: ir.model}: |
from openerp.tools.safe_eval import safe_eval
expected = (1, {"a": 9 * 2}, (True, False, None))
actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
assert actual == expected, "Simple python expressions are not working with safe_eval"
-
2. Try simple literal definition to verify it works with literal_eval
-
!python {model: ir.model}: |
import ast
expected = (1, {"a": 9}, (True, False, None))
actual = ast.literal_eval('(1, {"a": 9}, (True, False, None))')
assert actual == expected, "Simple python expressions are not working with literal_eval"
-
3. Try arithmetic expression in literal_eval to verify it does not work
-
!python {model: ir.model}: |
import ast
try:
ast.literal_eval('(1, {"a": 2*9}, (True, False, None))')
assert False, "literal_eval should not accept arithmetic expressions"
except ValueError:
pass
-
4. Try forbidden expressions in literal_eval to verify they are not allowed
-
!python {model: ir.model}: |
import ast
try:
ast.literal_eval('{"a": True.__class__}')
assert False, "literal_eval should accept only literals"
except ValueError:
pass
-
5. Try forbidden expressions in safe_eval to verify they are not allowed (open)
-
!python {model: ir.model}: |
from openerp.tools.safe_eval import safe_eval
from openerp.tools.misc import mute_logger
try:
with mute_logger('openerp.tools.safe_eval'):
safe_eval('open("/etc/passwd","r")')
assert False, "safe_eval should not allow calling open() builtin"
except ValueError:
pass
-
"ORM test: verify that parent_store computation are going right"
-
0. Emulate normal behavior of tree structure storing
-
!python {model: res.partner.category}: |
# pretend the pool has finished loading to avoid deferring parent_store computation
self.pool._init = False
# Force partner_categ.copy() to copy children
self._fields['child_ids'].copy = True
self._columns['child_ids'].copy = True
-
"1.0 Setup test partner categories: parent root"
-
!record {model: res.partner.category, id: test_categ_root}:
name: Root category
-
"1.1 Setup test partner categories: parent category"
-
!record {model: res.partner.category, id: test_categ_0}:
name: Parent category
parent_id: test_categ_root
-
"1.2 Setup test partner categories: child 1"
-
!record {model: res.partner.category, id: test_categ_1}:
name: Child 1
parent_id: test_categ_0
-
"1.3 Setup test partner categories: child 2"
-
!record {model: res.partner.category, id: test_categ_2}:
name: Child 2
parent_id: test_categ_0
-
"1.4 Setup test partner categories: child 2-1"
-
!record {model: res.partner.category, id: test_categ_21}:
name: Child 2-1
parent_id: test_categ_2
-
2. Duplicate the parent category and verify that the children have been duplicated too and are below the new parent
-
!python {model: res.partner.category}: |
self._columns['child_ids'].copy = True # force copying children for test
new_id = self.copy(cr, uid, ref('test_categ_0'))
new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
-
3. Duplicate the children then reassign them to the new parent (1st method) and check the parent_store structure.
-
!python {model: res.partner.category}: |
new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': []})
self.write(cr, uid, [new_child1_id, new_child2_id], {'parent_id': new_id})
new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
-
4. Duplicate the children then reassign them to the new parent (2nd method) and check the parent_store structure.
-
!python {model: res.partner.category}: |
new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': [(6,0,[new_child1_id, new_child2_id])]})
new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
-
5. Duplicate the children then reassign them to the new parent (3rd method) and make sure the parent_store structure is still right.
-
!python {model: res.partner.category}: |
new_child1_id = self.copy(cr, uid, ref('test_categ_1'))
new_child2_id = self.copy(cr, uid, ref('test_categ_2'))
new_id = self.copy(cr, uid, ref('test_categ_0'), {'child_ids': []})
self.write(cr, uid, [new_id], {'child_ids': [(4,new_child1_id), (4,new_child2_id)]})
new_struct = self.search(cr, uid, [('parent_id', 'child_of', new_id)])
assert len(new_struct) == 4, "After duplication, the new object must have the childs records"
old_struct = self.search(cr, uid, [('parent_id', 'child_of', ref('test_categ_0'))])
assert len(old_struct) == 4, "After duplication, previous record must have old childs records only"
assert (not set(new_struct).intersection(old_struct)), "After duplication, nodes should not be mixed"
-
6. Restore pool state after the test
-
!python {model: res.partner.category}: |
self.pool._init = True
self._fields['child_ids'].copy = False
self._columns['child_ids'].copy = False
-
"Float precision tests: verify that float rounding methods are working correctly via res.currency"
-
!python {model: res.currency}: |
from openerp.tools import float_repr
from math import log10
currency = self.browse(cr, uid, ref('base.EUR'))
def try_round(amount, expected, self=self, cr=cr, currency=currency, float_repr=float_repr,
log10=log10):
digits = max(0,-int(log10(currency.rounding)))
result = float_repr(self.round(cr, 1, currency, amount), precision_digits=digits)
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
try_round(2.674,'2.67')
try_round(2.675,'2.68') # in Python 2.7.2, round(2.675,2) gives 2.67
try_round(-2.675,'-2.68') # in Python 2.7.2, round(2.675,2) gives 2.67
try_round(0.001,'0.00')
try_round(-0.001,'-0.00')
try_round(0.0049,'0.00') # 0.0049 is closer to 0 than to 0.01, so should round down
try_round(0.005,'0.01') # the rule is to round half away from zero
try_round(-0.005,'-0.01') # the rule is to round half away from zero
def try_zero(amount, expected, self=self, cr=cr, currency=currency):
assert self.is_zero(cr, 1, currency, amount) == expected, "Rounding error: %s should be zero!" % amount
try_zero(0.01, False)
try_zero(-0.01, False)
try_zero(0.001, True)
try_zero(-0.001, True)
try_zero(0.0046, True)
try_zero(-0.0046, True)
try_zero(2.68-2.675, False) # 2.68 - 2.675 = 0.005 -> rounds to 0.01
try_zero(2.68-2.676, True) # 2.68 - 2.675 = 0.004 -> rounds to 0.0
try_zero(2.676-2.68, True) # 2.675 - 2.68 = -0.004 -> rounds to -0.0
try_zero(2.675-2.68, False) # 2.675 - 2.68 = -0.005 -> rounds to -0.01
def try_compare(amount1, amount2, expected, self=self, cr=cr, currency=currency):
assert self.compare_amounts(cr, 1, currency, amount1, amount2) == expected, \
"Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)
try_compare(0.001, 0.001, 0)
try_compare(-0.001, -0.001, 0)
try_compare(0.001, 0.002, 0)
try_compare(-0.001, -0.002, 0)
try_compare(2.675, 2.68, 0)
try_compare(2.676, 2.68, 0)
try_compare(-2.676, -2.68, 0)
try_compare(2.674, 2.68, -1)
try_compare(-2.674, -2.68, 1)
try_compare(3, 2.68, 1)
try_compare(-3, -2.68, -1)
try_compare(0.01, 0, 1)
try_compare(-0.01, 0, -1)
-
"Float precision tests: verify that float rounding methods are working correctly via tools"
-
!python {model: res.currency}: |
from openerp.tools import float_compare, float_is_zero, float_round, float_repr
def try_round(amount, expected, precision_digits=3, float_round=float_round, float_repr=float_repr, rounding_method='HALF-UP'):
result = float_repr(float_round(amount, precision_digits=precision_digits, rounding_method=rounding_method),
precision_digits=precision_digits)
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
try_round(2.6745, '2.675')
try_round(-2.6745, '-2.675')
try_round(2.6744, '2.674')
try_round(-2.6744, '-2.674')
try_round(0.0004, '0.000')
try_round(-0.0004, '-0.000')
try_round(357.4555, '357.456')
try_round(-357.4555, '-357.456')
try_round(457.4554, '457.455')
try_round(-457.4554, '-457.455')
# Try some rounding value with rounding method UP instead of HALF-UP
# We use 8.175 because when normalizing 8.175 with precision_digits=3 it gives
# us 8175,0000000001234 as value, and if not handle correctly the rounding UP
# value will be incorrect (should be 8,175 and not 8,176)
try_round(8.175, '8.175', rounding_method='UP')
try_round(8.1751, '8.176', rounding_method='UP')
try_round(-8.175, '-8.175', rounding_method='UP')
try_round(-8.1751, '-8.176', rounding_method='UP')
try_round(-6.000, '-6.000', rounding_method='UP')
try_round(1.8, '2', 0, rounding_method='UP')
try_round(-1.8, '-2', 0, rounding_method='UP')
# Extended float range test, inspired by Cloves Almeida's test on bug #882036.
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
precisions = [2, 2, 2, 2, 2, 2, 3, 4]
# Note: max precision for double floats is 53 bits of precision or
# 17 significant decimal digits
for magnitude in range(7):
for i in xrange(len(fractions)):
frac, exp, prec = fractions[i], expecteds[i], precisions[i]
for sign in [-1,1]:
for x in xrange(0,10000,97):
n = x * 10**magnitude
f = sign * (n + frac)
f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp
try_round(f, f_exp, precision_digits=prec)
def try_zero(amount, expected, float_is_zero=float_is_zero):
assert float_is_zero(amount, precision_digits=3) == expected, "Rounding error: %s should be zero!" % amount
try_zero(0.0002, True)
try_zero(-0.0002, True)
try_zero(0.00034, True)
try_zero(0.0005, False)
try_zero(-0.0005, False)
try_zero(0.0008, False)
try_zero(-0.0008, False)
def try_compare(amount1, amount2, expected, float_compare=float_compare):
assert float_compare(amount1, amount2, precision_digits=3) == expected, \
"Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected)
try_compare(0.0003, 0.0004, 0)
try_compare(-0.0003, -0.0004, 0)
try_compare(0.0002, 0.0005, -1)
try_compare(-0.0002, -0.0005, 1)
try_compare(0.0009, 0.0004, 1)
try_compare(-0.0009, -0.0004, -1)
try_compare(557.4555, 557.4556, 0)
try_compare(-557.4555, -557.4556, 0)
try_compare(657.4444, 657.445, -1)
try_compare(-657.4444, -657.445, 1)
# Rounding to unusual rounding units (e.g. coin values)
def try_round(amount, expected, precision_rounding=None, float_round=float_round, float_repr=float_repr):
result = float_repr(float_round(amount, precision_rounding=precision_rounding),
precision_digits=2)
assert result == expected, 'Rounding error: got %s, expected %s' % (result, expected)
try_round(-457.4554, '-457.45', precision_rounding=0.05)
try_round(457.444, '457.50', precision_rounding=0.5)
try_round(457.3, '455.00', precision_rounding=5)
try_round(457.5, '460.00', precision_rounding=5)
try_round(457.1, '456.00', precision_rounding=3)
-
"Float precision tests: check that proper rounding is performed for float persistence"
-
!python {model: res.currency}: |
currency = self.browse(cr, uid, ref('base.EUR'))
res_currency_rate = self.pool.get('res.currency.rate')
from openerp.tools import float_compare, float_is_zero, float_round, float_repr
def try_roundtrip(value, expected, self=self, cr=cr, currency=currency,
res_currency_rate=res_currency_rate):
rate_id = res_currency_rate.create(cr, 1, {'name':'2000-01-01',
'rate': value,
'currency_id': currency.id})
rate = res_currency_rate.read(cr, 1, [rate_id], ['rate'])[0]['rate']
assert rate == expected, 'Roundtrip error: got %s back from db, expected %s' % (rate, expected)
# res.currency.rate uses 6 digits of precision by default
try_roundtrip(2.6748955, 2.674896)
try_roundtrip(-2.6748955, -2.674896)
try_roundtrip(10000.999999, 10000.999999)
try_roundtrip(-10000.999999, -10000.999999)
-
"Float precision tests: verify that invalid parameters are forbidden"
-
!python {model: res.currency}: |
from openerp.tools import float_compare, float_is_zero, float_round
try:
float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)
except AssertionError:
pass
try:
float_compare(0.01, 0.02, precision_digits=3, precision_rounding=0.01)
except AssertionError:
pass
try:
float_round(0.01, precision_digits=3, precision_rounding=0.01)
except AssertionError:
pass
-
Test res.groups name search
-
!python {model: res.groups}: |
all_groups = self.search(cr, uid, [])
full_names = [(group.id, group.full_name) for group in self.browse(cr, uid, all_groups)]
group_ids = self.search(cr, uid, [('full_name', 'like', '%Sale%')])
assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sale' in full_name]), "did not match search for 'Sale'"
group_ids = self.search(cr, uid, [('full_name', 'like', '%Technical%')])
assert set(group_ids) == set([id for (id, full_name) in full_names if 'Technical' in full_name]), "did not match search for 'Technical'"
group_ids = self.search(cr, uid, [('full_name', 'like', '%Sales /%')])
assert set(group_ids) == set([id for (id, full_name) in full_names if 'Sales /' in full_name]), "did not match search for 'Sales /'"
group_ids = self.search(cr, uid, [('full_name', 'in', ['Administration / Access Rights','Contact Creation'])])
assert group_ids, "did not match search for 'Administration / Access Rights' and 'Contact Creation'"
+164 -65
View File
@@ -1,26 +1,23 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from openerp.exceptions import AccessError
from openerp.tools.misc import mute_logger
from openerp.tests import common
from odoo.exceptions import AccessError
from odoo.tests.common import TransactionCase
from odoo.tools.misc import mute_logger
# test group that demo user should not have
USER_DEMO = 'base.user_demo'
GROUP_SYSTEM = 'base.group_system'
GROUP_ERP_MANAGER = 'base.group_erp_manager'
class TestACL(common.TransactionCase):
class TestACL(TransactionCase):
def setUp(self):
super(TestACL, self).setUp()
self.res_currency = self.registry('res.currency')
self.res_partner = self.registry('res.partner')
self.res_users = self.registry('res.users')
self.res_company = self.registry('res.company')
self.demo_uid = self.env.ref('base.user_demo').id
self.demo_user = self.env.ref(USER_DEMO)
self.erp_system_group = self.env.ref(GROUP_SYSTEM)
self.erp_manager_group = self.env.ref(GROUP_ERP_MANAGER)
def _set_field_groups(self, model, field_name, groups):
field = model._fields[field_name]
@@ -32,109 +29,103 @@ class TestACL(common.TransactionCase):
def test_field_visibility_restriction(self):
"""Check that model-level ``groups`` parameter effectively restricts access to that
field for users who do not belong to one of the explicitly allowed groups"""
currency = self.env['res.currency'].sudo(self.demo_user)
# Verify the test environment first
original_fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
original_fields = currency.fields_get([])
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM)
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group before the test")
self.assertTrue('decimal_places' in original_fields, "'decimal_places' field must be properly visible before the test")
self.assertIn('decimal_places', original_fields, "'decimal_places' field must be properly visible before the test")
self.assertNotEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
"Field 'decimal_places' must be found in view definition before the test")
# restrict access to the field and check it's gone
self._set_field_groups(self.res_currency, 'decimal_places', GROUP_SYSTEM)
self._set_field_groups(currency, 'decimal_places', GROUP_SYSTEM)
fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
fields = currency.fields_get([])
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
self.assertFalse('decimal_places' in fields, "'decimal_places' field should be gone")
self.assertNotIn('decimal_places', fields, "'decimal_places' field should be gone")
self.assertEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
"Field 'decimal_places' must not be found in view definition")
# Make demo user a member of the restricted group and check that the field is back
self.erp_system_group.write({'users': [(4, self.demo_uid)]})
has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM)
fields = self.res_currency.fields_get(self.cr, self.demo_uid, [])
form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form')
self.erp_system_group.users += self.demo_user
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
fields = currency.fields_get([])
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
#import pprint; pprint.pprint(fields); pprint.pprint(form_view)
self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group")
self.assertTrue('decimal_places' in fields, "'decimal_places' field must be properly visible again")
self.assertIn('decimal_places', fields, "'decimal_places' field must be properly visible again")
self.assertNotEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
"Field 'decimal_places' must be found in view definition again")
#cleanup
self.erp_system_group.write({'users': [(3, self.demo_uid)]})
@mute_logger('openerp.models')
def test_field_crud_restriction(self):
"Read/Write RPC access to restricted field should be forbidden"
partner = self.env['res.partner'].browse(1).sudo(self.demo_user)
# Verify the test environment first
has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM)
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group")
self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
self.assert_(partner.read(['bank_ids']))
self.assert_(partner.write({'bank_ids': []}))
# Now restrict access to the field and check it's forbidden
self._set_field_groups(self.res_partner, 'bank_ids', GROUP_SYSTEM)
self._set_field_groups(partner, 'bank_ids', GROUP_SYSTEM)
with self.assertRaises(AccessError):
self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])
partner.read(['bank_ids'])
with self.assertRaises(AccessError):
self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})
partner.write({'bank_ids': []})
# Add the restricted group, and check that it works again
self.erp_system_group.write({'users': [(4, self.demo_uid)]})
has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM)
self.erp_system_group.users += self.demo_user
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group")
self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']))
self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}))
#cleanup
self.erp_system_group.write({'users': [(3, self.demo_uid)]})
self.assert_(partner.read(['bank_ids']))
self.assert_(partner.write({'bank_ids': []}))
@mute_logger('openerp.models')
def test_fields_browse_restriction(self):
"""Test access to records having restricted fields"""
self._set_field_groups(self.res_partner, 'email', GROUP_SYSTEM)
partner = self.env['res.partner'].sudo(self.demo_user)
self._set_field_groups(partner, 'email', GROUP_SYSTEM)
pid = self.res_partner.search(self.cr, self.demo_uid, [], limit=1)[0]
part = self.res_partner.browse(self.cr, self.demo_uid, pid)
# accessing fields must no raise exceptions...
part.name
partner = partner.search([], limit=1)
partner.name
# ... except if they are restricted
with self.assertRaises(AccessError) as cm:
with self.assertRaises(AccessError):
with mute_logger('openerp.models'):
part.email
#Useless because we get the title in the client side now in the chrome.js file(map_title)
#self.assertEqual(cm.exception.args[0], 'Access Error')
partner.email
def test_view_create_edit_button_invisibility(self):
""" Test form view Create, Edit, Delete button visibility based on access right of model"""
methods = ['create', 'edit', 'delete']
company_view = self.res_company.fields_view_get(self.cr, self.demo_uid, False, 'form')
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
for method in methods:
self.assertEqual(view_arch.get(method), 'false')
def test_view_create_edit_button_visibility(self):
""" Test form view Create, Edit, Delete button visibility based on access right of model"""
self.erp_system_group.users += self.demo_user
methods = ['create', 'edit', 'delete']
self.erp_manager_group.write({'users': [(4, self.demo_uid)]})
company_view = self.res_company.fields_view_get(self.cr, self.demo_uid, False, 'form')
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
for method in methods:
self.assertIsNone(view_arch.get(method))
# cleanup
self.erp_manager_group.write({'users': [(3, self.demo_uid)]})
def test_m2o_field_create_edit_invisibility(self):
""" Test many2one field Create and Edit option visibility based on access rights of relation field"""
methods = ['create', 'write']
company_view = self.res_company.fields_view_get(self.cr, self.demo_uid, False, 'form')
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
field_node = view_arch.xpath("//field[@name='currency_id']")
self.assertTrue(len(field_node), "currency_id field should be in company from view")
@@ -143,17 +134,125 @@ class TestACL(common.TransactionCase):
def test_m2o_field_create_edit_visibility(self):
""" Test many2one field Create and Edit option visibility based on access rights of relation field"""
self.erp_system_group.users += self.demo_user
methods = ['create', 'write']
self.erp_system_group.write({'users': [(4, self.demo_uid)]})
company_view = self.res_company.fields_view_get(self.cr, self.demo_uid, False, 'form')
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
field_node = view_arch.xpath("//field[@name='currency_id']")
self.assertTrue(len(field_node), "currency_id field should be in company from view")
for method in methods:
self.assertEqual(field_node[0].get('can_' + method), 'true')
# cleanup
self.erp_manager_group.write({'users': [(3, self.demo_uid)]})
if __name__ == '__main__':
unittest.main()
class TestIrRule(TransactionCase):
def test_ir_rule(self):
model_res_partner = self.env.ref('base.model_res_partner')
group_user = self.env.ref('base.group_user')
user_demo = self.env.ref('base.user_demo')
# create an ir_rule for the Employee group with an blank domain
rule1 = self.env['ir.rule'].create({
'name': 'test_rule1',
'model_id': model_res_partner.id,
'domain_force': False,
'groups': [(6, 0, group_user.ids)],
})
# read as demo user the partners (one blank domain)
partners_demo = self.env['res.partner'].sudo(user_demo)
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domain 1=1
rule1.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domain []
rule1.domain_force = "[]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# create another ir_rule for the Employee group (to test multiple rules)
rule2 = self.env['ir.rule'].create({
'name': 'test_rule2',
'model_id': model_res_partner.id,
'domain_force': False,
'groups': [(6, 0, group_user.ids)],
})
# read as demo user with domains [] and blank
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domains 1=1 and blank
rule1.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domains 1=1 and 1=1
rule2.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# create another ir_rule for the Employee group (to test multiple rules)
rule3 = self.env['ir.rule'].create({
'name': 'test_rule3',
'model_id': model_res_partner.id,
'domain_force': False,
'groups': [(6, 0, group_user.ids)],
})
# read the partners as demo user
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domains 1=1, 1=1 and 1=1
rule3.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# modify the global rule on res_company which triggers a recursive check
# of the rules on company
global_rule = self.env.ref('base.res_company_rule_employee')
global_rule.domain_force = "[('id','child_of',[user.company_id.id])]"
# read as demo user (exercising the global company rule)
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# Modify the ir_rule for employee to have a rule that fordids seeing any
# record. We use a domain with implicit AND operator for later tests on
# normalization.
rule2.domain_force = "[('id','=',False),('name','=',False)]"
# check that demo user still sees partners, because group-rules are OR'ed
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# create a new group with demo user in it, and a complex rule
group_test = self.env['res.groups'].create({
'name': 'Test Group',
'users': [(6, 0, user_demo.ids)],
})
# add the rule to the new group, with a domain containing an implicit
# AND operator, which is more tricky because it will have to be
# normalized before combining it
rule3.write({
'domain_force': "[('name','!=',False),('id','!=',False)]",
'groups': [(6, 0, group_test.ids)],
})
# read the partners again as demo user, which should give results
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see partners even with the combined rules.")
# delete global domains (to combine only group domains)
self.env['ir.rule'].search([('groups', '=', False)]).unlink()
# read the partners as demo user (several group domains, no global domain)
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partners.")
+8 -6
View File
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models
from openerp.tools import mute_logger
from openerp.tests import common
from openerp.exceptions import AccessError
from odoo import models
from odoo.tools import mute_logger
from odoo.tests import common
from odoo.exceptions import AccessError
class TestAPI(common.TransactionCase):
@@ -84,7 +86,7 @@ class TestAPI(common.TransactionCase):
ids = map(int, partners)
# modify those partners, and check that partners has not changed
self.registry('res.partner').write(self.cr, self.uid, ids, {'active': False})
partners.write({'active': False})
self.assertEqual(ids, map(int, partners))
# redo the search, and check that the result is now empty
@@ -94,7 +96,7 @@ class TestAPI(common.TransactionCase):
@mute_logger('openerp.models')
def test_06_fields(self):
""" Check that relation fields return records, recordsets or nulls. """
user = self.registry('res.users').browse(self.cr, self.uid, self.uid)
user = self.env.user
self.assertIsRecord(user, 'res.users')
self.assertIsRecord(user.partner_id, 'res.partner')
self.assertIsRecordset(user.groups_id, 'res.groups')
+267 -276
View File
@@ -1,80 +1,113 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import ast
import unittest
import openerp.tests.common as common
from openerp.exceptions import ValidationError
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger
from odoo.tools.safe_eval import safe_eval
class test_base(common.TransactionCase):
def setUp(self):
super(test_base,self).setUp()
self.res_partner = self.registry('res.partner')
self.res_users = self.registry('res.users')
self.res_partner_title = self.registry('res.partner.title')
class TestSafeEval(unittest.TestCase):
# samples use effective TLDs from the Mozilla public suffix
# list at http://publicsuffix.org
self.samples = [
('"Raoul Grosbedon" <raoul@chirurgiens-dentistes.fr> ', 'Raoul Grosbedon', 'raoul@chirurgiens-dentistes.fr'),
('ryu+giga-Sushi@aizubange.fukushima.jp', '', 'ryu+giga-Sushi@aizubange.fukushima.jp'),
('Raoul chirurgiens-dentistes.fr', 'Raoul chirurgiens-dentistes.fr', ''),
(" Raoul O'hara <!@historicalsociety.museum>", "Raoul O'hara", '!@historicalsociety.museum')
]
def test_01_safe_eval(self):
""" Try a few common expressions to verify they work with safe_eval """
expected = (1, {"a": 9 * 2}, (True, False, None))
actual = safe_eval('(1, {"a": 9 * 2}, (True, False, None))')
self.assertEqual(actual, expected, "Simple python expressions are not working with safe_eval")
def test_02_literal_eval(self):
""" Try simple literal definition to verify it works with literal_eval """
expected = (1, {"a": 9}, (True, False, None))
actual = ast.literal_eval('(1, {"a": 9}, (True, False, None))')
self.assertEqual(actual, expected, "Simple python expressions are not working with literal_eval")
def test_03_literal_eval_arithmetic(self):
""" Try arithmetic expression in literal_eval to verify it does not work """
with self.assertRaises(ValueError):
ast.literal_eval('(1, {"a": 2*9}, (True, False, None))')
def test_04_literal_eval_forbidden(self):
""" Try forbidden expressions in literal_eval to verify they are not allowed """
with self.assertRaises(ValueError):
ast.literal_eval('{"a": True.__class__}')
@mute_logger('openerp.tools.safe_eval')
def test_05_safe_eval_forbiddon(self):
""" Try forbidden expressions in safe_eval to verify they are not allowed (open) """
with self.assertRaises(ValueError):
safe_eval('open("/etc/passwd","r")')
# samples use effective TLDs from the Mozilla public suffix
# list at http://publicsuffix.org
SAMPLES = [
('"Raoul Grosbedon" <raoul@chirurgiens-dentistes.fr> ', 'Raoul Grosbedon', 'raoul@chirurgiens-dentistes.fr'),
('ryu+giga-Sushi@aizubange.fukushima.jp', '', 'ryu+giga-Sushi@aizubange.fukushima.jp'),
('Raoul chirurgiens-dentistes.fr', 'Raoul chirurgiens-dentistes.fr', ''),
(" Raoul O'hara <!@historicalsociety.museum>", "Raoul O'hara", '!@historicalsociety.museum'),
]
class TestBase(TransactionCase):
def test_00_res_partner_name_create(self):
cr, uid = self.cr, self.uid
parse = self.res_partner._parse_partner_name
for text, name, mail in self.samples:
self.assertEqual((name,mail), parse(text), 'Partner name parsing failed')
partner_id, dummy = self.res_partner.name_create(cr, uid, text)
partner = self.res_partner.browse(cr, uid, partner_id)
res_partner = self.env['res.partner']
parse = res_partner._parse_partner_name
for text, name, mail in SAMPLES:
self.assertEqual((name, mail), parse(text), 'Partner name parsing failed')
partner_id, dummy = res_partner.name_create(text)
partner = res_partner.browse(partner_id)
self.assertEqual(name or mail, partner.name, 'Partner name incorrect')
self.assertEqual(mail or False, partner.email, 'Partner email incorrect')
def test_10_res_partner_find_or_create(self):
cr,uid = self.cr, self.uid
email = self.samples[0][0]
partner_id, dummy = self.res_partner.name_create(cr, uid, email)
found_id = self.res_partner.find_or_create(cr, uid, email)
res_partner = self.env['res.partner']
email = SAMPLES[0][0]
partner_id, dummy = res_partner.name_create(email)
found_id = res_partner.find_or_create(email)
self.assertEqual(partner_id, found_id, 'find_or_create failed')
partner_id2, dummy2 = self.res_partner.name_create(cr, uid, 'sarah.john@connor.com')
found_id2 = self.res_partner.find_or_create(cr, uid, 'john@connor.com')
partner_id2, dummy2 = res_partner.name_create('sarah.john@connor.com')
found_id2 = res_partner.find_or_create('john@connor.com')
self.assertNotEqual(partner_id2, found_id2, 'john@connor.com match sarah.john@connor.com')
new_id = self.res_partner.find_or_create(cr, uid, self.samples[1][0])
new_id = res_partner.find_or_create(SAMPLES[1][0])
self.assertTrue(new_id > partner_id, 'find_or_create failed - should have created new one')
new_id2 = self.res_partner.find_or_create(cr, uid, self.samples[2][0])
new_id2 = res_partner.find_or_create(SAMPLES[2][0])
self.assertTrue(new_id2 > new_id, 'find_or_create failed - should have created new one again')
def test_15_res_partner_name_search(self):
cr,uid = self.cr, self.uid
for name, active in [
res_partner = self.env['res.partner']
DATA = [
('"A Raoul Grosbedon" <raoul@chirurgiens-dentistes.fr>', False),
('B Raoul chirurgiens-dentistes.fr', True),
("C Raoul O'hara <!@historicalsociety.museum>", True),
('ryu+giga-Sushi@aizubange.fukushima.jp', True),
]:
partner_id, dummy = self.res_partner.name_create(cr, uid, name, context={'default_active': active})
partners = self.res_partner.name_search(cr, uid, 'Raoul')
]
for name, active in DATA:
partner_id, dummy = res_partner.with_context(default_active=active).name_create(name)
partners = res_partner.name_search('Raoul')
self.assertEqual(len(partners), 2, 'Incorrect search number result for name_search')
partners = self.res_partner.name_search(cr, uid, 'Raoul', limit=1)
partners = res_partner.name_search('Raoul', limit=1)
self.assertEqual(len(partners), 1, 'Incorrect search number result for name_search with a limit')
self.assertEqual(partners[0][1], 'B Raoul chirurgiens-dentistes.fr', 'Incorrect partner returned, should be the first active')
def test_20_res_partner_address_sync(self):
cr, uid = self.cr, self.uid
ghoststep = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid,
{'name': 'GhostStep',
'is_company': True,
'street': 'Main Street, 10',
'phone': '123456789',
'email': 'info@ghoststep.com',
'vat': 'BE0477472701',
'type': 'contact'}))
p1 = self.res_partner.browse(cr, uid, self.res_partner.name_create(cr, uid, 'Denis Bladesmith <denis.bladesmith@ghoststep.com>')[0])
res_partner = self.env['res.partner']
ghoststep = res_partner.create({
'name': 'GhostStep',
'is_company': True,
'street': 'Main Street, 10',
'phone': '123456789',
'email': 'info@ghoststep.com',
'vat': 'BE0477472701',
'type': 'contact',
})
p1 = res_partner.browse(res_partner.name_create('Denis Bladesmith <denis.bladesmith@ghoststep.com>')[0])
self.assertEqual(p1.type, 'contact', 'Default type must be "contact"')
p1phone = '123456789#34'
p1.write({'phone': p1phone,
'parent_id': ghoststep.id})
p1.refresh()
self.assertEqual(p1.street, ghoststep.street, 'Address fields must be synced')
self.assertEqual(p1.phone, p1phone, 'Phone should be preserved after address sync')
self.assertEqual(p1.type, 'contact', 'Type should be preserved after address sync')
@@ -84,13 +117,11 @@ class test_base(common.TransactionCase):
p1street = 'Different street, 42'
p1.write({'street': p1street,
'type': 'invoice'})
p1.refresh(), ghoststep.refresh()
self.assertEqual(p1.street, p1street, 'Address fields must not be synced after turning sync off')
self.assertNotEqual(ghoststep.street, p1street, 'Parent address must never be touched')
# turn on sync again
p1.write({'type': 'contact'})
p1.refresh()
self.assertEqual(p1.street, ghoststep.street, 'Address fields must be synced again')
self.assertEqual(p1.phone, p1phone, 'Phone should be preserved after address sync')
self.assertEqual(p1.type, 'contact', 'Type should be preserved after address sync')
@@ -99,213 +130,197 @@ class test_base(common.TransactionCase):
# Modify parent, sync to children
ghoststreet = 'South Street, 25'
ghoststep.write({'street': ghoststreet})
p1.refresh()
self.assertEqual(p1.street, ghoststreet, 'Address fields must be synced automatically')
self.assertEqual(p1.phone, p1phone, 'Phone should not be synced')
self.assertEqual(p1.email, 'denis.bladesmith@ghoststep.com', 'Email should be preserved after sync')
p1street = 'My Street, 11'
p1.write({'street': p1street})
ghoststep.refresh()
self.assertEqual(ghoststep.street, ghoststreet, 'Touching contact should never alter parent')
def test_30_res_partner_first_contact_sync(self):
""" Test initial creation of company/contact pair where contact address gets copied to
company """
cr, uid = self.cr, self.uid
ironshield = self.res_partner.browse(cr, uid, self.res_partner.name_create(cr, uid, 'IronShield')[0])
res_partner = self.env['res.partner']
ironshield = res_partner.browse(res_partner.name_create('IronShield')[0])
self.assertFalse(ironshield.is_company, 'Partners are not companies by default')
self.assertEqual(ironshield.type, 'contact', 'Default type must be "contact"')
ironshield.write({'type': 'contact'})
p1 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid,
{'name': 'Isen Hardearth',
'street': 'Strongarm Avenue, 12',
'parent_id': ironshield.id}))
p1 = res_partner.create({
'name': 'Isen Hardearth',
'street': 'Strongarm Avenue, 12',
'parent_id': ironshield.id,
})
self.assertEquals(p1.type, 'contact', 'Default type must be "contact", not the copied parent type')
ironshield.refresh()
self.assertEqual(ironshield.street, p1.street, 'Address fields should be copied to company')
def test_40_res_partner_address_getc(self):
def test_40_res_partner_address_get(self):
""" Test address_get address resolution mechanism: it should first go down through descendants,
stopping when encountering another is_copmany entity, then go up, stopping again at the first
is_company entity or the root ancestor and if nothing matches, it should use the provided partner
itself """
cr, uid = self.cr, self.uid
elmtree = self.res_partner.browse(cr, uid, self.res_partner.name_create(cr, uid, 'Elmtree')[0])
branch1 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Branch 1',
'parent_id': elmtree.id,
'is_company': True}))
leaf10 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Leaf 10',
'parent_id': branch1.id,
'type': 'invoice'}))
branch11 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Branch 11',
'parent_id': branch1.id,
'type': 'other'}))
leaf111 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Leaf 111',
'parent_id': branch11.id,
'type': 'delivery'}))
res_partner = self.env['res.partner']
elmtree = res_partner.browse(res_partner.name_create('Elmtree')[0])
branch1 = res_partner.create({'name': 'Branch 1',
'parent_id': elmtree.id,
'is_company': True})
leaf10 = res_partner.create({'name': 'Leaf 10',
'parent_id': branch1.id,
'type': 'invoice'})
branch11 = res_partner.create({'name': 'Branch 11',
'parent_id': branch1.id,
'type': 'other'})
leaf111 = res_partner.create({'name': 'Leaf 111',
'parent_id': branch11.id,
'type': 'delivery'})
branch11.write({'is_company': False}) # force is_company after creating 1rst child
branch2 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Branch 2',
'parent_id': elmtree.id,
'is_company': True}))
leaf21 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Leaf 21',
'parent_id': branch2.id,
'type': 'delivery'}))
leaf22 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Leaf 22',
'parent_id': branch2.id}))
leaf23 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid, {'name': 'Leaf 23',
'parent_id': branch2.id,
'type': 'contact'}))
branch2 = res_partner.create({'name': 'Branch 2',
'parent_id': elmtree.id,
'is_company': True})
leaf21 = res_partner.create({'name': 'Leaf 21',
'parent_id': branch2.id,
'type': 'delivery'})
leaf22 = res_partner.create({'name': 'Leaf 22',
'parent_id': branch2.id})
leaf23 = res_partner.create({'name': 'Leaf 23',
'parent_id': branch2.id,
'type': 'contact'})
# go up, stop at branch1
self.assertEqual(self.res_partner.address_get(cr, uid, [leaf111.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(leaf111.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf111.id,
'invoice': leaf10.id,
'contact': branch1.id,
'other': branch11.id}, 'Invalid address resolution')
self.assertEqual(self.res_partner.address_get(cr, uid, [branch11.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(branch11.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf111.id,
'invoice': leaf10.id,
'contact': branch1.id,
'other': branch11.id}, 'Invalid address resolution')
# go down, stop at at all child companies
self.assertEqual(self.res_partner.address_get(cr, uid, [elmtree.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(elmtree.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': elmtree.id,
'invoice': elmtree.id,
'contact': elmtree.id,
'other': elmtree.id}, 'Invalid address resolution')
# go down through children
self.assertEqual(self.res_partner.address_get(cr, uid, [branch1.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(branch1.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf111.id,
'invoice': leaf10.id,
'contact': branch1.id,
'other': branch11.id}, 'Invalid address resolution')
self.assertEqual(self.res_partner.address_get(cr, uid, [branch2.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(branch2.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf21.id,
'invoice': branch2.id,
'contact': branch2.id,
'other': branch2.id}, 'Invalid address resolution. Company is the first encountered contact, therefore default for unfound addresses.')
# go up then down through siblings
self.assertEqual(self.res_partner.address_get(cr, uid, [leaf21.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(leaf21.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf21.id,
'invoice': branch2.id,
'contact': branch2.id,
'other': branch2.id}, 'Invalid address resolution, should scan commercial entity ancestor and its descendants')
self.assertEqual(self.res_partner.address_get(cr, uid, [leaf22.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(leaf22.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf21.id,
'invoice': leaf22.id,
'contact': leaf22.id,
'other': leaf22.id}, 'Invalid address resolution, should scan commercial entity ancestor and its descendants')
self.assertEqual(self.res_partner.address_get(cr, uid, [leaf23.id], ['delivery', 'invoice', 'contact', 'other']),
self.assertEqual(leaf23.address_get(['delivery', 'invoice', 'contact', 'other']),
{'delivery': leaf21.id,
'invoice': leaf23.id,
'contact': leaf23.id,
'other': leaf23.id}, 'Invalid address resolution, `default` should only override if no partner with specific type exists')
# empty adr_pref means only 'contact'
self.assertEqual(self.res_partner.address_get(cr, uid, [elmtree.id], []),
self.assertEqual(elmtree.address_get([]),
{'contact': elmtree.id}, 'Invalid address resolution, no contact means commercial entity ancestor')
self.assertEqual(self.res_partner.address_get(cr, uid, [leaf111.id], []),
self.assertEqual(leaf111.address_get([]),
{'contact': branch1.id}, 'Invalid address resolution, no contact means finding contact in ancestors')
branch11.write({'type': 'contact'})
self.assertEqual(self.res_partner.address_get(cr, uid, [leaf111.id], []),
self.assertEqual(leaf111.address_get([]),
{'contact': branch11.id}, 'Invalid address resolution, branch11 should now be contact')
def test_50_res_partner_commercial_sync(self):
cr, uid = self.cr, self.uid
p0 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid,
{'name': 'Sigurd Sunknife',
'email': 'ssunknife@gmail.com'}))
sunhelm = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid,
{'name': 'Sunhelm',
'is_company': True,
'street': 'Rainbow Street, 13',
'phone': '1122334455',
'email': 'info@sunhelm.com',
'vat': 'BE0477472701',
'child_ids': [(4, p0.id),
(0, 0, {'name': 'Alrik Greenthorn',
'email': 'agr@sunhelm.com'})],
}))
p1 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid,
{'name': 'Otto Blackwood',
'email': 'otto.blackwood@sunhelm.com',
'parent_id': sunhelm.id}))
p11 = self.res_partner.browse(cr, uid, self.res_partner.create(cr, uid,
{'name': 'Gini Graywool',
'email': 'ggr@sunhelm.com',
'parent_id': p1.id}))
p2 = self.res_partner.browse(cr, uid, self.res_partner.search(cr, uid,
[('email', '=', 'agr@sunhelm.com')])[0])
self.res_partner.write(cr, uid, sunhelm.id, {'child_ids': [(0, 0, {'name': 'Ulrik Greenthorn',
'email': 'ugr@sunhelm.com'})]})
p3 = self.res_partner.browse(cr, uid, self.res_partner.search(cr, uid,
[('email', '=', 'ugr@sunhelm.com')])[0])
def test_50_res_partner_commercial_sync(self):
res_partner = self.env['res.partner']
p0 = res_partner.create({'name': 'Sigurd Sunknife',
'email': 'ssunknife@gmail.com'})
sunhelm = res_partner.create({'name': 'Sunhelm',
'is_company': True,
'street': 'Rainbow Street, 13',
'phone': '1122334455',
'email': 'info@sunhelm.com',
'vat': 'BE0477472701',
'child_ids': [(4, p0.id),
(0, 0, {'name': 'Alrik Greenthorn',
'email': 'agr@sunhelm.com'})]})
p1 = res_partner.create({'name': 'Otto Blackwood',
'email': 'otto.blackwood@sunhelm.com',
'parent_id': sunhelm.id})
p11 = res_partner.create({'name': 'Gini Graywool',
'email': 'ggr@sunhelm.com',
'parent_id': p1.id})
p2 = res_partner.search([('email', '=', 'agr@sunhelm.com')], limit=1)
sunhelm.write({'child_ids': [(0, 0, {'name': 'Ulrik Greenthorn',
'email': 'ugr@sunhelm.com'})]})
p3 = res_partner.search([('email', '=', 'ugr@sunhelm.com')], limit=1)
for p in (p0, p1, p11, p2, p3):
p.refresh()
self.assertEquals(p.commercial_partner_id, sunhelm, 'Incorrect commercial entity resolution')
self.assertEquals(p.vat, sunhelm.vat, 'Commercial fields must be automatically synced')
sunhelmvat = 'BE0123456789'
sunhelm.write({'vat': sunhelmvat})
for p in (p0, p1, p11, p2, p3):
p.refresh()
self.assertEquals(p.vat, sunhelmvat, 'Commercial fields must be automatically and recursively synced')
p1vat = 'BE0987654321'
p1.write({'vat': p1vat})
for p in (sunhelm, p0, p11, p2, p3):
p.refresh()
self.assertEquals(p.vat, sunhelmvat, 'Sync to children should only work downstream and on commercial entities')
# promote p1 to commercial entity
p1.write({
'parent_id': sunhelm.id,
'is_company': True,
'name': 'Sunhelm Subsidiary'})
p1.refresh()
p1.write({'parent_id': sunhelm.id,
'is_company': True,
'name': 'Sunhelm Subsidiary'})
self.assertEquals(p1.vat, p1vat, 'Setting is_company should stop auto-sync of commercial fields')
self.assertEquals(p1.commercial_partner_id, p1, 'Incorrect commercial entity resolution after setting is_company')
# writing on parent should not touch child commercial entities
sunhelmvat2 = 'BE0112233445'
sunhelm.write({'vat': sunhelmvat2})
p1.refresh()
self.assertEquals(p1.vat, p1vat, 'Setting is_company should stop auto-sync of commercial fields')
p0.refresh()
self.assertEquals(p0.vat, sunhelmvat2, 'Commercial fields must be automatically synced')
def test_60_read_group(self):
cr, uid = self.cr, self.uid
title_sir = self.res_partner_title.create(cr, uid, {'name': 'Sir...'})
title_lady = self.res_partner_title.create(cr, uid, {'name': 'Lady...'})
title_sir = self.env['res.partner.title'].create({'name': 'Sir...'})
title_lady = self.env['res.partner.title'].create({'name': 'Lady...'})
test_users = [
{'name': 'Alice', 'login': 'alice', 'color': 1, 'function': 'Friend', 'date': '2015-03-28', 'title': title_lady},
{'name': 'Alice', 'login': 'alice2', 'color': 0, 'function': 'Friend', 'date': '2015-01-28', 'title': title_lady},
{'name': 'Bob', 'login': 'bob', 'color': 2, 'function': 'Friend', 'date': '2015-03-02', 'title': title_sir},
{'name': 'Eve', 'login': 'eve', 'color': 3, 'function': 'Eavesdropper', 'date': '2015-03-20', 'title': title_lady},
{'name': 'Nab', 'login': 'nab', 'color': -3, 'function': '5$ Wrench', 'date': '2014-09-10', 'title': title_sir},
{'name': 'Nab', 'login': 'nab-she', 'color': 6, 'function': '5$ Wrench', 'date': '2014-01-02', 'title': title_lady},
{'name': 'Alice', 'login': 'alice', 'color': 1, 'function': 'Friend', 'date': '2015-03-28', 'title': title_lady.id},
{'name': 'Alice', 'login': 'alice2', 'color': 0, 'function': 'Friend', 'date': '2015-01-28', 'title': title_lady.id},
{'name': 'Bob', 'login': 'bob', 'color': 2, 'function': 'Friend', 'date': '2015-03-02', 'title': title_sir.id},
{'name': 'Eve', 'login': 'eve', 'color': 3, 'function': 'Eavesdropper', 'date': '2015-03-20', 'title': title_lady.id},
{'name': 'Nab', 'login': 'nab', 'color': -3, 'function': '5$ Wrench', 'date': '2014-09-10', 'title': title_sir.id},
{'name': 'Nab', 'login': 'nab-she', 'color': 6, 'function': '5$ Wrench', 'date': '2014-01-02', 'title': title_lady.id},
]
ids = [self.res_users.create(cr, uid, u) for u in test_users]
domain = [('id', 'in', ids)]
res_users = self.env['res.users']
user_ids = [res_users.create(vals).id for vals in test_users]
domain = [('id', 'in', user_ids)]
# group on local char field without domain and without active_test (-> empty WHERE clause)
groups_data = self.res_users.read_group(cr, uid, [], fields=['login'], groupby=['login'], orderby='login DESC', context={'active_test': False})
groups_data = res_users.with_context(active_test=False).read_group([], fields=['login'], groupby=['login'], orderby='login DESC')
self.assertGreater(len(groups_data), 6, "Incorrect number of results when grouping on a field")
# group on local char field with limit
groups_data = self.res_users.read_group(cr, uid, domain, fields=['login'], groupby=['login'], orderby='login DESC', limit=3, offset=3)
groups_data = res_users.read_group(domain, fields=['login'], groupby=['login'], orderby='login DESC', limit=3, offset=3)
self.assertEqual(len(groups_data), 3, "Incorrect number of results when grouping on a field with limit")
self.assertEqual(['bob', 'alice2', 'alice'], [g['login'] for g in groups_data], 'Result mismatch')
# group on inherited char field, aggregate on int field (second groupby ignored on purpose)
groups_data = self.res_users.read_group(cr, uid, domain, fields=['name', 'color', 'function'], groupby=['function', 'login'])
groups_data = res_users.read_group(domain, fields=['name', 'color', 'function'], groupby=['function', 'login'])
self.assertEqual(len(groups_data), 3, "Incorrect number of results when grouping on a field")
self.assertEqual(['5$ Wrench', 'Eavesdropper', 'Friend'], [g['function'] for g in groups_data], 'incorrect read_group order')
for group_data in groups_data:
@@ -313,210 +328,186 @@ class test_base(common.TransactionCase):
self.assertEqual(group_data['color'], 3, "Incorrect sum for aggregated data for the column 'color'")
# group on inherited char field, reverse order
groups_data = self.res_users.read_group(cr, uid, domain, fields=['name', 'color'], groupby='name', orderby='name DESC')
groups_data = res_users.read_group(domain, fields=['name', 'color'], groupby='name', orderby='name DESC')
self.assertEqual(['Nab', 'Eve', 'Bob', 'Alice'], [g['name'] for g in groups_data], 'Incorrect ordering of the list')
# group on int field, default ordering
groups_data = self.res_users.read_group(cr, uid, domain, fields=['color'], groupby='color')
groups_data = res_users.read_group(domain, fields=['color'], groupby='color')
self.assertEqual([-3, 0, 1, 2, 3, 6], [g['color'] for g in groups_data], 'Incorrect ordering of the list')
# multi group, second level is int field, should still be summed in first level grouping
groups_data = self.res_users.read_group(cr, uid, domain, fields=['name', 'color'], groupby=['name', 'color'], orderby='name DESC')
groups_data = res_users.read_group(domain, fields=['name', 'color'], groupby=['name', 'color'], orderby='name DESC')
self.assertEqual(['Nab', 'Eve', 'Bob', 'Alice'], [g['name'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([3, 3, 2, 1], [g['color'] for g in groups_data], 'Incorrect ordering of the list')
# group on inherited char field, multiple orders with directions
groups_data = self.res_users.read_group(cr, uid, domain, fields=['name', 'color'], groupby='name', orderby='color DESC, name')
groups_data = res_users.read_group(domain, fields=['name', 'color'], groupby='name', orderby='color DESC, name')
self.assertEqual(len(groups_data), 4, "Incorrect number of results when grouping on a field")
self.assertEqual(['Eve', 'Nab', 'Bob', 'Alice'], [g['name'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([1, 2, 1, 2], [g['name_count'] for g in groups_data], 'Incorrect number of results')
# group on inherited date column (res_partner.date) -> Year-Month, default ordering
groups_data = self.res_users.read_group(cr, uid, domain, fields=['function', 'color', 'date'], groupby=['date'])
groups_data = res_users.read_group(domain, fields=['function', 'color', 'date'], groupby=['date'])
self.assertEqual(len(groups_data), 4, "Incorrect number of results when grouping on a field")
self.assertEqual(['January 2014', 'September 2014', 'January 2015', 'March 2015'], [g['date'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([1, 1, 1, 3], [g['date_count'] for g in groups_data], 'Incorrect number of results')
# group on inherited date column (res_partner.date) -> Year-Month, custom order
groups_data = self.res_users.read_group(cr, uid, domain, fields=['function', 'color', 'date'], groupby=['date'], orderby='date DESC')
groups_data = res_users.read_group(domain, fields=['function', 'color', 'date'], groupby=['date'], orderby='date DESC')
self.assertEqual(len(groups_data), 4, "Incorrect number of results when grouping on a field")
self.assertEqual(['March 2015', 'January 2015', 'September 2014', 'January 2014'], [g['date'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([3, 1, 1, 1], [g['date_count'] for g in groups_data], 'Incorrect number of results')
# group on inherited many2one (res_partner.title), default order
groups_data = self.res_users.read_group(cr, uid, domain, fields=['function', 'color', 'title'], groupby=['title'])
groups_data = res_users.read_group(domain, fields=['function', 'color', 'title'], groupby=['title'])
self.assertEqual(len(groups_data), 2, "Incorrect number of results when grouping on a field")
# m2o is returned as a (id, label) pair
self.assertEqual([(title_lady, 'Lady...'), (title_sir, 'Sir...')], [g['title'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([(title_lady.id, 'Lady...'), (title_sir.id, 'Sir...')], [g['title'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([4, 2], [g['title_count'] for g in groups_data], 'Incorrect number of results')
self.assertEqual([10, -1], [g['color'] for g in groups_data], 'Incorrect aggregation of int column')
# group on inherited many2one (res_partner.title), reversed natural order
groups_data = self.res_users.read_group(cr, uid, domain, fields=['function', 'color', 'title'], groupby=['title'], orderby="title desc")
groups_data = res_users.read_group(domain, fields=['function', 'color', 'title'], groupby=['title'], orderby="title desc")
self.assertEqual(len(groups_data), 2, "Incorrect number of results when grouping on a field")
# m2o is returned as a (id, label) pair
self.assertEqual([(title_sir, 'Sir...'), (title_lady, 'Lady...')], [g['title'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([(title_sir.id, 'Sir...'), (title_lady.id, 'Lady...')], [g['title'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([2, 4], [g['title_count'] for g in groups_data], 'Incorrect number of results')
self.assertEqual([-1, 10], [g['color'] for g in groups_data], 'Incorrect aggregation of int column')
# group on inherited many2one (res_partner.title), multiple orders with m2o in second position
groups_data = self.res_users.read_group(cr, uid, domain, fields=['function', 'color', 'title'], groupby=['title'], orderby="color desc, title desc")
groups_data = res_users.read_group(domain, fields=['function', 'color', 'title'], groupby=['title'], orderby="color desc, title desc")
self.assertEqual(len(groups_data), 2, "Incorrect number of results when grouping on a field")
# m2o is returned as a (id, label) pair
self.assertEqual([(title_lady, 'Lady...'), (title_sir, 'Sir...')], [g['title'] for g in groups_data], 'Incorrect ordering of the result')
self.assertEqual([(title_lady.id, 'Lady...'), (title_sir.id, 'Sir...')], [g['title'] for g in groups_data], 'Incorrect ordering of the result')
self.assertEqual([4, 2], [g['title_count'] for g in groups_data], 'Incorrect number of results')
self.assertEqual([10, -1], [g['color'] for g in groups_data], 'Incorrect aggregation of int column')
# group on inherited many2one (res_partner.title), ordered by other inherited field (color)
groups_data = self.res_users.read_group(cr, uid, domain, fields=['function', 'color', 'title'], groupby=['title'], orderby='color')
groups_data = res_users.read_group(domain, fields=['function', 'color', 'title'], groupby=['title'], orderby='color')
self.assertEqual(len(groups_data), 2, "Incorrect number of results when grouping on a field")
# m2o is returned as a (id, label) pair
self.assertEqual([(title_sir, 'Sir...'), (title_lady, 'Lady...')], [g['title'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([(title_sir.id, 'Sir...'), (title_lady.id, 'Lady...')], [g['title'] for g in groups_data], 'Incorrect ordering of the list')
self.assertEqual([2, 4], [g['title_count'] for g in groups_data], 'Incorrect number of results')
self.assertEqual([-1, 10], [g['color'] for g in groups_data], 'Incorrect aggregation of int column')
class test_partner_recursion(common.TransactionCase):
class TestPartnerRecursion(TransactionCase):
def setUp(self):
super(test_partner_recursion,self).setUp()
self.res_partner = self.registry('res.partner')
cr, uid = self.cr, self.uid
self.p1 = self.res_partner.name_create(cr, uid, 'Elmtree')[0]
self.p2 = self.res_partner.create(cr, uid, {'name': 'Elmtree Child 1', 'parent_id': self.p1})
self.p3 = self.res_partner.create(cr, uid, {'name': 'Elmtree Grand-Child 1.1', 'parent_id': self.p2})
super(TestPartnerRecursion,self).setUp()
res_partner = self.env['res.partner']
self.p1 = res_partner.browse(res_partner.name_create('Elmtree')[0])
self.p2 = res_partner.create({'name': 'Elmtree Child 1', 'parent_id': self.p1.id})
self.p3 = res_partner.create({'name': 'Elmtree Grand-Child 1.1', 'parent_id': self.p2.id})
# split 101, 102, 103 tests to force SQL rollback between them
def test_101_res_partner_recursion(self):
cr, uid, p1, p3 = self.cr, self.uid, self.p1, self.p3
self.assertRaises(ValidationError, self.res_partner.write, cr, uid, [p1], {'parent_id': p3})
with self.assertRaises(ValidationError):
self.p1.write({'parent_id': self.p3.id})
def test_102_res_partner_recursion(self):
cr, uid, p2, p3 = self.cr, self.uid, self.p2, self.p3
self.assertRaises(ValidationError, self.res_partner.write, cr, uid, [p2], {'parent_id': p3})
with self.assertRaises(ValidationError):
self.p2.write({'parent_id': self.p3.id})
def test_103_res_partner_recursion(self):
cr, uid, p3 = self.cr, self.uid, self.p3
self.assertRaises(ValidationError, self.res_partner.write, cr, uid, [p3], {'parent_id': p3})
with self.assertRaises(ValidationError):
self.p3.write({'parent_id': self.p3.id})
def test_104_res_partner_recursion_indirect_cycle(self):
""" Indirect hacky write to create cycle in children """
cr, uid, p2, p3 = self.cr, self.uid, self.p2, self.p3
p3b = self.res_partner.create(cr, uid, {'name': 'Elmtree Grand-Child 1.2', 'parent_id': self.p2})
self.assertRaises(ValidationError, self.res_partner.write, cr, uid, [p2],
{'child_ids': [(1, p3, {'parent_id': p3b}), (1, p3b, {'parent_id': p3})]})
p3b = self.p1.create({'name': 'Elmtree Grand-Child 1.2', 'parent_id': self.p2.id})
with self.assertRaises(ValidationError):
self.p2.write({'child_ids': [(1, self.p3.id, {'parent_id': p3b.id}),
(1, p3b.id, {'parent_id': self.p3.id})]})
def test_110_res_partner_recursion_multi_update(self):
""" multi-write on several partners in same hierarchy must not trigger a false cycle detection """
cr, uid, p1, p2, p3 = self.cr, self.uid, self.p1, self.p2, self.p3
self.assertTrue(self.res_partner.write(cr, uid, [p1,p2,p3], {'phone': '123456'}))
ps = self.p1 + self.p2 + self.p3
self.assertTrue(ps.write({'phone': '123456'}))
class test_translation(common.TransactionCase):
class TestParentStore(TransactionCase):
""" Verify that parent_store computation is done right """
def setUp(self):
super(test_translation, self).setUp()
self.res_category = self.registry('res.partner.category')
self.ir_translation = self.registry('ir.translation')
cr, uid = self.cr, self.uid
self.registry('ir.translation').load_module_terms(cr, ['base'], ['fr_FR'])
self.cat_id = self.res_category.create(cr, uid, {'name': 'Customers'})
self.ir_translation.create(cr, uid, {'name': 'res.partner.category,name', 'module':'base',
'value': 'Clients', 'res_id': self.cat_id, 'lang':'fr_FR', 'state':'translated', 'type': 'model'})
super(TestParentStore, self).setUp()
# pretend the pool has finished loading to avoid deferring parent_store computation
self.patch(self.registry, '_init', False)
def test_101_create_translated_record(self):
cr, uid = self.cr, self.uid
no_context_cat = self.res_category.browse(cr, uid, self.cat_id)
self.assertEqual(no_context_cat.name, 'Customers', "Error in basic name_get")
# force res_partner_category.copy() to copy children
category = self.env['res.partner.category']
self.patch(category._fields['child_ids'], 'copy', True)
self.patch(category._columns['child_ids'], 'copy', True)
fr_context_cat = self.res_category.browse(cr, uid, self.cat_id, context={'lang':'fr_FR'})
self.assertEqual(fr_context_cat.name, 'Clients', "Translation not found")
# setup categories
self.root = category.create({'name': 'Root category'})
self.cat0 = category.create({'name': 'Parent category', 'parent_id': self.root.id})
self.cat1 = category.create({'name': 'Child 1', 'parent_id': self.cat0.id})
self.cat2 = category.create({'name': 'Child 2', 'parent_id': self.cat0.id})
self.cat21 = category.create({'name': 'Child 2-1', 'parent_id': self.cat2.id})
def test_102_duplicate_record(self):
cr, uid = self.cr, self.uid
self.new_cat_id = self.res_category.copy(cr, uid, self.cat_id, context={'lang':'fr_FR'})
def test_duplicate_parent(self):
""" Duplicate the parent category and verify that the children have been duplicated too """
new_cat0 = self.cat0.copy()
new_struct = new_cat0.search([('parent_id', 'child_of', new_cat0.id)])
self.assertEqual(len(new_struct), 4, "After duplication, the new object must have the childs records")
old_struct = new_cat0.search([('parent_id', 'child_of', self.cat0.id)])
self.assertEqual(len(old_struct), 4, "After duplication, previous record must have old childs records only")
self.assertFalse(new_struct & old_struct, "After duplication, nodes should not be mixed")
no_context_cat = self.res_category.browse(cr, uid, self.new_cat_id)
self.assertEqual(no_context_cat.name, 'Customers', "Duplication did not set untranslated value")
def test_duplicate_children_01(self):
""" Duplicate the children then reassign them to the new parent (1st method). """
new_cat1 = self.cat1.copy()
new_cat2 = self.cat2.copy()
new_cat0 = self.cat0.copy({'child_ids': []})
(new_cat1 + new_cat2).write({'parent_id': new_cat0.id})
new_struct = new_cat0.search([('parent_id', 'child_of', new_cat0.id)])
self.assertEqual(len(new_struct), 4, "After duplication, the new object must have the childs records")
old_struct = new_cat0.search([('parent_id', 'child_of', self.cat0.id)])
self.assertEqual(len(old_struct), 4, "After duplication, previous record must have old childs records only")
self.assertFalse(new_struct & old_struct, "After duplication, nodes should not be mixed")
fr_context_cat = self.res_category.browse(cr, uid, self.new_cat_id, context={'lang':'fr_FR'})
self.assertEqual(fr_context_cat.name, 'Clients', "Did not found translation for initial value")
def test_duplicate_children_02(self):
""" Duplicate the children then reassign them to the new parent (2nd method). """
new_cat1 = self.cat1.copy()
new_cat2 = self.cat2.copy()
new_cat0 = self.cat0.copy({'child_ids': [(6, 0, (new_cat1 + new_cat2).ids)]})
new_struct = new_cat0.search([('parent_id', 'child_of', new_cat0.id)])
self.assertEqual(len(new_struct), 4, "After duplication, the new object must have the childs records")
old_struct = new_cat0.search([('parent_id', 'child_of', self.cat0.id)])
self.assertEqual(len(old_struct), 4, "After duplication, previous record must have old childs records only")
self.assertFalse(new_struct & old_struct, "After duplication, nodes should not be mixed")
def test_103_duplicate_record_fr(self):
cr, uid = self.cr, self.uid
self.new_fr_cat_id = self.res_category.copy(cr, uid, self.cat_id, default={'name': 'Clients (copie)'}, context={'lang':'fr_FR'})
def test_duplicate_children_03(self):
""" Duplicate the children then reassign them to the new parent (3rd method). """
new_cat1 = self.cat1.copy()
new_cat2 = self.cat2.copy()
new_cat0 = self.cat0.copy({'child_ids': []})
new_cat0.write({'child_ids': [(4, new_cat1.id), (4, new_cat2.id)]})
new_struct = new_cat0.search([('parent_id', 'child_of', new_cat0.id)])
self.assertEqual(len(new_struct), 4, "After duplication, the new object must have the childs records")
old_struct = new_cat0.search([('parent_id', 'child_of', self.cat0.id)])
self.assertEqual(len(old_struct), 4, "After duplication, previous record must have old childs records only")
self.assertFalse(new_struct & old_struct, "After duplication, nodes should not be mixed")
no_context_cat = self.res_category.browse(cr, uid, self.new_fr_cat_id)
self.assertEqual(no_context_cat.name, 'Customers', "Duplication erased original untranslated value")
fr_context_cat = self.res_category.browse(cr, uid, self.new_fr_cat_id, context={'lang':'fr_FR'})
self.assertEqual(fr_context_cat.name, 'Clients (copie)', "Did not used default value for translated value")
class TestGroups(TransactionCase):
def test_104_orderby_translated_field(self):
""" Test search ordered by a translated field. """
# create a category with a French translation
category = self.env['res.partner.category'].create({'name': 'Padawans'})
category_fr = category.with_context(lang='fr_FR')
category_fr.write({'name': 'Apprentis'})
# search for categories, and sort them by (translated) name
categories = category_fr.search([('id', 'in', [self.cat_id, category.id])], order='name')
self.assertEqual(categories.ids, [category.id, self.cat_id],
"Search ordered by translated name should return Padawans (Apprentis) before Customers (Clients)")
def test_res_groups_fullname_search(self):
all_groups = self.env['res.groups'].search([])
test_state = None
#: Stores state information across multiple test classes
def setUpModule():
global test_state
test_state = {}
def tearDownModule():
global test_state
test_state = None
groups = all_groups.search([('full_name', 'like', '%Sale%')])
self.assertItemsEqual(groups.ids, [g.id for g in all_groups if 'Sale' in g.full_name],
"did not match search for 'Sale'")
class TestPhaseInstall00(unittest.TestCase):
"""
WARNING: Relies on tests being run in alphabetical order
"""
@classmethod
def setUpClass(cls):
cls.state = None
groups = all_groups.search([('full_name', 'like', '%Technical%')])
self.assertItemsEqual(groups.ids, [g.id for g in all_groups if 'Technical' in g.full_name],
"did not match search for 'Technical'")
def test_00_setup(self):
type(self).state = 'init'
groups = all_groups.search([('full_name', 'like', '%Sales /%')])
self.assertItemsEqual(groups.ids, [g.id for g in all_groups if 'Sales /' in g.full_name],
"did not match search for 'Sales /'")
@common.at_install(False)
def test_01_no_install(self):
type(self).state = 'error'
def test_02_check(self):
self.assertEqual(
self.state, 'init',
"Testcase state should not have been transitioned from 00")
class TestPhaseInstall01(unittest.TestCase):
at_install = False
def test_default_norun(self):
self.fail("An unmarket test in a non-at-install case should not run")
@common.at_install(True)
def test_set_run(self):
test_state['set_at_install'] = True
class TestPhaseInstall02(unittest.TestCase):
"""
Can't put the check for test_set_run in the same class: if
@common.at_install does not work for test_set_run, it won't work for
the other one either. Thus move checking of whether test_set_run has
correctly run indeed to a separate class.
Warning: relies on *classes* being run in alphabetical order in test
modules
"""
def test_check_state(self):
self.assertTrue(
test_state.get('set_at_install'),
"The flag should be set if local overriding of runstate")
if __name__ == '__main__':
unittest.main()
groups = all_groups.search([('full_name', 'in', ['Administration / Access Rights','Contact Creation'])])
self.assertTrue(groups, "did not match search for 'Administration / Access Rights' and 'Contact Creation'")
+43 -57
View File
@@ -1,86 +1,72 @@
# -*- coding: utf-8 -*-
import unittest
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.tests import common
from odoo.tests import common
class test_single_transaction_case(common.SingleTransactionCase):
class TestSingleTransactionCase(common.SingleTransactionCase):
"""
Check the whole-class transaction behavior of SingleTransactionCase.
"""
def test_00(self):
"""Create a partner."""
cr, uid = self.cr, self.uid
self.registry('res.partner').create(cr, uid, {'name': 'test_per_class_teardown_partner'})
ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(1, len(ids), "Test partner not found.")
""" Create a partner. """
self.env['res.partner'].create({'name': 'test_per_class_teardown_partner'})
partners = self.env['res.partner'].search([('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(1, len(partners), "Test partner not found.")
def test_01(self):
"""Find the created partner."""
cr, uid = self.cr, self.uid
ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(1, len(ids), "Test partner not found.")
""" Find the created partner. """
partners = self.env['res.partner'].search([('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(1, len(partners), "Test partner not found.")
def test_20a(self):
""" Create a partner with a XML ID """
cr, uid = self.cr, self.uid
res_partner = self.registry('res.partner')
ir_model_data = self.registry('ir.model.data')
pid, _ = res_partner.name_create(cr, uid, 'Mr Blue')
ir_model_data.create(cr, uid, {'name': 'test_partner_blue',
'module': 'base',
'model': 'res.partner',
'res_id': pid})
pid, _ = self.env['res.partner'].name_create('Mr Blue')
self.env['ir.model.data'].create({'name': 'test_partner_blue',
'module': 'base',
'model': 'res.partner',
'res_id': pid})
def test_20b(self):
""" Resolve xml id with ref() and browse_ref() """
cr, uid = self.cr, self.uid
res_partner = self.registry('res.partner')
xid = 'base.test_partner_blue'
p_ref = self.ref(xid)
self.assertTrue(p_ref, "ref() should resolve xid to database ID")
partner = res_partner.browse(cr, uid, p_ref)
p_browse_ref = self.browse_ref(xid)
self.assertEqual(partner, p_browse_ref, "browse_ref() should resolve xid to browse records")
partner = self.env.ref(xid)
pid = self.ref(xid)
self.assertTrue(pid, "ref() should resolve xid to database ID")
self.assertEqual(pid, partner.id, "ref() is not consistent with env.ref()")
partner2 = self.browse_ref(xid)
self.assertEqual(partner, partner2, "browse_ref() should resolve xid to browse records")
class test_transaction_case(common.TransactionCase):
class TestTransactionCase(common.TransactionCase):
"""
Check the per-method transaction behavior of TransactionCase.
"""
def test_00(self):
"""Create a partner."""
cr, uid = self.cr, self.uid
ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(0, len(ids), "Test partner found.")
self.registry('res.partner').create(cr, uid, {'name': 'test_per_class_teardown_partner'})
ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(1, len(ids), "Test partner not found.")
""" Create a partner. """
partners = self.env['res.partner'].search([('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(0, len(partners), "Test partner found.")
self.env['res.partner'].create({'name': 'test_per_class_teardown_partner'})
partners = self.env['res.partner'].search([('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(1, len(partners), "Test partner not found.")
def test_01(self):
"""Don't find the created partner."""
cr, uid = self.cr, self.uid
ids = self.registry('res.partner').search(cr, uid, [('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(0, len(ids), "Test partner found.")
""" Don't find the created partner. """
partners = self.env['res.partner'].search([('name', '=', 'test_per_class_teardown_partner')])
self.assertEqual(0, len(partners), "Test partner found.")
def test_20a(self):
""" Create a partner with a XML ID then resolve xml id with ref() and browse_ref() """
cr, uid = self.cr, self.uid
res_partner = self.registry('res.partner')
ir_model_data = self.registry('ir.model.data')
pid, _ = res_partner.name_create(cr, uid, 'Mr Yellow')
ir_model_data.create(cr, uid, {'name': 'test_partner_yellow',
'module': 'base',
'model': 'res.partner',
'res_id': pid})
pid, _ = self.env['res.partner'].name_create('Mr Yellow')
self.env['ir.model.data'].create({'name': 'test_partner_yellow',
'module': 'base',
'model': 'res.partner',
'res_id': pid})
xid = 'base.test_partner_yellow'
p_ref = self.ref(xid)
self.assertEquals(p_ref, pid, "ref() should resolve xid to database ID")
partner = res_partner.browse(cr, uid, pid)
p_browse_ref = self.browse_ref(xid)
self.assertEqual(partner, p_browse_ref, "browse_ref() should resolve xid to browse records")
if __name__ == '__main__':
unittest.main()
partner = self.env.ref(xid)
pid = self.ref(xid)
self.assertEquals(pid, partner.id, "ref() should resolve xid to database ID")
partner2 = self.browse_ref(xid)
self.assertEqual(partner, partner2, "browse_ref() should resolve xid to browse records")
+6 -5
View File
@@ -1,18 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
import openerp
from openerp.tools.misc import mute_logger
from openerp.tests import common
import odoo
from odoo.tests import common
from odoo.tools.misc import mute_logger
ADMIN_USER_ID = common.ADMIN_USER_ID
def registry():
return openerp.modules.registry.RegistryManager.get(common.get_db_name())
return odoo.registry(common.get_db_name())
class test_cr_execute(unittest.TestCase):
class TestExecute(unittest.TestCase):
""" Try cr.execute with wrong parameters """
@mute_logger('openerp.sql_db')
File diff suppressed because it is too large Load Diff
+177
View File
@@ -0,0 +1,177 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from math import log10
from odoo.tests.common import TransactionCase
from odoo.tools import float_compare, float_is_zero, float_repr, float_round
class TestFloatPrecision(TransactionCase):
""" Tests on float precision. """
def test_rounding_02(self):
""" Test rounding methods with 2 digits. """
currency = self.env.ref('base.EUR')
def try_round(amount, expected):
digits = max(0, -int(log10(currency.rounding)))
result = float_repr(currency.round(amount), precision_digits=digits)
self.assertEqual(result, expected, 'Rounding error: got %s, expected %s' % (result, expected))
try_round(2.674,'2.67')
try_round(2.675,'2.68') # in Python 2.7.2, round(2.675,2) gives 2.67
try_round(-2.675,'-2.68') # in Python 2.7.2, round(2.675,2) gives 2.67
try_round(0.001,'0.00')
try_round(-0.001,'-0.00')
try_round(0.0049,'0.00') # 0.0049 is closer to 0 than to 0.01, so should round down
try_round(0.005,'0.01') # the rule is to round half away from zero
try_round(-0.005,'-0.01') # the rule is to round half away from zero
def try_zero(amount, expected):
self.assertEqual(currency.is_zero(amount), expected,
"Rounding error: %s should be zero!" % amount)
try_zero(0.01, False)
try_zero(-0.01, False)
try_zero(0.001, True)
try_zero(-0.001, True)
try_zero(0.0046, True)
try_zero(-0.0046, True)
try_zero(2.68-2.675, False) # 2.68 - 2.675 = 0.005 -> rounds to 0.01
try_zero(2.68-2.676, True) # 2.68 - 2.675 = 0.004 -> rounds to 0.0
try_zero(2.676-2.68, True) # 2.675 - 2.68 = -0.004 -> rounds to -0.0
try_zero(2.675-2.68, False) # 2.675 - 2.68 = -0.005 -> rounds to -0.01
def try_compare(amount1, amount2, expected):
self.assertEqual(currency.compare_amounts(amount1, amount2), expected,
"Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected))
try_compare(0.001, 0.001, 0)
try_compare(-0.001, -0.001, 0)
try_compare(0.001, 0.002, 0)
try_compare(-0.001, -0.002, 0)
try_compare(2.675, 2.68, 0)
try_compare(2.676, 2.68, 0)
try_compare(-2.676, -2.68, 0)
try_compare(2.674, 2.68, -1)
try_compare(-2.674, -2.68, 1)
try_compare(3, 2.68, 1)
try_compare(-3, -2.68, -1)
try_compare(0.01, 0, 1)
try_compare(-0.01, 0, -1)
def test_rounding_03(self):
""" Test rounding methods with 3 digits. """
def try_round(amount, expected, digits=3, method='HALF-UP'):
value = float_round(amount, precision_digits=digits, rounding_method=method)
result = float_repr(value, precision_digits=digits)
self.assertEqual(result, expected, 'Rounding error: got %s, expected %s' % (result, expected))
try_round(2.6745, '2.675')
try_round(-2.6745, '-2.675')
try_round(2.6744, '2.674')
try_round(-2.6744, '-2.674')
try_round(0.0004, '0.000')
try_round(-0.0004, '-0.000')
try_round(357.4555, '357.456')
try_round(-357.4555, '-357.456')
try_round(457.4554, '457.455')
try_round(-457.4554, '-457.455')
# Try some rounding value with rounding method UP instead of HALF-UP
# We use 8.175 because when normalizing 8.175 with precision_digits=3 it gives
# us 8175,0000000001234 as value, and if not handle correctly the rounding UP
# value will be incorrect (should be 8,175 and not 8,176)
try_round(8.175, '8.175', method='UP')
try_round(8.1751, '8.176', method='UP')
try_round(-8.175, '-8.175', method='UP')
try_round(-8.1751, '-8.176', method='UP')
try_round(-6.000, '-6.000', method='UP')
try_round(1.8, '2', 0, method='UP')
try_round(-1.8, '-2', 0, method='UP')
# Extended float range test, inspired by Cloves Almeida's test on bug #882036.
fractions = [.0, .015, .01499, .675, .67499, .4555, .4555, .45555]
expecteds = ['.00', '.02', '.01', '.68', '.67', '.46', '.456', '.4556']
precisions = [2, 2, 2, 2, 2, 2, 3, 4]
# Note: max precision for double floats is 53 bits of precision or
# 17 significant decimal digits
for magnitude in range(7):
for i in xrange(len(fractions)):
frac, exp, prec = fractions[i], expecteds[i], precisions[i]
for sign in [-1,1]:
for x in xrange(0,10000,97):
n = x * 10**magnitude
f = sign * (n + frac)
f_exp = ('-' if f != 0 and sign == -1 else '') + str(n) + exp
try_round(f, f_exp, digits=prec)
def try_zero(amount, expected):
self.assertEqual(float_is_zero(amount, precision_digits=3), expected,
"Rounding error: %s should be zero!" % amount)
try_zero(0.0002, True)
try_zero(-0.0002, True)
try_zero(0.00034, True)
try_zero(0.0005, False)
try_zero(-0.0005, False)
try_zero(0.0008, False)
try_zero(-0.0008, False)
def try_compare(amount1, amount2, expected):
self.assertEqual(float_compare(amount1, amount2, precision_digits=3), expected,
"Rounding error, compare_amounts(%s,%s) should be %s" % (amount1, amount2, expected))
try_compare(0.0003, 0.0004, 0)
try_compare(-0.0003, -0.0004, 0)
try_compare(0.0002, 0.0005, -1)
try_compare(-0.0002, -0.0005, 1)
try_compare(0.0009, 0.0004, 1)
try_compare(-0.0009, -0.0004, -1)
try_compare(557.4555, 557.4556, 0)
try_compare(-557.4555, -557.4556, 0)
try_compare(657.4444, 657.445, -1)
try_compare(-657.4444, -657.445, 1)
# Rounding to unusual rounding units (e.g. coin values)
def try_round(amount, expected, precision_rounding=None):
value = float_round(amount, precision_rounding=precision_rounding)
result = float_repr(value, precision_digits=2)
self.assertEqual(result, expected, 'Rounding error: got %s, expected %s' % (result, expected))
try_round(-457.4554, '-457.45', precision_rounding=0.05)
try_round(457.444, '457.50', precision_rounding=0.5)
try_round(457.3, '455.00', precision_rounding=5)
try_round(457.5, '460.00', precision_rounding=5)
try_round(457.1, '456.00', precision_rounding=3)
def test_rounding_04(self):
""" check that proper rounding is performed for float persistence """
currency = self.env.ref('base.EUR')
currency_rate = self.env['res.currency.rate']
def try_roundtrip(value, expected):
rate = currency_rate.create({'name':'2000-01-01',
'rate': value,
'currency_id': currency.id})
self.assertEqual(rate.rate, expected,
'Roundtrip error: got %s back from db, expected %s' % (rate, expected))
# res.currency.rate uses 6 digits of precision by default
try_roundtrip(2.6748955, 2.674896)
try_roundtrip(-2.6748955, -2.674896)
try_roundtrip(10000.999999, 10000.999999)
try_roundtrip(-10000.999999, -10000.999999)
def test_rounding_invalid(self):
""" verify that invalid parameters are forbidden """
with self.assertRaises(AssertionError):
float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)
with self.assertRaises(AssertionError):
float_compare(0.01, 0.02, precision_digits=3, precision_rounding=0.01)
with self.assertRaises(AssertionError):
float_round(0.01, precision_digits=3, precision_rounding=0.01)
+7 -5
View File
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import functools
import unittest
from openerp.tools.func import compose
from openerp.tools import frozendict
from odoo.tools import frozendict
from odoo.tools.func import compose
class TestCompose(unittest.TestCase):
def test_basic(self):
str_add = compose(str, lambda a, b: a + b)
self.assertEqual(
str_add(1, 2),
"3")
self.assertEqual(str_add(1, 2), "3")
def test_decorator(self):
""" ensure compose() can be partially applied as a decorator
@@ -21,6 +22,7 @@ class TestCompose(unittest.TestCase):
self.assertEqual(mul(5, 42), u"210")
class TestFrozendict(unittest.TestCase):
def test_frozendict_immutable(self):
""" Ensure that a frozendict is immutable. """
+153 -189
View File
@@ -1,58 +1,50 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.exceptions import ValidationError
import openerp.tests.common as common
from openerp.tools import mute_logger
from odoo.exceptions import ValidationError
from odoo.tools import mute_logger
import odoo.tests.common as common
import odoo.workflow
class TestServerActionsBase(common.TransactionCase):
def setUp(self):
super(TestServerActionsBase, self).setUp()
cr, uid = self.cr, self.uid
# Models
self.ir_actions_server = self.registry('ir.actions.server')
self.ir_actions_client = self.registry('ir.actions.client')
self.ir_values = self.registry('ir.values')
self.ir_model = self.registry('ir.model')
self.ir_model_fields = self.registry('ir.model.fields')
self.res_partner = self.registry('res.partner')
self.res_country = self.registry('res.country')
# Data on which we will run the server action
self.test_country_id = self.res_country.create(cr, uid, {
self.test_country = self.env['res.country'].create({
'name': 'TestingCountry',
'code': 'TY',
'address_format': 'SuperFormat',
})
self.test_country = self.res_country.browse(cr, uid, self.test_country_id)
self.test_partner_id = self.res_partner.create(cr, uid, {
self.test_partner = self.env['res.partner'].create({
'name': 'TestingPartner',
'city': 'OrigCity',
'country_id': self.test_country_id,
'country_id': self.test_country.id,
})
self.test_partner = self.res_partner.browse(cr, uid, self.test_partner_id)
self.context = {
'active_id': self.test_partner_id,
'active_model': 'res.partner',
'active_id': self.test_partner.id,
}
# Model data
self.res_partner_model_id = self.ir_model.search(cr, uid, [('model', '=', 'res.partner')])[0]
self.res_partner_name_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'name')])[0]
self.res_partner_city_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'city')])[0]
self.res_partner_country_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'country_id')])[0]
self.res_partner_parent_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.partner'), ('name', '=', 'parent_id')])[0]
self.res_country_model_id = self.ir_model.search(cr, uid, [('model', '=', 'res.country')])[0]
self.res_country_name_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.country'), ('name', '=', 'name')])[0]
self.res_country_code_field_id = self.ir_model_fields.search(cr, uid, [('model', '=', 'res.country'), ('name', '=', 'code')])[0]
Model = self.env['ir.model']
Fields = self.env['ir.model.fields']
self.res_partner_model = Model.search([('model', '=', 'res.partner')])
self.res_partner_name_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'name')])
self.res_partner_city_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'city')])
self.res_partner_country_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'country_id')])
self.res_partner_parent_field = Fields.search([('model', '=', 'res.partner'), ('name', '=', 'parent_id')])
self.res_country_model = Model.search([('model', '=', 'res.country')])
self.res_country_name_field = Fields.search([('model', '=', 'res.country'), ('name', '=', 'name')])
self.res_country_code_field = Fields.search([('model', '=', 'res.country'), ('name', '=', 'code')])
# create server action to
self.act_id = self.ir_actions_server.create(cr, uid, {
self.action = self.env['ir.actions.server'].create({
'name': 'TestAction',
'condition': 'True',
'model_id': self.res_partner_model_id,
'model_id': self.res_partner_model.id,
'state': 'code',
'code': 'obj.write({"comment": "MyComment"})',
})
@@ -61,346 +53,318 @@ class TestServerActionsBase(common.TransactionCase):
class TestServerActions(TestServerActionsBase):
def test_00_action(self):
cr, uid = self.cr, self.uid
# Do: eval 'True' condition
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
self.test_partner.refresh()
self.action.with_context(self.context).run()
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: invalid condition check')
self.test_partner.write({'comment': False})
# Do: eval False condition, that should be considered as True (void = True)
self.ir_actions_server.write(cr, uid, [self.act_id], {'condition': False})
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
self.test_partner.refresh()
self.action.write({'condition': False})
self.action.with_context(self.context).run()
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: invalid condition check')
# Do: create contextual action
self.ir_actions_server.create_action(cr, uid, [self.act_id])
self.action.create_action()
# Test: ir_values created
ir_values_ids = self.ir_values.search(cr, uid, [('name', '=', 'Run TestAction')])
self.assertEqual(len(ir_values_ids), 1, 'ir_actions_server: create_action should have created an entry in ir_values')
ir_value = self.ir_values.browse(cr, uid, ir_values_ids[0])
self.assertEqual(ir_value.value, 'ir.actions.server,%s' % self.act_id, 'ir_actions_server: created ir_values should reference the server action')
self.assertEqual(ir_value.model, 'res.partner', 'ir_actions_server: created ir_values should be linked to the action base model')
ir_values = self.env['ir.values'].search([('name', '=', 'Run TestAction')])
self.assertEqual(len(ir_values), 1, 'ir_actions_server: create_action should have created an entry in ir_values')
self.assertEqual(ir_values.value, 'ir.actions.server,%s' % self.action.id, 'ir_actions_server: created ir_values should reference the server action')
self.assertEqual(ir_values.model, 'res.partner', 'ir_actions_server: created ir_values should be linked to the action base model')
# Do: remove contextual action
self.ir_actions_server.unlink_action(cr, uid, [self.act_id])
self.action.unlink_action()
# Test: ir_values removed
ir_values_ids = self.ir_values.search(cr, uid, [('name', '=', 'Run TestAction')])
self.assertEqual(len(ir_values_ids), 0, 'ir_actions_server: unlink_action should remove the ir_values record')
ir_values = self.env['ir.values'].search([('name', '=', 'Run TestAction')])
self.assertEqual(len(ir_values), 0, 'ir_actions_server: unlink_action should remove the ir_values record')
def test_10_code(self):
cr, uid = self.cr, self.uid
self.ir_actions_server.write(cr, uid, self.act_id, {
self.action.write({
'state': 'code',
'code': """partner_name = obj.name + '_code'
self.pool["res.partner"].create(cr, uid, {"name": partner_name}, context=context)
workflow"""
'code': ("partner_name = obj.name + '_code'\n"
"obj.env['res.partner'].create({'name': partner_name})\n"
"workflow"),
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: code server action correctly finished should return False')
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner_code')])
self.assertEqual(len(pids), 1, 'ir_actions_server: 1 new partner should have been created')
partners = self.test_partner.search([('name', 'ilike', 'TestingPartner_code')])
self.assertEqual(len(partners), 1, 'ir_actions_server: 1 new partner should have been created')
def test_20_trigger(self):
cr, uid = self.cr, self.uid
Workflow = self.env['workflow']
WorkflowActivity = self.env['workflow.activity']
WorkflowTransition = self.env['workflow.transition']
# Data: code server action (at this point code-based actions should work)
act_id2 = self.ir_actions_server.create(cr, uid, {
action2 = self.action.create({
'name': 'TestAction2',
'type': 'ir.actions.server',
'condition': 'True',
'model_id': self.res_partner_model_id,
'model_id': self.res_partner_model.id,
'state': 'code',
'code': 'obj.write({"comment": "MyComment"})',
})
act_id3 = self.ir_actions_server.create(cr, uid, {
action3 = self.action.create({
'name': 'TestAction3',
'type': 'ir.actions.server',
'condition': 'True',
'model_id': self.res_country_model_id,
'model_id': self.res_country_model.id,
'state': 'code',
'code': 'obj.write({"code": "ZZ"})',
})
# Data: create workflows
partner_wf_id = self.registry('workflow').create(cr, uid, {
partner_workflow = Workflow.create({
'name': 'TestWorkflow',
'osv': 'res.partner',
'on_create': True,
})
partner_act1_id = self.registry('workflow.activity').create(cr, uid, {
partner_activity1 = WorkflowActivity.create({
'name': 'PartnerStart',
'wkf_id': partner_wf_id,
'flow_start': True
'wkf_id': partner_workflow.id,
'flow_start': True,
})
partner_act2_id = self.registry('workflow.activity').create(cr, uid, {
partner_activity2 = WorkflowActivity.create({
'name': 'PartnerTwo',
'wkf_id': partner_wf_id,
'wkf_id': partner_workflow.id,
'kind': 'function',
'action': 'True',
'action_id': act_id2,
'action_id': action2.id,
})
partner_trs1_id = self.registry('workflow.transition').create(cr, uid, {
partner_transition1 = WorkflowTransition.create({
'signal': 'partner_trans',
'act_from': partner_act1_id,
'act_to': partner_act2_id
'act_from': partner_activity1.id,
'act_to': partner_activity2.id,
})
country_wf_id = self.registry('workflow').create(cr, uid, {
country_workflow = Workflow.create({
'name': 'TestWorkflow',
'osv': 'res.country',
'on_create': True,
})
country_act1_id = self.registry('workflow.activity').create(cr, uid, {
country_activity1 = WorkflowActivity.create({
'name': 'CountryStart',
'wkf_id': country_wf_id,
'flow_start': True
'wkf_id': country_workflow.id,
'flow_start': True,
})
country_act2_id = self.registry('workflow.activity').create(cr, uid, {
country_activity2 = WorkflowActivity.create({
'name': 'CountryTwo',
'wkf_id': country_wf_id,
'wkf_id': country_workflow.id,
'kind': 'function',
'action': 'True',
'action_id': act_id3,
'action_id': action3.id,
})
country_trs1_id = self.registry('workflow.transition').create(cr, uid, {
country_transition1 = WorkflowTransition.create({
'signal': 'country_trans',
'act_from': country_act1_id,
'act_to': country_act2_id
'act_from': country_activity1.id,
'act_to': country_activity2.id,
})
# Data: re-create country and partner to benefit from the workflows
self.test_country_id = self.res_country.create(cr, uid, {
country = self.test_country.create({
'name': 'TestingCountry2',
'code': 'T2',
})
self.test_country = self.res_country.browse(cr, uid, self.test_country_id)
self.test_partner_id = self.res_partner.create(cr, uid, {
partner = self.test_partner.create({
'name': 'TestingPartner2',
'country_id': self.test_country_id,
'country_id': country.id,
})
self.test_partner = self.res_partner.browse(cr, uid, self.test_partner_id)
self.context = {
'active_id': self.test_partner_id,
'active_model': 'res.partner',
}
context = dict(self.context, active_id=partner.id)
# Run the action on partner object itself ('base')
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'trigger',
'use_relational_model': 'base',
'wkf_model_id': self.res_partner_model_id,
'wkf_transition_id': partner_trs1_id,
'wkf_model_id': self.res_partner_model.id,
'wkf_transition_id': partner_transition1.id,
})
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
self.test_partner.refresh()
self.assertEqual(self.test_partner.comment, 'MyComment', 'ir_actions_server: incorrect signal trigger')
self.action.with_context(context).run()
self.assertEqual(partner.comment, 'MyComment', 'ir_actions_server: incorrect signal trigger')
# Run the action on related country object ('relational')
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'use_relational_model': 'relational',
'wkf_model_id': self.res_country_model_id,
'wkf_field_id': self.res_partner_country_field_id,
'wkf_transition_id': country_trs1_id,
'wkf_model_id': self.res_country_model.id,
'wkf_field_id': self.res_partner_country_field.id,
'wkf_transition_id': country_transition1.id,
})
self.ir_actions_server.run(cr, uid, [self.act_id], self.context)
self.test_country.refresh()
self.assertEqual(self.test_country.code, 'ZZ', 'ir_actions_server: incorrect signal trigger')
self.action.with_context(context).run()
self.assertEqual(country.code, 'ZZ', 'ir_actions_server: incorrect signal trigger')
# Clear workflow cache, otherwise openerp will try to create workflows even if it has been deleted
from openerp.workflow import clear_cache
clear_cache(cr, uid)
# Clear workflow cache, otherwise odoo will try to create workflows even if it has been deleted
odoo.workflow.clear_cache(self.cr, self.uid)
def test_30_client(self):
cr, uid = self.cr, self.uid
client_action_id = self.registry('ir.actions.client').create(cr, uid, {
client_action = self.env['ir.actions.client'].create({
'name': 'TestAction2',
'tag': 'Test',
})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'client_action',
'action_id': client_action_id,
'action_id': client_action.id,
})
res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
res = self.action.with_context(self.context).run()
self.assertEqual(res['name'], 'TestAction2', 'ir_actions_server: incorrect return result for a client action')
def test_40_crud_create(self):
cr, uid = self.cr, self.uid
_city = 'TestCity'
_name = 'TestNew'
# Do: create a new record in the same model and link it
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'object_create',
'use_create': 'new',
'link_new_record': True,
'link_field_id': self.res_partner_parent_field_id,
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field_id, 'value': _name}),
(0, 0, {'col1': self.res_partner_city_field_id, 'value': _city})],
'link_field_id': self.res_partner_parent_field.id,
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field.id, 'value': _name}),
(0, 0, {'col1': self.res_partner_city_field.id, 'value': _city})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new partner created
pids = self.res_partner.search(cr, uid, [('name', 'ilike', _name)])
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
partner = self.res_partner.browse(cr, uid, pids[0])
partner = self.test_partner.search([('name', 'ilike', _name)])
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
self.assertEqual(partner.city, _city, 'ir_actions_server: TODO')
# Test: new partner linked
self.test_partner.refresh()
self.assertEqual(self.test_partner.parent_id.id, pids[0], 'ir_actions_server: TODO')
self.assertEqual(self.test_partner.parent_id, partner, 'ir_actions_server: TODO')
# Do: copy current record
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'object_create',
'use_create': 'copy_current',
'link_new_record': False,
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field_id, 'value': 'TestCopyCurrent'}),
(0, 0, {'col1': self.res_partner_city_field_id, 'value': 'TestCity'})],
'fields_lines': [(5,),
(0, 0, {'col1': self.res_partner_name_field.id, 'value': 'TestCopyCurrent'}),
(0, 0, {'col1': self.res_partner_city_field.id, 'value': 'TestCity'})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new partner created
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
partner = self.res_partner.browse(cr, uid, pids[0])
partner = self.test_partner.search([('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
self.assertEqual(partner.city, 'TestCity', 'ir_actions_server: TODO')
self.assertEqual(partner.country_id.id, self.test_partner.country_id.id, 'ir_actions_server: TODO')
self.assertEqual(partner.country_id, self.test_partner.country_id, 'ir_actions_server: TODO')
# Do: create a new record in another model
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'object_create',
'use_create': 'new_other',
'crud_model_id': self.res_country_model_id,
'crud_model_id': self.res_country_model.id,
'link_new_record': False,
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'obj.name', 'type': 'equation'}),
(0, 0, {'col1': self.res_country_code_field_id, 'value': 'obj.name[0:2]', 'type': 'equation'})],
'fields_lines': [(5,),
(0, 0, {'col1': self.res_country_name_field.id, 'value': 'obj.name', 'type': 'equation'}),
(0, 0, {'col1': self.res_country_code_field.id, 'value': 'obj.name[0:2]', 'type': 'equation'})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new country created
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'TestingPartner')])
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
country = self.res_country.browse(cr, uid, cids[0])
country = self.test_country.search([('name', 'ilike', 'TestingPartner')])
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
self.assertEqual(country.code, 'TE', 'ir_actions_server: TODO')
# Do: copy a record in another model
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'object_create',
'use_create': 'copy_other',
'crud_model_id': self.res_country_model_id,
'crud_model_id': self.res_country_model.id,
'link_new_record': False,
'ref_object': 'res.country,%s' % self.test_country_id,
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'NewCountry', 'type': 'value'}),
(0, 0, {'col1': self.res_country_code_field_id, 'value': 'NY', 'type': 'value'})],
'ref_object': 'res.country,%s' % self.test_country.id,
'fields_lines': [(5,),
(0, 0, {'col1': self.res_country_name_field.id, 'value': 'NewCountry', 'type': 'value'}),
(0, 0, {'col1': self.res_country_code_field.id, 'value': 'NY', 'type': 'value'})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new country created
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'NewCountry')])
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
country = self.res_country.browse(cr, uid, cids[0])
country = self.test_country.search([('name', 'ilike', 'NewCountry')])
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
self.assertEqual(country.code, 'NY', 'ir_actions_server: TODO')
self.assertEqual(country.address_format, 'SuperFormat', 'ir_actions_server: TODO')
def test_50_crud_write(self):
cr, uid = self.cr, self.uid
_name = 'TestNew'
# Do: create a new record in the same model and link it
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'object_write',
'use_write': 'current',
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field_id, 'value': _name})],
'fields_lines': [(0, 0, {'col1': self.res_partner_name_field.id, 'value': _name})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new partner created
pids = self.res_partner.search(cr, uid, [('name', 'ilike', _name)])
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
partner = self.res_partner.browse(cr, uid, pids[0])
partner = self.test_partner.search([('name', 'ilike', _name)])
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
self.assertEqual(partner.city, 'OrigCity', 'ir_actions_server: TODO')
# Do: copy current record
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'use_write': 'other',
'crud_model_id': self.res_country_model_id,
'ref_object': 'res.country,%s' % self.test_country_id,
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'obj.name', 'type': 'equation'})],
'crud_model_id': self.res_country_model.id,
'ref_object': 'res.country,%s' % self.test_country.id,
'fields_lines': [(5,), (0, 0, {'col1': self.res_country_name_field.id, 'value': 'obj.name', 'type': 'equation'})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new country created
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'TestNew')])
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
country = self.test_country.search([('name', 'ilike', 'TestNew')])
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
# Do: copy a record in another model
self.ir_actions_server.write(cr, uid, [self.act_id], {'fields_lines': [[5]]})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'use_write': 'expression',
'crud_model_id': self.res_country_model_id,
'crud_model_id': self.res_country_model.id,
'write_expression': 'object.country_id',
'fields_lines': [(0, 0, {'col1': self.res_country_name_field_id, 'value': 'NewCountry', 'type': 'value'})],
'fields_lines': [(5,), (0, 0, {'col1': self.res_country_name_field.id, 'value': 'NewCountry', 'type': 'value'})],
})
run_res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
run_res = self.action.with_context(self.context).run()
self.assertFalse(run_res, 'ir_actions_server: create record action correctly finished should return False')
# Test: new country created
cids = self.res_country.search(cr, uid, [('name', 'ilike', 'NewCountry')])
self.assertEqual(len(cids), 1, 'ir_actions_server: TODO')
country = self.test_country.search([('name', 'ilike', 'NewCountry')])
self.assertEqual(len(country), 1, 'ir_actions_server: TODO')
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_60_multi(self):
cr, uid = self.cr, self.uid
# Data: 2 server actions that will be nested
act1_id = self.ir_actions_server.create(cr, uid, {
action1 = self.action.create({
'name': 'Subaction1',
'sequence': 1,
'model_id': self.res_partner_model_id,
'model_id': self.res_partner_model.id,
'state': 'code',
'code': 'action = {"type": "ir.actions.act_window"}',
})
act2_id = self.ir_actions_server.create(cr, uid, {
action2 = self.action.create({
'name': 'Subaction2',
'sequence': 2,
'model_id': self.res_partner_model_id,
'model_id': self.res_partner_model.id,
'state': 'object_create',
'use_create': 'copy_current',
})
act3_id = self.ir_actions_server.create(cr, uid, {
action3 = self.action.create({
'name': 'Subaction3',
'sequence': 3,
'model_id': self.res_partner_model_id,
'model_id': self.res_partner_model.id,
'state': 'code',
'code': 'action = {"type": "ir.actions.act_url"}',
})
self.ir_actions_server.write(cr, uid, [self.act_id], {
self.action.write({
'state': 'multi',
'child_ids': [(6, 0, [act1_id, act2_id, act3_id])],
'child_ids': [(6, 0, [action1.id, action2.id, action3.id])],
})
# Do: run the action
res = self.ir_actions_server.run(cr, uid, [self.act_id], context=self.context)
res = self.action.with_context(self.context).run()
# Test: new partner created
pids = self.res_partner.search(cr, uid, [('name', 'ilike', 'TestingPartner (copy)')]) # currently res_partner overrides default['name'] whatever its value
self.assertEqual(len(pids), 1, 'ir_actions_server: TODO')
# currently res_partner overrides default['name'] whatever its value
partner = self.test_partner.search([('name', 'ilike', 'TestingPartner (copy)')])
self.assertEqual(len(partner), 1, 'ir_actions_server: TODO')
# Test: action returned
self.assertEqual(res.get('type'), 'ir.actions.act_url')
# Test loops
with self.assertRaises(ValidationError):
self.ir_actions_server.write(cr, uid, [self.act_id], {
'child_ids': [(6, 0, [self.act_id])]
self.action.write({
'child_ids': [(6, 0, [self.action.id])]
})
if __name__ == '__main__':
unittest.main()
+35 -50
View File
@@ -1,17 +1,20 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import hashlib
import os
import openerp
import openerp.tests.common
from odoo.tests.common import TransactionCase
HASH_SPLIT = 2 # FIXME: testing implementations detail is not a good idea
class test_ir_attachment(openerp.tests.common.TransactionCase):
class TestIrAttachment(TransactionCase):
def setUp(self):
super(test_ir_attachment, self).setUp()
registry, cr, uid = self.registry, self.cr, self.uid
self.ira = registry('ir.attachment')
self.filestore = self.ira._filestore(cr, uid)
super(TestIrAttachment, self).setUp()
self.Attachment = self.env['ir.attachment']
self.ira = self.Attachment._model
self.filestore = self.Attachment._filestore()
# Blob1
self.blob1 = 'blob1'
@@ -20,73 +23,55 @@ class test_ir_attachment(openerp.tests.common.TransactionCase):
self.blob1_fname = blob1_hash[:HASH_SPLIT] + '/' + blob1_hash
# Blob2
blob2 = 'blob2'
self.blob2_b64 = blob2.encode('base64')
self.blob2 = 'blob2'
self.blob2_b64 = self.blob2.encode('base64')
def test_01_store_in_db(self):
registry, cr, uid = self.registry, self.cr, self.uid
# force storing in database
registry('ir.config_parameter').set_param(cr, uid, 'ir_attachment.location', 'db')
self.env['ir.config_parameter'].set_param('ir_attachment.location', 'db')
# 'ir_attachment.location' is undefined test database storage
a1 = self.ira.create(cr, uid, {'name': 'a1', 'datas': self.blob1_b64})
a1_read = self.ira.read(cr, uid, [a1], ['datas'])
self.assertEqual(a1_read[0]['datas'], self.blob1_b64)
a1 = self.Attachment.create({'name': 'a1', 'datas': self.blob1_b64})
self.assertEqual(a1.datas, self.blob1_b64)
a1_db_datas = self.ira.browse(cr, uid, a1).db_datas
a1_db_datas = a1.db_datas
self.assertEqual(a1_db_datas, self.blob1_b64)
def test_02_store_on_disk(self):
registry, cr, uid = self.registry, self.cr, self.uid
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
self.assertEqual(a2_store_fname, self.blob1_fname)
self.assertTrue(os.path.isfile(os.path.join(self.filestore, a2_store_fname)))
a2 = self.Attachment.create({'name': 'a2', 'datas': self.blob1_b64})
self.assertEqual(a2.store_fname, self.blob1_fname)
self.assertTrue(os.path.isfile(os.path.join(self.filestore, a2.store_fname)))
def test_03_no_duplication(self):
registry, cr, uid = self.registry, self.cr, self.uid
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
a3 = self.ira.create(cr, uid, {'name': 'a3', 'datas': self.blob1_b64})
a3_store_fname = self.ira.browse(cr, uid, a3).store_fname
self.assertEqual(a3_store_fname, a2_store_fname)
a2 = self.Attachment.create({'name': 'a2', 'datas': self.blob1_b64})
a3 = self.Attachment.create({'name': 'a3', 'datas': self.blob1_b64})
self.assertEqual(a3.store_fname, a2.store_fname)
def test_04_keep_file(self):
registry, cr, uid = self.registry, self.cr, self.uid
a2 = self.Attachment.create({'name': 'a2', 'datas': self.blob1_b64})
a3 = self.Attachment.create({'name': 'a3', 'datas': self.blob1_b64})
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
a3 = self.ira.create(cr, uid, {'name': 'a3', 'datas': self.blob1_b64})
a2_fn = os.path.join(self.filestore, a2.store_fname)
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
a2_fn = os.path.join(self.filestore, a2_store_fname)
self.ira.unlink(cr, uid, [a3])
a3.unlink()
self.assertTrue(os.path.isfile(a2_fn))
# delete a2 it is unlinked
self.ira.unlink(cr, uid, [a2])
a2.unlink()
self.assertFalse(os.path.isfile(a2_fn))
def test_05_change_data_change_file(self):
registry, cr, uid = self.registry, self.cr, self.uid
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
a2_fn = os.path.join(self.filestore, a2_store_fname)
a2 = self.Attachment.create({'name': 'a2', 'datas': self.blob1_b64})
a2_store_fname1 = a2.store_fname
a2_fn = os.path.join(self.filestore, a2_store_fname1)
self.assertTrue(os.path.isfile(a2_fn))
self.ira.write(cr, uid, [a2], {'datas': self.blob2_b64})
a2.write({'datas': self.blob2_b64})
self.assertFalse(os.path.isfile(a2_fn))
new_a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
self.assertNotEqual(a2_store_fname, new_a2_store_fname)
a2_store_fname2 = a2.store_fname
self.assertNotEqual(a2_store_fname1, a2_store_fname2)
new_a2_fn = os.path.join(self.filestore, new_a2_store_fname)
self.assertTrue(os.path.isfile(new_a2_fn))
a2_fn = os.path.join(self.filestore, a2_store_fname2)
self.assertTrue(os.path.isfile(a2_fn))
+66 -71
View File
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
import functools
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import exceptions
from openerp.tests import common
from odoo import exceptions
from odoo.tests.common import TransactionCase, ADMIN_USER_ID
def noid(d):
""" Removes values that are not relevant for the test comparisons """
@@ -10,17 +10,19 @@ def noid(d):
d.pop('action_id', None)
return d
class FiltersCase(common.TransactionCase):
class FiltersCase(TransactionCase):
def build(self, model, *args):
Model = self.registry(model)
for vars in args:
Model.create(self.cr, common.ADMIN_USER_ID, vars, {})
Model = self.env[model].sudo(ADMIN_USER_ID)
for vals in args:
Model.create(vals)
class TestGetFilters(FiltersCase):
def setUp(self):
super(TestGetFilters, self).setUp()
self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
self.USER_ID = self.USER[0]
self.USER_NG = self.env['res.users'].name_search('demo')[0]
self.USER_ID = self.USER_NG[0]
def test_own_filters(self):
self.build(
@@ -30,14 +32,13 @@ class TestGetFilters(FiltersCase):
dict(name='c', user_id=self.USER_ID, model_id='ir.filters'),
dict(name='d', user_id=self.USER_ID, model_id='ir.filters'))
filters = self.registry('ir.filters').get_filters(
self.cr, self.USER_ID, 'ir.filters')
filters = self.env['ir.filters'].sudo(self.USER_ID).get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', is_default=False, user_id=self.USER, domain='[]', context='{}', sort='[]'),
dict(name='b', is_default=False, user_id=self.USER, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}', sort='[]'),
dict(name='d', is_default=False, user_id=self.USER, domain='[]', context='{}', sort='[]'),
dict(name='a', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
dict(name='b', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
dict(name='d', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
])
def test_global_filters(self):
@@ -49,8 +50,7 @@ class TestGetFilters(FiltersCase):
dict(name='d', user_id=False, model_id='ir.filters'),
)
filters = self.registry('ir.filters').get_filters(
self.cr, self.USER_ID, 'ir.filters')
filters = self.env['ir.filters'].sudo(self.USER_ID).get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', is_default=False, user_id=False, domain='[]', context='{}', sort='[]'),
@@ -63,40 +63,40 @@ class TestGetFilters(FiltersCase):
self.build(
'ir.filters',
dict(name='a', user_id=False, model_id='ir.filters'),
dict(name='b', user_id=common.ADMIN_USER_ID, model_id='ir.filters'),
dict(name='b', user_id=ADMIN_USER_ID, model_id='ir.filters'),
dict(name='c', user_id=self.USER_ID, model_id='ir.filters'),
dict(name='d', user_id=common.ADMIN_USER_ID, model_id='ir.filters') )
dict(name='d', user_id=ADMIN_USER_ID, model_id='ir.filters') )
filters = self.registry('ir.filters').get_filters(
self.cr, self.USER_ID, 'ir.filters')
filters = self.env['ir.filters'].sudo(self.USER_ID).get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', is_default=False, user_id=False, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=self.USER, domain='[]', context='{}', sort='[]'),
dict(name='c', is_default=False, user_id=self.USER_NG, domain='[]', context='{}', sort='[]'),
])
class TestOwnDefaults(FiltersCase):
def setUp(self):
super(TestOwnDefaults, self).setUp()
self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
self.USER_ID = self.USER[0]
self.USER_NG = self.env['res.users'].name_search('demo')[0]
self.USER_ID = self.USER_NG[0]
def test_new_no_filter(self):
"""
When creating a @is_default filter with no existing filter, that new
filter gets the default flag
"""
Filters = self.registry('ir.filters')
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters = self.env['ir.filters'].sudo(self.USER_ID)
Filters.create_or_replace({
'name': 'a',
'model_id': 'ir.filters',
'user_id': self.USER_ID,
'is_default': True,
})
filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
filters = Filters.get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', user_id=self.USER, is_default=True,
dict(name='a', user_id=self.USER_NG, is_default=True,
domain='[]', context='{}', sort='[]')
])
@@ -111,19 +111,19 @@ class TestOwnDefaults(FiltersCase):
dict(name='b', user_id=self.USER_ID, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters = self.env['ir.filters'].sudo(self.USER_ID)
Filters.create_or_replace({
'name': 'c',
'model_id': 'ir.filters',
'user_id': self.USER_ID,
'is_default': True,
})
filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
filters = Filters.get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}', sort='[]'),
dict(name='a', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=self.USER_NG, is_default=True, domain='[]', context='{}', sort='[]'),
])
def test_new_filter_existing_default(self):
@@ -137,19 +137,19 @@ class TestOwnDefaults(FiltersCase):
dict(name='b', is_default=True, user_id=self.USER_ID, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters = self.env['ir.filters'].sudo(self.USER_ID)
Filters.create_or_replace({
'name': 'c',
'model_id': 'ir.filters',
'user_id': self.USER_ID,
'is_default': True,
})
filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
filters = Filters.get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', user_id=self.USER, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=self.USER, is_default=True, domain='[]', context='{}', sort='[]'),
dict(name='a', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='c', user_id=self.USER_NG, is_default=True, domain='[]', context='{}', sort='[]'),
])
def test_update_filter_set_default(self):
@@ -163,25 +163,26 @@ class TestOwnDefaults(FiltersCase):
dict(name='b', is_default=True, user_id=self.USER_ID, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters = self.env['ir.filters'].sudo(self.USER_ID)
Filters.create_or_replace({
'name': 'a',
'model_id': 'ir.filters',
'user_id': self.USER_ID,
'is_default': True,
})
filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
filters = Filters.get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', user_id=self.USER, is_default=True, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='a', user_id=self.USER_NG, is_default=True, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=self.USER_NG, is_default=False, domain='[]', context='{}', sort='[]'),
])
class TestGlobalDefaults(FiltersCase):
def setUp(self):
super(TestGlobalDefaults, self).setUp()
self.USER = self.registry('res.users').name_search(self.cr, self.uid, 'demo')[0]
self.USER_ID = self.USER[0]
self.USER_NG = self.env['res.users'].name_search('demo')[0]
self.USER_ID = self.USER_NG[0]
def test_new_filter_not_default(self):
"""
@@ -194,14 +195,14 @@ class TestGlobalDefaults(FiltersCase):
dict(name='b', user_id=False, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters = self.env['ir.filters'].sudo(self.USER_ID)
Filters.create_or_replace({
'name': 'c',
'model_id': 'ir.filters',
'user_id': False,
'is_default': True,
})
filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
filters = Filters.get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', user_id=False, is_default=False, domain='[]', context='{}', sort='[]'),
@@ -220,9 +221,9 @@ class TestGlobalDefaults(FiltersCase):
dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters = self.env['ir.filters'].sudo(self.USER_ID)
with self.assertRaises(exceptions.Warning):
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters.create_or_replace({
'name': 'c',
'model_id': 'ir.filters',
'user_id': False,
@@ -240,10 +241,9 @@ class TestGlobalDefaults(FiltersCase):
dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters = self.env['ir.filters'].sudo(self.USER_ID)
with self.assertRaises(exceptions.Warning):
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters.create_or_replace({
'name': 'a',
'model_id': 'ir.filters',
'user_id': False,
@@ -260,42 +260,37 @@ class TestGlobalDefaults(FiltersCase):
dict(name='b', is_default=True, user_id=False, model_id='ir.filters'),
)
Filters = self.registry('ir.filters')
Filters = self.env['ir.filters'].sudo(self.USER_ID)
context_value = "{'some_key': True}"
Filters.create_or_replace(self.cr, self.USER_ID, {
Filters.create_or_replace({
'name': 'b',
'model_id': 'ir.filters',
'user_id': False,
'context': context_value,
'is_default': True,
})
filters = Filters.get_filters(self.cr, self.USER_ID, 'ir.filters')
filters = Filters.get_filters('ir.filters')
self.assertItemsEqual(map(noid, filters), [
dict(name='a', user_id=False, is_default=False, domain='[]', context='{}', sort='[]'),
dict(name='b', user_id=False, is_default=True, domain='[]', context=context_value, sort='[]'),
])
from openerp.tests.common import TransactionCase
class TestReadGroup(TransactionCase):
"""Test function read_group with groupby on a many2one field to a model
(in test, "user_id" to "res.users") which is ordered by an inherited not stored field (in
test, "name" inherited from "res.partners").
"""
def setUp(self):
super(TestReadGroup, self).setUp()
self.ir_filters_model = self.env['ir.filters']
self.res_partner_model = self.env['res.partner']
self.res_users_model = self.env['res.users']
def test_read_group_1(self):
self.assertEqual(self.res_users_model._order, "name, login", "Model res.users must be ordered by name, login")
self.assertFalse(self.res_users_model._fields['name'].store, "Field name is not stored in res.users")
Users = self.env['res.users']
self.assertEqual(Users._order, "name, login", "Model res.users must be ordered by name, login")
self.assertFalse(Users._fields['name'].store, "Field name is not stored in res.users")
filter_a = self.ir_filters_model.create(dict(name="Filter_A", model_id="ir.filters"))
filter_b = self.ir_filters_model.create(dict(name="Filter_B", model_id="ir.filters"))
Filters = self.env['ir.filters']
filter_a = Filters.create(dict(name="Filter_A", model_id="ir.filters"))
filter_b = Filters.create(dict(name="Filter_B", model_id="ir.filters"))
filter_b.write(dict(user_id=False))
res = self.ir_filters_model.read_group([], ['name', 'user_id'], ['user_id'])
res = Filters.read_group([], ['name', 'user_id'], ['user_id'])
self.assertTrue(any(val['user_id'] == False for val in res), "At least one group must contain val['user_id'] == False.")
-189
View File
@@ -1,189 +0,0 @@
-
Exercise ir_rule.py code (and indirectly expression.py).
-
Create an ir_rule for the Employee group (called base.group_user)
with an blank domain.
-
!record {model: ir.rule, id: test_rule}:
model_id: base.model_res_partner
domain_force: False
name: test_rule
groups:
- base.group_user
perm_unlink: 1
perm_write: 1
perm_read: 1
perm_create: 1
-
Read as demo user the partners (one blank domain).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Domain is not empty.
-
!record {model: ir.rule, id: test_rule}:
model_id: base.model_res_partner
domain_force: "[(1,'=',1)]"
-
Read as demo user the partners (one 1=1 domain).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Domain is an empty list.
-
!record {model: ir.rule, id: test_rule}:
model_id: base.model_res_partner
domain_force: "[]"
-
Read as demo user the partners (one [] domain).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Create another ir_rule for the Employee group (to test rules from
multiple groups).
-
!record {model: ir.rule, id: test_rule2}:
model_id: base.model_res_partner
domain_force: False
name: test_rule2
groups:
- base.group_user
perm_unlink: 1
perm_write: 1
perm_read: 1
perm_create: 1
-
Read as demo user the partners (blank and [] domains).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Domain is not empty.
-
!record {model: ir.rule, id: test_rule}:
model_id: base.model_res_partner
domain_force: "[(1,'=',1)]"
-
Read as demo user the partners (1=1 and blank domain).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Domain is not empty.
-
!record {model: ir.rule, id: test_rule2}:
model_id: base.model_res_partner
domain_force: "[(1,'=',1)]"
-
Read as demo user the partners (two 1=1 domains).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Create another ir_rule for the Employee group (to test rules from
multiple groups).
-
!record {model: ir.rule, id: test_rule3}:
model_id: base.model_res_partner
domain_force: False
name: test_rule3
groups:
- base.group_user
perm_unlink: 1
perm_write: 1
perm_read: 1
perm_create: 1
-
Read as demo user the partners.
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Domain is not empty.
-
!record {model: ir.rule, id: test_rule3}:
model_id: base.model_res_partner
domain_force: "[(1,'=',1)]"
-
Read as demo user the partners (three 1=1 domains).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Modify the global rule on res_company which triggers a recursive check
of the rules on company.
-
!record {model: ir.rule, id: res_company_rule_employee}:
domain_force: "[('id','child_of',[user.company_id.id])]"
-
Read as demo user the partners (exercising the global company rule).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Modify the ir_rule for employee to have a rule that fordids
seeing any record.
We use a domain with implicit AND operator for later tests
on normalization.
-
!record {model: ir.rule, id: test_rule2}:
domain_force: "[('id','=',False),('name','=',False)]"
-
Check that demo user still sees partners, because group-rules are OR'ed.
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
-
Create a new group with demo user in it, and a complex rule.
Demo should still see partners.
-
!record {model: res.groups, id: test_group}:
name: Test Group
users:
- base.user_demo
-
Add the rule to the new group, with a domain containing an implicit AND operator,
which is more tricky because it will have to be normalized before combining it.
-
!record {model: ir.rule, id: test_rule3}:
model_id: base.model_res_partner
domain_force: "[('name', '!=', False),('id', '!=', False)]"
name: test_rule4
groups:
- test_group
perm_unlink: 1
perm_write: 1
perm_read: 1
perm_create: 1
-
Read the partners again as demo user, which should give results.
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see partners even with the combined rules."
-
Delete global domains (to combine only group domains).
-
!python {model: ir.rule}: |
ids = self.search(cr, uid, [('groups','=',False)])
assert ids, "Demo user should see some partner."
self.unlink(cr, uid, ids)
-
Read as demo user the partners (several group domains, no global domain).
-
!python {model: res.partner }: |
ids = self.search(cr, ref('base.user_demo'), [])
assert ids, "Demo user should see some partner."
+128 -148
View File
@@ -1,193 +1,177 @@
# -*- coding: utf-8 -*-
# Run with one of these commands:
# > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
# OPENERP_DATABASE=yy PYTHONPATH=. python tests/test_ir_sequence.py
# > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
# OPENERP_DATABASE=yy nosetests tests/test_ir_sequence.py
# > OPENERP_ADDONS_PATH='../../../addons/trunk' OPENERP_PORT=8069 \
# OPENERP_DATABASE=yy PYTHONPATH=../:. unit2 test_ir_sequence
# This assume an existing database.
import psycopg2
import psycopg2.errorcodes
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from contextlib import contextmanager
import unittest
import openerp
from openerp.tests import common
import psycopg2
import psycopg2.errorcodes
import odoo
from odoo.tests import common
ADMIN_USER_ID = common.ADMIN_USER_ID
def registry(model):
return openerp.modules.registry.RegistryManager.get(common.get_db_name())[model]
def cursor():
return openerp.modules.registry.RegistryManager.get(common.get_db_name()).cursor()
@contextmanager
def environment():
""" Return an environment with a new cursor for the current database; the
cursor is committed and closed after the context block.
"""
registry = odoo.registry(common.get_db_name())
with registry.cursor() as cr:
yield odoo.api.Environment(cr, ADMIN_USER_ID, {})
cr.commit()
def drop_sequence(code):
cr = cursor()
s = registry('ir.sequence')
ids = s.search(cr, ADMIN_USER_ID, [('code', '=', code)])
s.unlink(cr, ADMIN_USER_ID, ids)
cr.commit()
cr.close()
with environment() as env:
seq = env['ir.sequence'].search([('code', '=', code)])
seq.unlink()
class test_ir_sequence_standard(unittest.TestCase):
class TestIrSequenceStandard(unittest.TestCase):
""" A few tests for a 'Standard' (i.e. PostgreSQL) sequence. """
def test_ir_sequence_create(self):
""" Try to create a sequence object. """
cr = cursor()
d = dict(code='test_sequence_type', name='Test sequence')
c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
assert c
cr.commit()
cr.close()
with environment() as env:
seq = env['ir.sequence'].create({
'code': 'test_sequence_type',
'name': 'Test sequence',
})
self.assertTrue(seq)
def test_ir_sequence_search(self):
""" Try a search. """
cr = cursor()
ids = registry('ir.sequence').search(cr, ADMIN_USER_ID, [], {})
assert ids
cr.commit()
cr.close()
with environment() as env:
seqs = env['ir.sequence'].search([])
self.assertTrue(seqs)
def test_ir_sequence_draw(self):
""" Try to draw a number. """
cr = cursor()
n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type', {})
assert n
cr.commit()
cr.close()
with environment() as env:
n = env['ir.sequence'].next_by_code('test_sequence_type')
self.assertTrue(n)
def test_ir_sequence_draw_twice(self):
""" Try to draw a number from two transactions. """
cr0 = cursor()
cr1 = cursor()
n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type', {})
assert n0
n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type', {})
assert n1
cr0.commit()
cr1.commit()
cr0.close()
cr1.close()
with environment() as env0:
with environment() as env1:
n0 = env0['ir.sequence'].next_by_code('test_sequence_type')
self.assertTrue(n0)
n1 = env1['ir.sequence'].next_by_code('test_sequence_type')
self.assertTrue(n1)
@classmethod
def tearDownClass(cls):
drop_sequence('test_sequence_type')
class test_ir_sequence_no_gap(unittest.TestCase):
class TestIrSequenceNoGap(unittest.TestCase):
""" Copy of the previous tests for a 'No gap' sequence. """
def test_ir_sequence_create_no_gap(self):
""" Try to create a sequence object. """
cr = cursor()
d = dict(code='test_sequence_type_2', name='Test sequence',
implementation='no_gap')
c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
assert c
cr.commit()
cr.close()
with environment() as env:
seq = env['ir.sequence'].create({
'code': 'test_sequence_type_2',
'name': 'Test sequence',
'implementation': 'no_gap',
})
self.assertTrue(seq)
def test_ir_sequence_draw_no_gap(self):
""" Try to draw a number. """
cr = cursor()
n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_2', {})
assert n
cr.commit()
cr.close()
with environment() as env:
n = env['ir.sequence'].next_by_code('test_sequence_type_2')
self.assertTrue(n)
def test_ir_sequence_draw_twice_no_gap(self):
""" Try to draw a number from two transactions.
This is expected to not work.
"""
cr0 = cursor()
cr1 = cursor()
cr1._default_log_exceptions = False # Prevent logging a traceback
with self.assertRaises(psycopg2.OperationalError) as e:
n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type_2', {})
assert n0
n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type_2', {})
self.assertEqual(e.exception.pgcode, psycopg2.errorcodes.LOCK_NOT_AVAILABLE, msg="postgresql returned an incorrect errcode")
cr0.close()
cr1.close()
with environment() as env0:
with environment() as env1:
env1.cr._default_log_exceptions = False # Prevent logging a traceback
with self.assertRaises(psycopg2.OperationalError) as e:
n0 = env0['ir.sequence'].next_by_code('test_sequence_type_2')
self.assertTrue(n0)
n1 = env1['ir.sequence'].next_by_code('test_sequence_type_2')
self.assertEqual(e.exception.pgcode, psycopg2.errorcodes.LOCK_NOT_AVAILABLE, msg="postgresql returned an incorrect errcode")
@classmethod
def tearDownClass(cls):
drop_sequence('test_sequence_type_2')
class test_ir_sequence_change_implementation(unittest.TestCase):
class TestIrSequenceChangeImplementation(unittest.TestCase):
""" Create sequence objects and change their ``implementation`` field. """
def test_ir_sequence_1_create(self):
""" Try to create a sequence object. """
cr = cursor()
d = dict(code='test_sequence_type_3', name='Test sequence')
c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
assert c
d = dict(code='test_sequence_type_4', name='Test sequence',
implementation='no_gap')
c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
assert c
cr.commit()
cr.close()
with environment() as env:
seq = env['ir.sequence'].create({
'code': 'test_sequence_type_3',
'name': 'Test sequence',
})
self.assertTrue(seq)
seq = env['ir.sequence'].create({
'code': 'test_sequence_type_4',
'name': 'Test sequence',
'implementation': 'no_gap',
})
self.assertTrue(seq)
def test_ir_sequence_2_write(self):
cr = cursor()
ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
[('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
{'implementation': 'standard'}, {})
registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
{'implementation': 'no_gap'}, {})
cr.commit()
cr.close()
with environment() as env:
domain = [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])]
seqs = env['ir.sequence'].search(domain)
seqs.write({'implementation': 'standard'})
seqs.write({'implementation': 'no_gap'})
def test_ir_sequence_3_unlink(self):
cr = cursor()
ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
[('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
registry('ir.sequence').unlink(cr, ADMIN_USER_ID, ids, {})
cr.commit()
cr.close()
with environment() as env:
domain = [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])]
seqs = env['ir.sequence'].search(domain)
seqs.unlink()
@classmethod
def tearDownClass(cls):
drop_sequence('test_sequence_type_3')
drop_sequence('test_sequence_type_4')
class test_ir_sequence_generate(unittest.TestCase):
class TestIrSequenceGenerate(unittest.TestCase):
""" Create sequence objects and generate some values. """
def test_ir_sequence_create(self):
""" Try to create a sequence object. """
cr = cursor()
d = dict(code='test_sequence_type_5', name='Test sequence')
c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
assert c
cr.commit()
cr.close()
with environment() as env:
seq = env['ir.sequence'].create({
'code': 'test_sequence_type_5',
'name': 'Test sequence',
})
self.assertTrue(seq)
cr = cursor()
f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_5', {})
assert all(str(x) == f() for x in xrange(1,10))
cr.commit()
cr.close()
with environment() as env:
for i in xrange(1, 10):
n = env['ir.sequence'].next_by_code('test_sequence_type_5')
self.assertEqual(n, str(i))
def test_ir_sequence_create_no_gap(self):
""" Try to create a sequence object. """
cr = cursor()
d = dict(code='test_sequence_type_6', name='Test sequence', implementation='no_gap')
c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
assert c
cr.commit()
cr.close()
with environment() as env:
seq = env['ir.sequence'].create({
'code': 'test_sequence_type_6',
'name': 'Test sequence',
'implementation': 'no_gap',
})
self.assertTrue(seq)
cr = cursor()
f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_6', {})
assert all(str(x) == f() for x in xrange(1,10))
cr.commit()
cr.close()
with environment() as env:
for i in xrange(1, 10):
n = env['ir.sequence'].next_by_code('test_sequence_type_6')
self.assertEqual(n, str(i))
@classmethod
def tearDownClass(cls):
@@ -195,33 +179,29 @@ class test_ir_sequence_generate(unittest.TestCase):
drop_sequence('test_sequence_type_6')
class Test_ir_sequence_init(common.TransactionCase):
class TestIrSequenceInit(common.TransactionCase):
def test_00(self):
registry, cr, uid = self.registry, self.cr, self.uid
# test if read statement return the good number_next value (from postgreSQL sequence and not ir_sequence value)
sequence = registry('ir.sequence')
""" test whether the read method returns the right number_next value
(from postgreSQL sequence and not ir_sequence value)
"""
# first creation of sequence (normal)
values = {'number_next': 1,
'company_id': 1,
'padding': 4,
'number_increment': 1,
'implementation': 'standard',
'name': 'test-sequence-00'}
seq_id = sequence.create(cr, uid, values)
# Call get next 4 times
sequence.next_by_id(cr, uid, seq_id)
sequence.next_by_id(cr, uid, seq_id)
sequence.next_by_id(cr, uid, seq_id)
read_sequence = sequence.next_by_id(cr, uid, seq_id)
seq = self.env['ir.sequence'].create({
'number_next': 1,
'company_id': 1,
'padding': 4,
'number_increment': 1,
'implementation': 'standard',
'name': 'test-sequence-00',
})
# Call next() 4 times, and check the last returned value
seq.next_by_id()
seq.next_by_id()
seq.next_by_id()
n = seq.next_by_id()
self.assertEqual(n, "0004", 'The actual sequence value must be 4. reading : %s' % n)
# reset sequence to 1 by write()
seq.write({'number_next': 1})
# Read the value of the current sequence
assert read_sequence == "0004", 'The actual sequence value must be 4. reading : %s' % read_sequence
# reset sequence to 1 by write method calling
sequence.write(cr, uid, [seq_id], {'number_next': 1})
# Read the value of the current sequence
read_sequence = sequence.next_by_id(cr, uid, seq_id)
assert read_sequence == "0001", 'The actual sequence value must be 1. reading : %s' % read_sequence
if __name__ == "__main__":
unittest.main()
n = seq.next_by_id()
self.assertEqual(n, "0001", 'The actual sequence value must be 1. reading : %s' % n)
@@ -1,122 +1,142 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.tests import common
from datetime import date
from odoo.tests.common import SingleTransactionCase
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
class test_ir_sequence_date_range_standard(common.SingleTransactionCase):
class TestIrSequenceDateRangeStandard(SingleTransactionCase):
""" A few tests for a 'Standard' (i.e. PostgreSQL) sequence. """
def test_ir_sequence_date_range_1_create(self):
""" Try to create a sequence object with date ranges enabled. """
d = dict(code='test_sequence_date_range', name='Test sequence', use_date_range=True)
c = self.registry('ir.sequence').create(self.cr, self.uid, d, {})
assert c
seq = self.env['ir.sequence'].create({
'code': 'test_sequence_date_range',
'name': 'Test sequence',
'use_date_range': True,
})
self.assertTrue(seq)
def test_ir_sequence_date_range_2_change_dates(self):
""" Draw numbers to create a first subsequence then change its date range. Then, try to draw a new number adn check a new subsequence was correctly created. """
year = str(int(date.today().strftime("%Y")) - 1)
dt = "{}-01-16".format(year)
n = self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range', {'ir_sequence_date': dt})
year = date.today().year - 1
january = lambda d: date(year, 1, d).strftime(DATE_FORMAT)
seq16 = self.env['ir.sequence'].with_context({'ir_sequence_date': january(16)})
n = seq16.next_by_code('test_sequence_date_range')
self.assertEqual(n, '1')
n = self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range', {'ir_sequence_date': dt})
n = seq16.next_by_code('test_sequence_date_range')
self.assertEqual(n, '2')
# modify the range of date created
domain = [('sequence_id.code', '=', 'test_sequence_date_range'), ('date_from', '=', '{}-01-01'.format(year))]
seq_date_range_id = self.registry('ir.sequence.date_range').search(self.cr, self.uid, domain, {})
self.registry('ir.sequence.date_range').write(self.cr, self.uid, seq_date_range_id, {'date_from': '{}-01-18'.format(year)}, {})
n = self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range', {'ir_sequence_date': dt})
domain = [('sequence_id.code', '=', 'test_sequence_date_range'), ('date_from', '=', january(1))]
seq_date_range = self.env['ir.sequence.date_range'].search(domain)
seq_date_range.write({'date_from': january(18)})
n = seq16.next_by_code('test_sequence_date_range')
self.assertEqual(n, '1')
# check the newly created sequence stops at the 17th of January
domain = [('sequence_id.code', '=', 'test_sequence_date_range'), ('date_from', '=', '{}-01-01'.format(year))]
seq_date_range_id = self.registry('ir.sequence.date_range').search(self.cr, self.uid, domain, {})
seq_date_range = self.registry('ir.sequence.date_range').browse(self.cr, self.uid, seq_date_range_id[0])
self.assertEqual(seq_date_range.date_to, '{}-01-17'.format(year))
domain = [('sequence_id.code', '=', 'test_sequence_date_range'), ('date_from', '=', january(1))]
seq_date_range = self.env['ir.sequence.date_range'].search(domain)
self.assertEqual(seq_date_range.date_to, january(17))
def test_ir_sequence_date_range_3_unlink(self):
ids = self.registry('ir.sequence').search(self.cr, self.uid, [('code', '=', 'test_sequence_date_range')])
self.registry('ir.sequence').unlink(self.cr, self.uid, ids)
seq = self.env['ir.sequence'].search([('code', '=', 'test_sequence_date_range')])
seq.unlink()
class test_ir_sequence_date_range_no_gap(common.SingleTransactionCase):
class TestIrSequenceDateRangeNoGap(SingleTransactionCase):
""" Copy of the previous tests for a 'No gap' sequence. """
def test_ir_sequence_date_range_1_create_no_gap(self):
""" Try to create a sequence object. """
d = dict(code='test_sequence_date_range_2', name='Test sequence',
implementation='no_gap', use_date_range=True)
c = self.registry('ir.sequence').create(self.cr, self.uid, d, {})
assert c
seq = self.env['ir.sequence'].create({
'code': 'test_sequence_date_range_2',
'name': 'Test sequence',
'use_date_range': True,
'implementation': 'no_gap',
})
self.assertTrue(seq)
def test_ir_sequence_date_range_2_change_dates(self):
""" Draw numbers to create a first subsequence then change its date range. Then, try to draw a new number adn check a new subsequence was correctly created. """
year = str(int(date.today().strftime("%Y")) - 1)
dt = "{}-01-16".format(year)
n = self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_2', {'ir_sequence_date': dt})
year = date.today().year - 1
january = lambda d: date(year, 1, d).strftime(DATE_FORMAT)
seq16 = self.env['ir.sequence'].with_context({'ir_sequence_date': january(16)})
n = seq16.next_by_code('test_sequence_date_range_2')
self.assertEqual(n, '1')
n = self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_2', {'ir_sequence_date': dt})
n = seq16.next_by_code('test_sequence_date_range_2')
self.assertEqual(n, '2')
# modify the range of date created
domain = [('sequence_id.code', '=', 'test_sequence_date_range_2'), ('date_from', '=', '{}-01-01'.format(year))]
seq_date_range_id = self.registry('ir.sequence.date_range').search(self.cr, self.uid, domain, {})
self.registry('ir.sequence.date_range').write(self.cr, self.uid, seq_date_range_id, {'date_from': '{}-01-18'.format(year)}, {})
n = self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_2', {'ir_sequence_date': dt})
domain = [('sequence_id.code', '=', 'test_sequence_date_range_2'), ('date_from', '=', january(1))]
seq_date_range = self.env['ir.sequence.date_range'].search(domain)
seq_date_range.write({'date_from': january(18)})
n = seq16.next_by_code('test_sequence_date_range_2')
self.assertEqual(n, '1')
# check the newly created sequence stops at the 17th of January
domain = [('sequence_id.code', '=', 'test_sequence_date_range_2'), ('date_from', '=', '{}-01-01'.format(year))]
seq_date_range_id = self.registry('ir.sequence.date_range').search(self.cr, self.uid, domain, {})
seq_date_range = self.registry('ir.sequence.date_range').browse(self.cr, self.uid, seq_date_range_id[0])
self.assertEqual(seq_date_range.date_to, '{}-01-17'.format(year))
domain = [('sequence_id.code', '=', 'test_sequence_date_range_2'), ('date_from', '=', january(1))]
seq_date_range = self.env['ir.sequence.date_range'].search(domain)
self.assertEqual(seq_date_range.date_to, january(17))
def test_ir_sequence_date_range_3_unlink(self):
ids = self.registry('ir.sequence').search(self.cr, self.uid, [('code', '=', 'test_sequence_date_range_2')])
self.registry('ir.sequence').unlink(self.cr, self.uid, ids)
seq = self.env['ir.sequence'].search([('code', '=', 'test_sequence_date_range_2')])
seq.unlink()
class test_ir_sequence_date_range_change_implementation(common.SingleTransactionCase):
class TestIrSequenceDateRangeChangeImplementation(SingleTransactionCase):
""" Create sequence objects and change their ``implementation`` field. """
def test_ir_sequence_date_range_1_create(self):
""" Try to create a sequence object. """
d = dict(code='test_sequence_date_range_3', name='Test sequence', use_date_range=True)
c = self.registry('ir.sequence').create(self.cr, self.uid, d, {})
assert c
d = {
seq = self.env['ir.sequence'].create({
'code': 'test_sequence_date_range_3',
'name': 'Test sequence',
'use_date_range': True,
})
self.assertTrue(seq)
seq = self.env['ir.sequence'].create({
'code': 'test_sequence_date_range_4',
'name': 'Test sequence',
'use_date_range': True,
'implementation': 'no_gap',
'use_date_range': True
}
c = self.registry('ir.sequence').create(self.cr, self.uid, d, {})
assert c
})
self.assertTrue(seq)
def test_ir_sequence_date_range_2_use(self):
""" Make some use of the sequences to create some subsequences """
year = str(int(date.today().strftime("%Y")) - 1)
dt = "{}-01-16".format(year)
f = lambda *a: self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_3', {})
assert all(str(x) == f() for x in xrange(1, 5))
f = lambda *a: self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_3', {'ir_sequence_date': dt})
assert all(str(x) == f() for x in xrange(1, 5))
f = lambda *a: self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_4', {})
assert all(str(x) == f() for x in xrange(1, 5))
f = lambda *a: self.registry('ir.sequence').next_by_code(self.cr, self.uid, 'test_sequence_date_range_4', {'ir_sequence_date': dt})
assert all(str(x) == f() for x in xrange(1, 5))
year = date.today().year - 1
january = lambda d: date(year, 1, d).strftime(DATE_FORMAT)
seq = self.env['ir.sequence']
seq16 = self.env['ir.sequence'].with_context({'ir_sequence_date': january(16)})
for i in xrange(1, 5):
n = seq.next_by_code('test_sequence_date_range_3')
self.assertEqual(n, str(i))
for i in xrange(1, 5):
n = seq16.next_by_code('test_sequence_date_range_3')
self.assertEqual(n, str(i))
for i in xrange(1, 5):
n = seq.next_by_code('test_sequence_date_range_4')
self.assertEqual(n, str(i))
for i in xrange(1, 5):
n = seq16.next_by_code('test_sequence_date_range_4')
self.assertEqual(n, str(i))
def test_ir_sequence_date_range_3_write(self):
"""swap the implementation method on both"""
ids = self.registry('ir.sequence').search(self.cr, self.uid, [
('code', 'in', ['test_sequence_date_range_3', 'test_sequence_date_range_4'])
], {})
self.registry('ir.sequence').write(self.cr, self.uid, ids, {'implementation': 'standard'})
self.registry('ir.sequence').write(self.cr, self.uid, ids, {'implementation': 'no_gap'})
domain = [('code', 'in', ['test_sequence_date_range_3', 'test_sequence_date_range_4'])]
seqs = self.env['ir.sequence'].search(domain)
seqs.write({'implementation': 'standard'})
seqs.write({'implementation': 'no_gap'})
def test_ir_sequence_date_range_4_unlink(self):
ids = self.registry('ir.sequence').search(self.cr, self.uid, [
('code', 'in', ['test_sequence_date_range_3', 'test_sequence_date_range_4'])
], {})
self.registry('ir.sequence').unlink(self.cr, self.uid, ids, {})
if __name__ == '__main__':
unittest.main()
domain = [('code', 'in', ['test_sequence_date_range_3', 'test_sequence_date_range_4'])]
seqs = self.env['ir.sequence'].search(domain)
seqs.unlink()
+63 -62
View File
@@ -1,98 +1,99 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp.tests.common as common
from odoo.tests.common import TransactionCase
class test_ir_values(common.TransactionCase):
class TestIrValues(TransactionCase):
def test_00(self):
# Create some default value for some (non-existing) model, for all users.
ir_values = self.registry('ir.values')
ir_values = self.env['ir.values']
# use the old API
ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',
['unexisting_model'], 'global value')
ir_values.set('default', False, 'my_test_field', ['unexisting_model'],
'global value')
# use the new API
ir_values.set_default(self.cr, self.uid, 'other_unexisting_model',
'my_other_test_field', 'conditional value', condition='foo=bar')
ir_values.set_default('other_unexisting_model', 'my_other_test_field',
'conditional value', condition='foo=bar')
# Retrieve them.
ir_values = self.registry('ir.values')
# d is a list of triplets (id, name, value)
# Old API
d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model'])
assert len(d) == 1, "Only one single value should be retrieved for this model"
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)"
assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)"
d = ir_values.get('default', False, ['unexisting_model'])
self.assertEqual(len(d), 1, "Only one single value should be retrieved for this model")
self.assertEqual(d[0][1], 'my_test_field', "Can't retrieve the created default value. (1)")
self.assertEqual(d[0][2], 'global value', "Can't retrieve the created default value. (2)")
# New API, Conditional version
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model')
assert len(d) == 0, "No value should be retrieved, the condition is not met"
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=eggs")
assert len(d) == 0, 'Condition is not met either, no defaults should be returned'
d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=bar")
assert len(d) == 1, "Only one single value should be retrieved"
assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)"
assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)"
d = ir_values.get_defaults('other_unexisting_model')
self.assertEqual(len(d), 0, "No value should be retrieved, the condition is not met")
d = ir_values.get_defaults('other_unexisting_model', condition="foo=eggs")
self.assertEqual(len(d), 0, 'Condition is not met either, no defaults should be returned')
d = ir_values.get_defaults('other_unexisting_model', condition="foo=bar")
self.assertEqual(len(d), 1, "Only one single value should be retrieved")
self.assertEqual(d[0][1], 'my_other_test_field', "Can't retrieve the created default value. (5)")
self.assertEqual(d[0][2], 'conditional value', "Can't retrieve the created default value. (6)")
# Do it again but for a specific user.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True)
ir_values.set('default', False, 'my_test_field',['unexisting_model'],
'specific value', preserve_user=True)
# Retrieve it and check it is the one for the current user.
ir_values = self.registry('ir.values')
d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model'])
assert len(d) == 1, "Only one default must be returned per field"
assert d[0][1] == 'my_test_field', "Can't retrieve the created default value."
assert d[0][2] == 'specific value', "Can't retrieve the created default value."
d = ir_values.get('default', False, ['unexisting_model'])
self.assertEqual(len(d), 1, "Only one default must be returned per field")
self.assertEqual(d[0][1], 'my_test_field', "Can't retrieve the created default value.")
self.assertEqual(d[0][2], 'specific value', "Can't retrieve the created default value.")
# Create some action bindings for a non-existing model.
act_id_1 = self.ref('base.act_values_form_action')
act_id_2 = self.ref('base.act_values_form_defaults')
act_id_3 = self.ref('base.action_res_company_form')
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_1, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_2, isobject=True)
ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_3, isobject=True)
report_ids = self.registry('ir.actions.report.xml').search(self.cr, self.uid, [], {})
reports = self.registry('ir.actions.report.xml').browse(self.cr, self.uid, report_ids, {})
report_id = [report.id for report in reports if not report.groups_id][0] # assume at least one
ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,%d' % report_id, isobject=True)
ir_values.set('action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'],
'ir.actions.act_window,%d' % act_id_1, isobject=True)
ir_values.set('action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'],
'ir.actions.act_window,%d' % act_id_2, isobject=True)
ir_values.set('action', 'client_action_multi', 'Side Wizard', ['unexisting_model'],
'ir.actions.act_window,%d' % act_id_3, isobject=True)
reports = self.env['ir.actions.report.xml'].search([])
report_id = next(report.id for report in reports if not report.groups_id)
ir_values.set('action', 'client_print_multi', 'Nice Report', ['unexisting_model'],
'ir.actions.report.xml,%d' % report_id, isobject=True)
# Replace one action binding to set a new name.
ir_values = self.registry('ir.values')
ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,%d' % act_id_1, isobject=True)
ir_values.set('action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'],
'ir.actions.act_window,%d' % act_id_1, isobject=True)
# Retrieve the action bindings and check they're correct
ir_values = self.registry('ir.values')
actions = ir_values.get(self.cr, self.uid, 'action', 'tree_but_open', ['unexisting_model'])
assert len(actions) == 2, "Mismatching number of bound actions"
actions = ir_values.get('action', 'tree_but_open', ['unexisting_model'])
self.assertEqual(len(actions), 2, "Mismatching number of bound actions")
#first action
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_2, 'Bound action does not match definition'
self.assertEqual(len(actions[0]), 3, "Malformed action definition")
self.assertEqual(actions[0][1], 'OnDblClick Action 2', 'Bound action does not match definition')
self.assertTrue(isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_2,
'Bound action does not match definition')
#second action - this ones comes last because it was re-created with a different name
assert len(actions[1]) == 3, "Malformed action definition"
assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it'
assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == act_id_1, 'Bound action does not match definition'
self.assertEqual(len(actions[1]), 3, "Malformed action definition")
self.assertEqual(actions[1][1], 'OnDblClick Action New', 'Re-Registering an action should replace it')
self.assertTrue(isinstance(actions[1][2], dict) and actions[1][2]['id'] == act_id_1,
'Bound action does not match definition')
actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_3, 'Bound action does not match definition'
actions = ir_values.get('action', 'client_action_multi', ['unexisting_model'])
self.assertEqual(len(actions), 1, "Mismatching number of bound actions")
self.assertEqual(len(actions[0]), 3, "Malformed action definition")
self.assertEqual(actions[0][1], 'Side Wizard', 'Bound action does not match definition')
self.assertTrue(isinstance(actions[0][2], dict) and actions[0][2]['id'] == act_id_3,
'Bound action does not match definition')
actions = ir_values.get(self.cr, self.uid, 'action', 'client_print_multi', ['unexisting_model'])
assert len(actions) == 1, "Mismatching number of bound actions"
assert len(actions[0]) == 3, "Malformed action definition"
assert actions[0][1] == 'Nice Report', 'Bound action does not match definition'
assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == report_id, 'Bound action does not match definition'
if __name__ == '__main__':
unittest.main()
actions = ir_values.get('action', 'client_print_multi', ['unexisting_model'])
self.assertEqual(len(actions), 1, "Mismatching number of bound actions")
self.assertEqual(len(actions[0]), 3, "Malformed action definition")
self.assertEqual(actions[0][1], 'Nice Report', 'Bound action does not match definition')
self.assertTrue(isinstance(actions[0][2], dict) and actions[0][2]['id'] == report_id,
'Bound action does not match definition')
+2 -9
View File
@@ -1,14 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This test can be run stand-alone with something like:
# > PYTHONPATH=. python2 openerp/tests/test_misc.py
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import cgi
import unittest
from openerp.tools import html_sanitize, append_content_to_html, plaintext2html, email_split
import test_mail_examples
from odoo.tools import html_sanitize, append_content_to_html, plaintext2html, email_split
from . import test_mail_examples
class TestSanitizer(unittest.TestCase):
@@ -325,7 +322,3 @@ class TestEmailTools(unittest.TestCase):
]
for text, expected in cases:
self.assertEqual(email_split(text), expected, 'email_split is broken')
if __name__ == '__main__':
unittest.main()
@@ -1,5 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
MISC_HTML_SOURCE = """
<font size="2" style="color: rgb(31, 31, 31); font-family: monospace; font-variant: normal; line-height: normal; ">test1</font>
@@ -515,4 +515,4 @@ REMOVE_CLASS_IN = [
REMOVE_CLASS_OUT = [
'<div class="modal-backdrop in">',
'<div class="modal-content openerp">',
'<div class="modal-header">']
'<div class="modal-header">']
+19 -21
View File
@@ -1,32 +1,30 @@
import openerp.tests.common as common
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
class test_menu(common.TransactionCase):
from odoo.tests.common import TransactionCase
def setUp(self):
super(test_menu,self).setUp()
self.Menus = self.registry('ir.ui.menu')
class TestMenu(TransactionCase):
def test_00_menu_deletion(self):
"""Verify that menu deletion works properly when there are child menus, and those
are indeed made orphans"""
cr, uid, Menus = self.cr, self.uid, self.Menus
# Generic trick necessary for search() calls to avoid hidden menus
ctx = {'ir.ui.menu.full_list': True}
root_id = Menus.create(cr, uid, {'name': 'Test root'})
child1_id = Menus.create(cr, uid, {'name': 'Test child 1', 'parent_id': root_id})
child2_id = Menus.create(cr, uid, {'name': 'Test child 2', 'parent_id': root_id})
child21_id = Menus.create(cr, uid, {'name': 'Test child 2-1', 'parent_id': child2_id})
all_ids = [root_id, child1_id, child2_id, child21_id]
Menu = self.env['ir.ui.menu']
root = Menu.create({'name': 'Test root'})
child1 = Menu.create({'name': 'Test child 1', 'parent_id': root.id})
child2 = Menu.create({'name': 'Test child 2', 'parent_id': root.id})
child21 = Menu.create({'name': 'Test child 2-1', 'parent_id': child2.id})
all_ids = [root.id, child1.id, child2.id, child21.id]
# delete and check that direct children are promoted to top-level
# cfr. explanation in menu.unlink()
Menus.unlink(cr, uid, [root_id])
root.unlink()
remaining_ids = Menus.search(cr, uid, [('id', 'in', all_ids)], order="id", context=ctx)
self.assertEqual([child1_id, child2_id, child21_id], remaining_ids)
# Generic trick necessary for search() calls to avoid hidden menus
Menu = self.env['ir.ui.menu'].with_context({'ir.ui.menu.full_list': True})
orphan_ids = Menus.search(cr, uid, [('id', 'in', all_ids), ('parent_id', '=', False)], order="id", context=ctx)
self.assertEqual([child1_id, child2_id], orphan_ids)
remaining = Menu.search([('id', 'in', all_ids)], order="id")
self.assertEqual([child1.id, child2.id, child21.id], remaining.ids)
orphans = Menu.search([('id', 'in', all_ids), ('parent_id', '=', False)], order="id")
self.assertEqual([child1.id, child2.id], orphans.ids)
+5 -5
View File
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
from openerp.tools import misc
from odoo.tools import misc
class test_countingstream(unittest.TestCase):
class TestCountingStream(unittest.TestCase):
def test_empty_stream(self):
s = misc.CountingStream(iter([]))
self.assertEqual(s.index, -1)
@@ -32,6 +35,3 @@ class test_countingstream(unittest.TestCase):
self.assertEqual(s.index, 0)
self.assertIsNone(next(s, None))
self.assertEqual(s.index, 0)
if __name__ == '__main__':
unittest.main()
+197 -229
View File
@@ -1,117 +1,125 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from openerp.tools import mute_logger
from openerp.tests import common
UID = common.ADMIN_USER_ID
from odoo.exceptions import AccessError, MissingError
from odoo.tests.common import TransactionCase
from odoo.tools import mute_logger
class TestORM(common.TransactionCase):
""" test special behaviors of ORM CRUD functions
TODO: use real Exceptions types instead of Exception """
def setUp(self):
super(TestORM, self).setUp()
cr, uid = self.cr, self.uid
self.partner = self.registry('res.partner')
self.users = self.registry('res.users')
self.p1 = self.partner.name_create(cr, uid, 'W')[0]
self.p2 = self.partner.name_create(cr, uid, 'Y')[0]
self.ir_rule = self.registry('ir.rule')
# sample unprivileged user
employee_gid = self.ref('base.group_user')
self.uid2 = self.users.create(cr, uid, {'name': 'test user', 'login': 'test', 'groups_id': [4,employee_gid]})
class TestORM(TransactionCase):
""" test special behaviors of ORM CRUD functions """
@mute_logger('openerp.models')
def testAccessDeletedRecords(self):
def test_access_deleted_records(self):
""" Verify that accessing deleted records works as expected """
cr, uid, uid2, p1, p2 = self.cr, self.uid, self.uid2, self.p1, self.p2
self.partner.unlink(cr, uid, [p1])
p1 = self.env['res.partner'].create({'name': 'W'})
p2 = self.env['res.partner'].create({'name': 'Y'})
p1.unlink()
# read() is expected to skip deleted records because our API is not
# transactional for a sequence of search()->read() performed from the
# client-side... a concurrent deletion could therefore cause spurious
# exceptions even when simply opening a list view!
# /!\ Using unprileged user to detect former side effects of ir.rules!
self.assertEqual([{'id': p2, 'name': 'Y'}], self.partner.read(cr, uid2, [p1,p2], ['name']), "read() should skip deleted records")
self.assertEqual([], self.partner.read(cr, uid2, [p1], ['name']), "read() should skip deleted records")
user = self.env['res.users'].create({
'name': 'test user',
'login': 'test2',
'groups_id': [4, self.ref('base.group_user')],
})
ps = (p1 + p2).sudo(user)
self.assertEqual([{'id': p2.id, 'name': 'Y'}], ps.read(['name']), "read() should skip deleted records")
self.assertEqual([], ps[0].read(['name']), "read() should skip deleted records")
# Deleting an already deleted record should be simply ignored
self.assertTrue(self.partner.unlink(cr, uid, [p1]), "Re-deleting should be a no-op")
self.assertTrue(p1.unlink(), "Re-deleting should be a no-op")
# Updating an already deleted record should raise, even as admin
with self.assertRaises(Exception):
self.partner.write(cr, uid, [p1], {'name': 'foo'})
with self.assertRaises(MissingError):
p1.write({'name': 'foo'})
@mute_logger('openerp.models')
def testAccessFilteredRecords(self):
def test_access_filtered_records(self):
""" Verify that accessing filtered records works as expected for non-admin user """
cr, uid, uid2, p1, p2 = self.cr, self.uid, self.uid2, self.p1, self.p2
partner_model = self.registry('ir.model').search(cr, uid, [('model','=','res.partner')])[0]
self.ir_rule.create(cr, uid, {'name': 'Y is invisible',
'domain_force': [('id', '!=', p1)],
'model_id': partner_model})
p1 = self.env['res.partner'].create({'name': 'W'})
p2 = self.env['res.partner'].create({'name': 'Y'})
user = self.env['res.users'].create({
'name': 'test user',
'login': 'test2',
'groups_id': [4, self.ref('base.group_user')],
})
partner_model = self.env['ir.model'].search([('model','=','res.partner')])
self.env['ir.rule'].create({
'name': 'Y is invisible',
'domain_force': [('id', '!=', p1.id)],
'model_id': partner_model.id,
})
# search as unprivileged user
partners = self.partner.search(cr, uid2, [])
self.assertFalse(p1 in partners, "W should not be visible...")
self.assertTrue(p2 in partners, "... but Y should be visible")
partners = self.env['res.partner'].sudo(user).search([])
self.assertNotIn(p1, partners, "W should not be visible...")
self.assertIn(p2, partners, "... but Y should be visible")
# read as unprivileged user
with self.assertRaises(Exception):
self.partner.read(cr, uid2, [p1], ['name'])
with self.assertRaises(AccessError):
p1.sudo(user).read(['name'])
# write as unprivileged user
with self.assertRaises(Exception):
self.partner.write(cr, uid2, [p1], {'name': 'foo'})
with self.assertRaises(AccessError):
p1.sudo(user).write({'name': 'foo'})
# unlink as unprivileged user
with self.assertRaises(Exception):
self.partner.unlink(cr, uid2, [p1])
with self.assertRaises(AccessError):
p1.sudo(user).unlink()
# Prepare mixed case
self.partner.unlink(cr, uid, [p2])
p2.unlink()
# read mixed records: some deleted and some filtered
with self.assertRaises(Exception):
self.partner.read(cr, uid2, [p1,p2], ['name'])
with self.assertRaises(AccessError):
(p1 + p2).sudo(user).read(['name'])
# delete mixed records: some deleted and some filtered
with self.assertRaises(Exception):
self.partner.unlink(cr, uid2, [p1,p2])
with self.assertRaises(AccessError):
(p1 + p2).sudo(user).unlink()
def test_multi_read(self):
record_id = self.partner.create(self.cr, UID, {'name': 'MyPartner1'})
records = self.partner.read(self.cr, UID, [record_id])
self.assertIsInstance(records, list)
def test_read(self):
partner = self.env['res.partner'].create({'name': 'MyPartner1'})
result = partner.read()
self.assertIsInstance(result, list)
def test_one_read(self):
record_id = self.partner.create(self.cr, UID, {'name': 'MyPartner1'})
record = self.partner.read(self.cr, UID, record_id)
self.assertIsInstance(record, dict)
# special cases for the old API only
result = self.registry('res.partner').read(self.cr, self.uid, [partner.id])
self.assertIsInstance(result, list)
result = self.registry('res.partner').read(self.cr, self.uid, partner.id)
self.assertIsInstance(result, dict)
@mute_logger('openerp.models')
def test_search_read(self):
partner = self.env['res.partner']
# simple search_read
self.partner.create(self.cr, UID, {'name': 'MyPartner1'})
found = self.partner.search_read(self.cr, UID, [['name', '=', 'MyPartner1']], ['name'])
partner.create({'name': 'MyPartner1'})
found = partner.search_read([('name', '=', 'MyPartner1')], ['name'])
self.assertEqual(len(found), 1)
self.assertEqual(found[0]['name'], 'MyPartner1')
self.assertTrue('id' in found[0])
self.assertIn('id', found[0])
# search_read correct order
self.partner.create(self.cr, UID, {'name': 'MyPartner2'})
found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name")
partner.create({'name': 'MyPartner2'})
found = partner.search_read([('name', 'like', 'MyPartner')], ['name'], order="name")
self.assertEqual(len(found), 2)
self.assertEqual(found[0]['name'], 'MyPartner1')
self.assertEqual(found[1]['name'], 'MyPartner2')
found = self.partner.search_read(self.cr, UID, [['name', 'like', 'MyPartner']], ['name'], order="name desc")
found = partner.search_read([('name', 'like', 'MyPartner')], ['name'], order="name desc")
self.assertEqual(len(found), 2)
self.assertEqual(found[0]['name'], 'MyPartner2')
self.assertEqual(found[1]['name'], 'MyPartner1')
# search_read that finds nothing
found = self.partner.search_read(self.cr, UID, [['name', '=', 'Does not exists']], ['name'])
found = partner.search_read([('name', '=', 'Does not exists')], ['name'])
self.assertEqual(len(found), 0)
def test_exists(self):
partner = self.partner.browse(self.cr, UID, [])
partner = self.env['res.partner']
# check that records obtained from search exist
recs = partner.search([])
@@ -123,7 +131,7 @@ class TestORM(common.TransactionCase):
self.assertFalse(recs.exists())
def test_groupby_date(self):
partners = dict(
partners_data = dict(
A='2012-11-19',
B='2012-12-17',
C='2012-12-31',
@@ -133,42 +141,37 @@ class TestORM(common.TransactionCase):
G='2013-02-11',
)
all_partners = []
partners_by_day = defaultdict(set)
partners_by_month = defaultdict(set)
partners_by_year = defaultdict(set)
partner_ids = []
partner_ids_by_day = defaultdict(list)
partner_ids_by_month = defaultdict(list)
partner_ids_by_year = defaultdict(list)
for name, date in partners.items():
p = self.partner.create(self.cr, UID, dict(name=name, date=date))
all_partners.append(p)
partners_by_day[date].add(p)
partners_by_month[date.rsplit('-', 1)[0]].add(p)
partners_by_year[date.split('-', 1)[0]].add(p)
partners = self.env['res.partner']
for name, date in partners_data.items():
p = partners.create(dict(name=name, date=date))
partner_ids.append(p.id)
partner_ids_by_day[date].append(p.id)
partner_ids_by_month[date.rsplit('-', 1)[0]].append(p.id)
partner_ids_by_year[date.split('-', 1)[0]].append(p.id)
def read_group(interval, domain=None):
main_domain = [('id', 'in', all_partners)]
if domain:
domain = ['&'] + main_domain + domain
else:
domain = main_domain
rg = self.partner.read_group(self.cr, self.uid, domain, ['date'], 'date' + ':' + interval)
def read_group(interval):
domain = [('id', 'in', partner_ids)]
result = {}
for r in rg:
result[r['date:' + interval]] = set(self.partner.search(self.cr, self.uid, r['__domain']))
for grp in partners.read_group(domain, ['date'], ['date:' + interval]):
result[grp['date:' + interval]] = partners.search(grp['__domain'])
return result
self.assertEqual(len(read_group('day')), len(partners_by_day))
self.assertEqual(len(read_group('month')), len(partners_by_month))
self.assertEqual(len(read_group('year')), len(partners_by_year))
self.assertEqual(len(read_group('day')), len(partner_ids_by_day))
self.assertEqual(len(read_group('month')), len(partner_ids_by_month))
self.assertEqual(len(read_group('year')), len(partner_ids_by_year))
rg = self.partner.read_group(self.cr, self.uid, [('id', 'in', all_partners)],
['date'], ['date:month', 'date:day'], lazy=False)
self.assertEqual(len(rg), len(all_partners))
res = partners.read_group([('id', 'in', partner_ids)], ['date'],
['date:month', 'date:day'], lazy=False)
self.assertEqual(len(res), len(partner_ids))
def test_write_duplicate(self):
cr, uid, p1 = self.cr, self.uid, self.p1
self.partner.write(cr, uid, [p1, p1], {'name': 'X'})
p1 = self.env['res.partner'].create({'name': 'W'})
(p1 + p1).write({'name': 'X'})
def test_m2m_store_trigger(self):
group_user = self.env.ref('base.group_user')
@@ -187,110 +190,93 @@ class TestORM(common.TransactionCase):
self.assertTrue(user.share)
class TestInherits(common.TransactionCase):
class TestInherits(TransactionCase):
""" test the behavior of the orm for models that use _inherits;
specifically: res.users, that inherits from res.partner
"""
def setUp(self):
super(TestInherits, self).setUp()
self.partner = self.registry('res.partner')
self.user = self.registry('res.users')
def test_default(self):
""" `default_get` cannot return a dictionary or a new id """
defaults = self.user.default_get(self.cr, UID, ['partner_id'])
defaults = self.env['res.users'].default_get(['partner_id'])
if 'partner_id' in defaults:
self.assertIsInstance(defaults['partner_id'], (bool, int, long))
def test_create(self):
""" creating a user should automatically create a new partner """
partners_before = self.partner.search(self.cr, UID, [])
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
foo = self.user.browse(self.cr, UID, foo_id)
partners_before = self.env['res.partner'].search([])
user_foo = self.env['res.users'].create({'name': 'Foo', 'login': 'foo'})
self.assertNotIn(foo.partner_id.id, partners_before)
self.assertNotIn(user_foo.partner_id, partners_before)
def test_create_with_ancestor(self):
""" creating a user with a specific 'partner_id' should not create a new partner """
par_id = self.partner.create(self.cr, UID, {'name': 'Foo'})
partners_before = self.partner.search(self.cr, UID, [])
foo_id = self.user.create(self.cr, UID, {'partner_id': par_id, 'login': 'foo', 'password': 'foo'})
partners_after = self.partner.search(self.cr, UID, [])
partner_foo = self.env['res.partner'].create({'name': 'Foo'})
partners_before = self.env['res.partner'].search([])
user_foo = self.env['res.users'].create({'partner_id': partner_foo.id, 'login': 'foo'})
partners_after = self.env['res.partner'].search([])
self.assertEqual(set(partners_before), set(partners_after))
foo = self.user.browse(self.cr, UID, foo_id)
self.assertEqual(foo.name, 'Foo')
self.assertEqual(foo.partner_id.id, par_id)
self.assertEqual(partners_before, partners_after)
self.assertEqual(user_foo.name, 'Foo')
self.assertEqual(user_foo.partner_id, partner_foo)
@mute_logger('openerp.models')
def test_read(self):
""" inherited fields should be read without any indirection """
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo'})
foo_values, = self.user.read(self.cr, UID, [foo_id])
partner_id = foo_values['partner_id'][0]
partner_values, = self.partner.read(self.cr, UID, [partner_id])
self.assertEqual(foo_values['name'], partner_values['name'])
user_foo = self.env['res.users'].create({'name': 'Foo', 'login': 'foo'})
user_values, = user_foo.read()
partner_values, = user_foo.partner_id.read()
foo = self.user.browse(self.cr, UID, foo_id)
self.assertEqual(foo.name, foo.partner_id.name)
self.assertEqual(user_values['name'], partner_values['name'])
self.assertEqual(user_foo.name, user_foo.partner_id.name)
@mute_logger('openerp.models')
def test_copy(self):
""" copying a user should automatically copy its partner, too """
foo_id = self.user.create(self.cr, UID, {
user_foo = self.env['res.users'].create({
'name': 'Foo',
'login': 'foo',
'password': 'foo',
'supplier': True,
})
foo_before, = self.user.read(self.cr, UID, [foo_id])
foo_before, = user_foo.read()
del foo_before['__last_update']
bar_id = self.user.copy(self.cr, UID, foo_id, {
'login': 'bar',
'password': 'bar',
})
foo_after, = self.user.read(self.cr, UID, [foo_id])
user_bar = user_foo.copy({'login': 'bar'})
foo_after, = user_foo.read()
del foo_after['__last_update']
self.assertEqual(foo_before, foo_after)
foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
self.assertEqual(bar.name, 'Foo (copy)')
self.assertEqual(bar.login, 'bar')
self.assertEqual(foo.supplier, bar.supplier)
self.assertNotEqual(foo.id, bar.id)
self.assertNotEqual(foo.partner_id.id, bar.partner_id.id)
self.assertEqual(user_bar.name, 'Foo (copy)')
self.assertEqual(user_bar.login, 'bar')
self.assertEqual(user_foo.supplier, user_bar.supplier)
self.assertNotEqual(user_foo.id, user_bar.id)
self.assertNotEqual(user_foo.partner_id.id, user_bar.partner_id.id)
@mute_logger('openerp.models')
def test_copy_with_ancestor(self):
""" copying a user with 'parent_id' in defaults should not duplicate the partner """
foo_id = self.user.create(self.cr, UID, {'name': 'Foo', 'login': 'foo', 'password': 'foo',
user_foo = self.env['res.users'].create({'name': 'Foo', 'login': 'foo', 'password': 'foo',
'login_date': '2016-01-01', 'signature': 'XXX'})
par_id = self.partner.create(self.cr, UID, {'name': 'Bar'})
partner_bar = self.env['res.partner'].create({'name': 'Bar'})
foo_before, = self.user.read(self.cr, UID, [foo_id])
foo_before, = user_foo.read()
del foo_before['__last_update']
del foo_before['login_date']
partners_before = self.partner.search(self.cr, UID, [])
bar_id = self.user.copy(self.cr, UID, foo_id, {'partner_id': par_id, 'login': 'bar'})
foo_after, = self.user.read(self.cr, UID, [foo_id])
partners_before = self.env['res.partner'].search([])
user_bar = user_foo.copy({'partner_id': partner_bar.id, 'login': 'bar'})
foo_after, = user_foo.read()
del foo_after['__last_update']
del foo_after['login_date']
partners_after = self.partner.search(self.cr, UID, [])
partners_after = self.env['res.partner'].search([])
self.assertEqual(foo_before, foo_after)
self.assertEqual(set(partners_before), set(partners_after))
foo, bar = self.user.browse(self.cr, UID, [foo_id, bar_id])
self.assertNotEqual(foo.id, bar.id)
self.assertEqual(bar.partner_id.id, par_id)
self.assertEqual(bar.login, 'bar', "login is given from copy parameters")
self.assertFalse(bar.password, "password should not be copied from original record")
self.assertEqual(bar.name, 'Bar', "name is given from specific partner")
self.assertEqual(bar.signature, foo.signature, "signature should be copied")
self.assertEqual(partners_before, partners_after)
self.assertNotEqual(user_foo.id, user_bar.id)
self.assertEqual(user_bar.partner_id.id, partner_bar.id)
self.assertEqual(user_bar.login, 'bar', "login is given from copy parameters")
self.assertFalse(user_bar.password, "password should not be copied from original record")
self.assertEqual(user_bar.name, 'Bar', "name is given from specific partner")
self.assertEqual(user_bar.signature, user_foo.signature, "signature should be copied")
CREATE = lambda values: (0, False, values)
@@ -301,11 +287,8 @@ LINK_TO = lambda id: (4, id, False)
DELETE_ALL = lambda: (5, False, False)
REPLACE_WITH = lambda ids: (6, False, ids)
def sorted_by_id(list_of_dicts):
"sort dictionaries by their 'id' field; useful for comparisons"
return sorted(list_of_dicts, key=lambda d: d.get('id'))
class TestO2MSerialization(common.TransactionCase):
class TestO2MSerialization(TransactionCase):
""" test the orm method 'write' on one2many fields """
def setUp(self):
@@ -314,136 +297,121 @@ class TestO2MSerialization(common.TransactionCase):
def test_no_command(self):
" empty list of commands yields an empty list of records "
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', [])
results = self.env['res.partner'].resolve_2many_commands('child_ids', [])
self.assertEqual(results, [])
def test_CREATE_commands(self):
" returns the VALUES dict as-is "
values = [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}]
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', map(CREATE, values))
results = self.env['res.partner'].resolve_2many_commands('child_ids', map(CREATE, values))
self.assertEqual(results, values)
def test_LINK_TO_command(self):
" reads the records from the database, records are returned with their ids. "
ids = [
self.partner.create(self.cr, UID, {'name': 'foo'}),
self.partner.create(self.cr, UID, {'name': 'bar'}),
self.partner.create(self.cr, UID, {'name': 'baz'})
self.env['res.partner'].create({'name': 'foo'}).id,
self.env['res.partner'].create({'name': 'bar'}).id,
self.env['res.partner'].create({'name': 'baz'}).id,
]
commands = map(LINK_TO, ids)
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', commands, ['name'])
self.assertEqual(sorted_by_id(results), sorted_by_id([
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertItemsEqual(results, [
{'id': ids[0], 'name': 'foo'},
{'id': ids[1], 'name': 'bar'},
{'id': ids[2], 'name': 'baz'}
]))
{'id': ids[2], 'name': 'baz'},
])
def test_bare_ids_command(self):
" same as the equivalent LINK_TO commands "
ids = [
self.partner.create(self.cr, UID, {'name': 'foo'}),
self.partner.create(self.cr, UID, {'name': 'bar'}),
self.partner.create(self.cr, UID, {'name': 'baz'})
self.env['res.partner'].create({'name': 'foo'}).id,
self.env['res.partner'].create({'name': 'bar'}).id,
self.env['res.partner'].create({'name': 'baz'}).id,
]
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', ids, ['name'])
self.assertEqual(sorted_by_id(results), sorted_by_id([
results = self.env['res.partner'].resolve_2many_commands('child_ids', ids, ['name'])
self.assertItemsEqual(results, [
{'id': ids[0], 'name': 'foo'},
{'id': ids[1], 'name': 'bar'},
{'id': ids[2], 'name': 'baz'}
]))
{'id': ids[2], 'name': 'baz'},
])
def test_UPDATE_command(self):
" take the in-db records and merge the provided information in "
id_foo = self.partner.create(self.cr, UID, {'name': 'foo'})
id_bar = self.partner.create(self.cr, UID, {'name': 'bar'})
id_baz = self.partner.create(self.cr, UID, {'name': 'baz', 'city': 'tag'})
foo = self.env['res.partner'].create({'name': 'foo'})
bar = self.env['res.partner'].create({'name': 'bar'})
baz = self.env['res.partner'].create({'name': 'baz', 'city': 'tag'})
commands = [
LINK_TO(foo.id),
UPDATE(bar.id, {'name': 'qux', 'city': 'tagtag'}),
UPDATE(baz.id, {'name': 'quux'}),
]
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', [
LINK_TO(id_foo),
UPDATE(id_bar, {'name': 'qux', 'city': 'tagtag'}),
UPDATE(id_baz, {'name': 'quux'})
], ['name', 'city'])
self.assertEqual(sorted_by_id(results), sorted_by_id([
{'id': id_foo, 'name': 'foo', 'city': False},
{'id': id_bar, 'name': 'qux', 'city': 'tagtag'},
{'id': id_baz, 'name': 'quux', 'city': 'tag'}
]))
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name', 'city'])
self.assertItemsEqual(results, [
{'id': foo.id, 'name': 'foo', 'city': False},
{'id': bar.id, 'name': 'qux', 'city': 'tagtag'},
{'id': baz.id, 'name': 'quux', 'city': 'tag'},
])
def test_DELETE_command(self):
" deleted records are not returned at all. "
ids = [
self.partner.create(self.cr, UID, {'name': 'foo'}),
self.partner.create(self.cr, UID, {'name': 'bar'}),
self.partner.create(self.cr, UID, {'name': 'baz'})
self.env['res.partner'].create({'name': 'foo'}).id,
self.env['res.partner'].create({'name': 'bar'}).id,
self.env['res.partner'].create({'name': 'baz'}).id,
]
commands = [DELETE(ids[0]), DELETE(ids[1]), DELETE(ids[2])]
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', commands, ['name'])
commands = map(DELETE, ids)
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertEqual(results, [])
def test_mixed_commands(self):
ids = [
self.partner.create(self.cr, UID, {'name': name})
self.env['res.partner'].create({'name': name}).id
for name in ['NObar', 'baz', 'qux', 'NOquux', 'NOcorge', 'garply']
]
commands = [
CREATE({'name': 'foo'}),
UPDATE(ids[0], {'name': 'bar'}),
LINK_TO(ids[1]),
DELETE(ids[2]),
UPDATE(ids[3], {'name': 'quux',}),
UPDATE(ids[4], {'name': 'corge'}),
CREATE({'name': 'grault'}),
LINK_TO(ids[5]),
]
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', [
CREATE({'name': 'foo'}),
UPDATE(ids[0], {'name': 'bar'}),
LINK_TO(ids[1]),
DELETE(ids[2]),
UPDATE(ids[3], {'name': 'quux',}),
UPDATE(ids[4], {'name': 'corge'}),
CREATE({'name': 'grault'}),
LINK_TO(ids[5])
], ['name'])
self.assertEqual(sorted_by_id(results), sorted_by_id([
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertItemsEqual(results, [
{'name': 'foo'},
{'id': ids[0], 'name': 'bar'},
{'id': ids[1], 'name': 'baz'},
{'id': ids[3], 'name': 'quux'},
{'id': ids[4], 'name': 'corge'},
{'name': 'grault'},
{'id': ids[5], 'name': 'garply'}
]))
{'id': ids[5], 'name': 'garply'},
])
def test_LINK_TO_pairs(self):
"LINK_TO commands can be written as pairs, instead of triplets"
ids = [
self.partner.create(self.cr, UID, {'name': 'foo'}),
self.partner.create(self.cr, UID, {'name': 'bar'}),
self.partner.create(self.cr, UID, {'name': 'baz'})
self.env['res.partner'].create({'name': 'foo'}).id,
self.env['res.partner'].create({'name': 'bar'}).id,
self.env['res.partner'].create({'name': 'baz'}).id,
]
commands = map(lambda id: (4, id), ids)
commands = [(4, id) for id in ids]
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', commands, ['name'])
self.assertEqual(sorted_by_id(results), sorted_by_id([
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertItemsEqual(results, [
{'id': ids[0], 'name': 'foo'},
{'id': ids[1], 'name': 'bar'},
{'id': ids[2], 'name': 'baz'}
]))
{'id': ids[2], 'name': 'baz'},
])
def test_singleton_commands(self):
"DELETE_ALL can appear as a singleton"
results = self.partner.resolve_2many_commands(
self.cr, UID, 'child_ids', [DELETE_ALL()], ['name'])
commands = [DELETE_ALL()]
results = self.env['res.partner'].resolve_2many_commands('child_ids', commands, ['name'])
self.assertEqual(results, [])
+6 -3
View File
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.tests import common
from openerp.tools import get_cache_key_counter
from odoo.tests.common import TransactionCase
from odoo.tools import get_cache_key_counter
class TestOrmcache(common.TransactionCase):
class TestOrmcache(TransactionCase):
def test_ormcache(self):
""" Test the effectiveness of the ormcache() decorator. """
IMD = self.env['ir.model.data']
+2 -1
View File
@@ -2,7 +2,8 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
from openerp.osv.query import Query
from odoo.osv.query import Query
class QueryTestCase(unittest.TestCase):
@@ -1,632 +0,0 @@
-
Testing for hierarchical search in M2M
-
!python {model: res.partner }: |
ids = self.search(cr, uid, [('category_id', 'child_of',ref('res_partner_category_0'))])
assert len(ids) >= 1, ids
-
"1.0 Setup test partner categories: parent root"
-
!record {model: res.partner.category, id: categ_root}:
name: Root category
-
"1.1 Setup test partner categories: parent category"
-
!record {model: res.partner.category, id: categ_0}:
name: Parent category
parent_id: categ_root
-
"1.2 Setup test partner categories: child 1"
-
!record {model: res.partner.category, id: categ_1}:
name: Child 1
parent_id: categ_0
-
Test hierarchical search in M2M with child ID (list of ids)
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_root')])])
assert len(ids) == 3, ids
-
Test hierarchical search in M2M with child ID (single id)
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'child_of',ref('categ_root'))])
assert len(ids) == 3, ids
-
Test hierarchical search in M2M with child IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1'), ref('categ_0')])])
assert len(ids) == 2, ids
-
Test hierarchical search in M2M with child IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_0')])])
assert len(ids) == 2, ids
-
Test hierarchical search in M2M with child IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'child_of',[ref('categ_1')])])
assert len(ids) == 1, ids
-
Test hierarchical search in M2M with parent ID (list of ids)
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'parent_of',[ref('categ_1')])])
assert len(ids) == 3, ids
-
Test hierarchical search in M2M with parent ID (single id)
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'parent_of',ref('categ_1'))])
assert len(ids) == 3, ids
-
Test hierarchical search in M2M with parent IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'parent_of',[ref('categ_root'), ref('categ_0')])])
assert len(ids) == 2, ids
-
Test hierarchical search in M2M with parent IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'parent_of',[ref('categ_0')])])
assert len(ids) == 2, ids
-
Test hierarchical search in M2M with parent IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'parent_of',[ref('categ_1')])])
assert len(ids) == 3, ids
-
Test hierarchical search in M2M with parent IDs
-
!python {model: res.partner.category }: |
ids = self.search(cr, uid, [('id', 'parent_of',[ref('categ_root')])])
assert len(ids) == 1, ids
-
Testing that some domain expressions work
-
!python {model: res.partner }: |
ids = self.search(cr, uid, [('parent_id','=','Agrolait')])
assert len(ids) >= 1, ids
-
Trying the "in" operator, for scalar value
-
!python {model: res.partner }: |
ids = self.search(cr, uid, [('parent_id','in','Agrolait')])
assert len(ids) >= 1, ids
-
Trying the "in" operator for list value
-
!python {model: res.partner }: |
ids = self.search(cr, uid, [('parent_id','in',['Agrolait','ASUStek'])])
assert len(ids) >= 1, ids
-
Check we can use "in" operator for plain fields.
-
!python {model: ir.ui.menu }: |
ids = self.search(cr, uid, [('sequence','in',[1, 2, 10, 20])])
assert len(ids) >= 1, ids
-
Test one2many operator with empty search list
-
!assert {model: res.partner, search: "[('child_ids', 'in', [])]", count: 0, string: "Ids should be empty"}
-
Test one2many operator with False
-
!assert {model: res.partner, search: "[('child_ids', '=', False)]"}:
- list(child_ids) == []
-
Test many2many operator with empty search list
-
!assert {model: res.partner, search: "[('category_id', 'in', [])]", count: 0, string: "Ids should be empty"}
-
Test many2many operator with False
-
!assert {model: res.partner, search: "[('category_id', '=', False)]"}:
- list(category_id) == []
-
Filtering on invalid value across x2many relationship should return an empty set
-
!assert {model: res.partner, search: "[('child_ids.city','=','foo')]", count: 0, string: "Searching for address.city = foo should give empty results"}
-
Check if many2one works with empty search list
-
!assert {model: res.partner, search: "[('company_id','in', [])]", count: 0, string: "Searching for company_id in [] should be empty!" }
-
For the sake of the following tests, I will create a second company
-
!record {model: res.company, id: ymltest_company2}:
name: Acme 2
-
And create a few partners with that company or no company
-
!python {model: res.partner }: |
for r in range(4):
self.create(cr, uid, { 'name': 'P of Acme %d' % r,
'company_id': ref('ymltest_company2') })
for r in range(4):
self.create(cr, uid, { 'name': 'P of All %d' % r,
'company_id': False })
-
Check if many2one works with negative empty list
-
!python {model: res.partner }: |
all_ids = self.search(cr, uid, [])
all_ids.sort()
res_ids = self.search(cr, uid,['|',('company_id','not in', []), ('company_id','=',False)])
res_ids.sort()
assert all_ids == res_ids, "not in [] fails"
-
Check that many2one will pick the correct records with a list
-
!python {model: res.partner }: |
res_ids = self.search(cr, uid, [('company_id', 'in', [False,])])
assert len(res_ids) >= 4, "We created 4 partners w/company, why find %d? %r" % \
(len(res_ids), res_ids)
-
Check that many2one will exclude the correct records with a list
-
!python {model: res.partner }: |
# assuming that the default company is #1
res_ids = self.search(cr, uid, [('company_id', 'not in', [1])])
assert len(res_ids) >= 4, "We should have found 4 records at least, only have %d! %r" % \
(len(res_ids), res_ids)
-
Check that we exclude the correct records, + False
-
!python {model: res.partner }: |
# assuming that the default company is #1
res_ids = self.search(cr, uid, ['|', ('company_id', 'not in', [1]), ('company_id', '=', False)])
assert len(res_ids) >= 8, "We should have found 8 records at least, only have %d! %r" % \
(len(res_ids), res_ids)
-
Check that multi-level expressions also work
-
!python {model: res.partner }: |
res_ids = self.search(cr, uid, [('company_id.partner_id', 'in', [])])
assert res_ids == [], "Searching an empty set should return empty result, not %r" % res_ids
-
Check that multi-level expressions with negative op work
-
!python {model: res.partner }: |
all_ids = self.search(cr, uid, [('company_id', '!=', False)])
all_ids.sort()
res_ids = self.search(cr, uid, [('company_id.partner_id', 'not in', [])])
res_ids.sort()
assert res_ids == all_ids, "Searching against empty set failed, returns %r" % res_ids
-
Test the '(not) like/in' behavior. res.partner and its parent_id column are used because
parent_id is a many2one, allowing to test the Null value, and there are actually some
null and non-null values in the demo data.
-
!python {model: res.partner }: |
partner_ids = self.search(cr, uid, [])
partner_ids.sort()
max_partner_id = max(partner_ids)
# Grab test sample data without using a normal
# search domain, because we want to test these later,
# so we can't rely on them!
partners = self.browse(cr, uid, partner_ids)
with_parent = []
without_parent = []
with_website = []
for x in partners:
if x.parent_id:
with_parent.append(x.id)
else:
without_parent.append(x.id)
if x.website:
with_website.append(x.id)
with_parent.sort()
without_parent.sort()
with_website.sort()
# We treat null values differently than in SQL. For instance in SQL:
# SELECT id FROM res_partner WHERE parent_id NOT IN (0)
# will return only the records with non-null parent_id.
# SELECT id FROM res_partner WHERE parent_id IN (0)
# will return expectedly nothing (our ids always begin at 1).
# This means the union of those two results will give only some
# records, but not all present in database.
#
# When using domains and the ORM's search method, we think it is
# more intuitive that the union returns all the records, and that
# a domain like ('parent_id', 'not in', [0]) will return all
# the records. For instance, if you perform a search for the companies
# that don't have OpenERP has a parent company, you expect to find,
# among others, the companies that don't have parent company.
#
# existing values be treated similarly if we simply check that some
# existing value belongs to them.
res_0 = self.search(cr, uid, [('parent_id', 'not like', 'probably_unexisting_name')]) # get all rows, included null parent_id
res_0.sort()
res_1 = self.search(cr, uid, [('parent_id', 'not in', [max_partner_id + 1])]) # get all rows, included null parent_id
res_1.sort()
res_2 = self.search(cr, uid, [('parent_id', '!=', False)]) # get rows with not null parent_id, deprecated syntax
res_2.sort()
res_3 = self.search(cr, uid, [('parent_id', 'not in', [])]) # get all rows, included null parent_id
res_3.sort()
res_4 = self.search(cr, uid, [('parent_id', 'not in', [False])]) # get rows with not null parent_id
res_4.sort()
res_4b = self.search(cr, uid, [('parent_id', 'not ilike', '')]) # get only rows without parent
res_4b.sort()
assert res_0 == partner_ids, 'res0: expected %r, got %r' % (partner_ids, res_0)
assert res_1 == partner_ids, 'res1: expected %r, got %r' % (partner_ids, res_1)
assert res_2 == with_parent, 'res2: expected %r, got %r' % (with_parent, res_2)
assert res_3 == partner_ids, 'res3: expected %r, got %r' % (partner_ids, res_3)
assert res_4 == with_parent, 'res4: expected %r, got %r' % (with_parent, res_4)
assert res_4b == without_parent, 'res4b: expected %r, got %r' % (without_parent, res_4b)
# The results of these queries, when combined with queries 0..4 must
# give the whole set of ids.
res_5 = self.search(cr, uid, [('parent_id', 'like', 'probably_unexisting_name')])
res_5.sort()
res_6 = self.search(cr, uid, [('parent_id', 'in', [max_partner_id + 1])])
res_6.sort()
res_7 = self.search(cr, uid, [('parent_id', '=', False)])
res_7.sort()
res_8 = self.search(cr, uid, [('parent_id', 'in', [])])
res_8.sort()
res_9 = self.search(cr, uid, [('parent_id', 'in', [False])])
res_9.sort()
res_9b = self.search(cr, uid, [('parent_id', 'ilike', '')]) # get those with a parent
res_9b.sort()
assert res_5 == [], 'res5: expected %r, got %r' % ([], res_5)
assert res_6 == [], 'res6: expected %r, got %r' % ([], res_6)
assert res_7 == without_parent, 'res7: expected %r, got %r' % (without_parent, res_7)
assert res_8 == [], 'res8: expected %r, got %r' % ([], res_8)
assert res_9 == without_parent, 'res9: expected %r, got %r' % (without_parent, res_9)
assert res_9b == with_parent, 'res9b: expected %r, got %r' % (with_parent, res_9b)
# These queries must return exactly the results than the queries 0..4,
# i.e. not ... in ... must be the same as ... not in ... .
res_10 = self.search(cr, uid, ['!', ('parent_id', 'like', 'probably_unexisting_name')])
res_10.sort()
res_11 = self.search(cr, uid, ['!', ('parent_id', 'in', [max_partner_id + 1])])
res_11.sort()
res_12 = self.search(cr, uid, ['!', ('parent_id', '=', False)])
res_12.sort()
res_13 = self.search(cr, uid, ['!', ('parent_id', 'in', [])])
res_13.sort()
res_14 = self.search(cr, uid, ['!', ('parent_id', 'in', [False])])
res_14.sort()
assert res_0 == res_10
assert res_1 == res_11
assert res_2 == res_12
assert res_3 == res_13
assert res_4 == res_14
# Testing many2one field is not enough, a regular char field is tested
# with in [] and must not return any result.
res_15 = self.search(cr, uid, [('website', 'in', [])])
assert res_15 == []
# not in [] must return everything.
res_16 = self.search(cr, uid, [('website', 'not in', [])])
res_16.sort()
assert res_16 == partner_ids
res_17 = self.search(cr, uid, [('website', '!=', False)])
res_17.sort()
assert res_17 == with_website
-
Check behavior for required many2one fields
-
!python {model: res.company }: |
company_ids = sorted(self.search(cr, uid, []))
# currency_id is required
res_101 = sorted(self.search(cr, uid, [('currency_id', 'not ilike', '')])) # get no companies
res_102 = sorted(self.search(cr, uid, [('currency_id', 'ilike', '')])) # get all companies
assert res_101 == [], 'res_101: expected %r, got %r' % ([], res_101)
assert res_102 == company_ids, 'res_102: expected %r, got %r' % (company_ids, res_102)
-
Verify domain evaluation for `one2many != False`
-
!python {model: res.partner.category }: |
all_ids = self.search(cr, uid, [])
parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
result = set(self.search(cr, uid, [('child_ids', '!=', False)]))
assert result and result == parent_categs, "Got %r, expected %r" % (result, parent_categs)
-
Verify domain evaluation for `one2many == False`
-
!python {model: res.partner.category }: |
all_ids = self.search(cr, uid, [])
parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
leaf_categs = set(all_ids) - parent_categs
result = set(self.search(cr, uid, [('child_ids', '=', False)]))
assert result and result == leaf_categs, "Got %r, expected %r" % (result, leaf_categs)
-
Equivalent queries.
-
!python {model: res.currency }: |
max_currency_id = max(self.search(cr, uid, []))
res_0 = self.search(cr, uid, [])
res_1 = self.search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
res_2 = self.search(cr, uid, [('id', 'not in', [max_currency_id + 1003])])
res_3 = self.search(cr, uid, [('id', 'not in', [])])
res_4 = self.search(cr, uid, [('id', '!=', False)])
res_0.sort()
res_1.sort()
res_2.sort()
res_3.sort()
res_4.sort()
assert res_0 == res_1
assert res_0 == res_2
assert res_0 == res_3
assert res_0 == res_4
-
Equivalent queries, integer and string.
-
!python {model: res.partner }: |
all_ids = self.search(cr, uid, [])
if len(all_ids) > 1:
one = all_ids[0]
record = self.browse(cr, uid, one)
others = all_ids[1:]
res_1 = self.search(cr, uid, [('id', '=', one)])
# self.search(cr, uid, [('id', '!=', others)]) # not permitted
res_2 = self.search(cr, uid, [('id', 'not in', others)])
res_3 = self.search(cr, uid, ['!', ('id', '!=', one)])
res_4 = self.search(cr, uid, ['!', ('id', 'in', others)])
# res_5 = self.search(cr, uid, [('id', 'in', one)]) # TODO make it permitted, just like for child_of
res_6 = self.search(cr, uid, [('id', 'in', [one])])
res_7 = self.search(cr, uid, [('name', '=', record.name)])
res_8 = self.search(cr, uid, [('name', 'in', [record.name])])
# res_9 = self.search(cr, uid, [('name', 'in', record.name)]) # TODO
assert [one] == res_1
assert [one] == res_2
assert [one] == res_3
assert [one] == res_4
#assert [one] == res_5
assert [one] == res_6
assert [one] == res_7
-
Need a company with a parent_id.
-
!record {model: res.company, id: ymltest_company3}:
name: Acme 3
-
Need a company with a parent_id.
-
!record {model: res.company, id: ymltest_company4}:
name: Acme 4
parent_id: ymltest_company3
-
Equivalent queries, one2many.
-
!python {model: res.company }: |
company = self.browse(cr, uid, ref('ymltest_company3'))
# one2many towards same model
res_1 = self.search(cr, uid, [('child_ids', 'in', [x.id for x in company.child_ids])]) # any company having a child of company3 as child
res_2 = self.search(cr, uid, [('child_ids', 'in', [company.child_ids[0].id])]) # any company having the first child of company3 as child
assert res_1 == [ref('ymltest_company3')], "res_1"
assert res_2 == [ref('ymltest_company3')], "res_2"
# try testing real subsets with IN/NOT IN
res_partner = self.pool.get('res.partner')
res_users = self.pool.get('res.users')
p1, _ = res_partner.name_create(cr, uid, "Dédé Boitaclou")
p2, _ = res_partner.name_create(cr, uid, "Raoulette Pizza O'poil")
u1a = res_users.create(cr, uid, {'login': 'dbo', 'partner_id': p1})
u1b = res_users.create(cr, uid, {'login': 'dbo2', 'partner_id': p1})
u2 = res_users.create(cr, uid, {'login': 'rpo', 'partner_id': p2})
assert [p1] == res_partner.search(cr, uid, [('user_ids', 'in', u1a)]), "o2m IN accept single int on right side"
assert [p1] == res_partner.search(cr, uid, [('user_ids', '=', 'Dédé Boitaclou')]), "o2m NOT IN matches none on the right side"
assert [] == res_partner.search(cr, uid, [('user_ids', 'in', [10000])]), "o2m NOT IN matches none on the right side"
assert [p1,p2] == res_partner.search(cr, uid, [('user_ids', 'in', [u1a,u2])]), "o2m IN matches any on the right side"
all_partners = res_partner.search(cr, uid, [])
assert (set(all_partners) - set([p1])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', u1a)])), "o2m NOT IN matches none on the right side"
assert (set(all_partners) - set([p1])) == set(res_partner.search(cr, uid, [('user_ids', '!=', 'Dédé Boitaclou')])), "o2m NOT IN matches none on the right side"
assert (set(all_partners) - set([p1,p2])) == set(res_partner.search(cr, uid, [('user_ids', 'not in', [u1b, u2])])), "o2m NOT IN matches none on the right side"
# child_of x returns x and its children (direct or not).
company = self.browse(cr, uid, ref('ymltest_company3'))
expected = [ref('ymltest_company3'), ref('ymltest_company4')]
expected.sort()
res_1 = self.search(cr, uid, [('id', 'child_of', [ref('ymltest_company3')])])
res_1.sort()
res_2 = self.search(cr, uid, [('id', 'child_of', ref('ymltest_company3'))])
res_2.sort()
res_3 = self.search(cr, uid, [('id', 'child_of', [company.name])])
res_3.sort()
res_4 = self.search(cr, uid, [('id', 'child_of', company.name)])
res_4.sort()
assert res_1 == expected
assert res_2 == expected
assert res_3 == expected
assert res_4 == expected
# parent_of x returns x and its parents (direct or not).
company = self.browse(cr, uid, ref('ymltest_company4'))
expected = [ref('ymltest_company4'), ref('ymltest_company3')]
expected.sort()
res_1 = self.search(cr, uid, [('id', 'parent_of', [ref('ymltest_company4')])])
res_1.sort()
res_2 = self.search(cr, uid, [('id', 'parent_of', ref('ymltest_company4'))])
res_2.sort()
res_3 = self.search(cr, uid, [('id', 'parent_of', [company.name])])
res_3.sort()
res_4 = self.search(cr, uid, [('id', 'parent_of', company.name)])
res_4.sort()
assert res_1 == expected
assert res_2 == expected
assert res_3 == expected
assert res_4 == expected
-
Equivalent queries, one2many.
-
!python {model: res.currency }: |
# Create a currency and a currency rate
res_currency = self.pool['res.currency']
res_currency_rate = self.pool['res.currency.rate']
currency = res_currency.create(cr, uid, {'name': 'ZZZ', 'symbol': 'ZZZ', 'rounding': 1.0})
currency_rate = self.pool['res.currency.rate'].create(cr, uid, {'name': '2010-01-01', 'currency_id': currency, 'rate': 1.0})
# Search the currency via its rates one2many (the one2many must point back at the currency).
max_currency_rate_id = max(res_currency_rate.search(cr, uid, []))
currency_rate1 = res_currency_rate.search(cr, uid, [('name', 'not like', 'probably_unexisting_name')])
currency_rate2 = res_currency_rate.search(cr, uid, [('id', 'not in', [max_currency_rate_id + 1003])])
currency_rate3 = res_currency_rate.search(cr, uid, [('id', 'not in', [])])
assert currency_rate1 == currency_rate2 == currency_rate3, 'All 3 results should be the same : empty'
default_currency = self.browse(cr, uid, 1)
# one2many towards another model
res_3 = self.search(cr, uid, [('rate_ids', 'in', [x.id for x in default_currency.rate_ids])]) # currencies having a rate of main currency
res_4 = self.search(cr, uid, [('rate_ids', 'in', [default_currency.rate_ids[0].id])]) # currencies having first rate of main currency
res_5 = self.search(cr, uid, [('rate_ids', 'in', default_currency.rate_ids[0].id)]) # currencies having first rate of main currency
# res_6 = self.search(cr, uid, [('rate_ids', 'in', [default_currency.rate_ids[0].name])])
# res_7 = self.search(cr, uid, [('rate_ids', '=', default_currency.rate_ids[0].name)])
# res_8 = self.search(cr, uid, [('rate_ids', 'like', default_currency.rate_ids[0].name)])
res_9 = self.search(cr, uid, [('rate_ids', 'like', 'probably_unexisting_name')])
# self.search(cr, uid, [('rate_ids', 'unexisting_op', 'probably_unexisting_name')]) # TODO expected exception
assert res_3 == [1], "res_3"
assert res_4 == [1], "res_4"
assert res_5 == [1], "res_5"
# assert res_7 == [1], "res_7"
# assert res_8 == [1], "res_8"
assert res_9 == [], "res_9"
# get the currencies referenced by some currency rates using a weird negative domain
res_10 = self.search(cr, uid, [('rate_ids', 'not like', 'probably_unexisting_name')])
res_11 = self.search(cr, uid, [('rate_ids', 'not in', [max_currency_rate_id + 1])])
res_12 = self.search(cr, uid, [('rate_ids', '!=', False)])
res_13 = self.search(cr, uid, [('rate_ids', 'not in', [])])
res_10.sort()
res_11.sort()
res_12.sort()
res_13.sort()
assert res_10 == res_11, 'res_11'
assert res_10 == res_12, 'res_12'
assert res_10 == res_13, 'res_13'
-
Unaccent. Create a company with an accent in its name.
-
!record {model: res.company, id: ymltest_unaccent_company}:
name: Hélène
-
Test the unaccent-enabled 'ilike'.
-
!python {model: res.company}: |
if self.pool.has_unaccent:
ids = self.search(cr, uid, [('name','ilike','Helene')], {})
assert ids == [ref('ymltest_unaccent_company')]
ids = self.search(cr, uid, [('name','ilike','hélène')], {})
assert ids == [ref('ymltest_unaccent_company')]
ids = self.search(cr, uid, [('name','not ilike','Helene')], {})
assert ref('ymltest_unaccent_company') not in ids
ids = self.search(cr, uid, [('name','not ilike','hélène')], {})
assert ref('ymltest_unaccent_company') not in ids
-
Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on an untranslated field.
-
!python {model: res.partner }: |
all_ids = self.search(cr, uid, [('name', '=like', 'A_U_TeK')])
assert len(all_ids) == 1, "Must match one partner (ASUSTeK), got %r"%all_ids
all_ids = self.search(cr, uid, [('name', '=ilike', 'c%')])
assert len(all_ids) >= 1, "Must match one partner (China Export), got %r"%all_ids
-
Check that =like/=ilike expressions (no wildcard variants of like/ilike) are working on translated field.
-
!python {model: res.country }: |
all_ids = self.search(cr, uid, [('name', '=like', 'Ind__')])
assert len(all_ids) == 1, "Must match India only, got %r"%all_ids
all_ids = self.search(cr, uid, [('name', '=ilike', 'z%')])
assert len(all_ids) == 3, "Must match only countries with names starting with Z (currently 3), got %r"%all_ids
-
Use the create_date column on res.country (which doesn't declare it in _columns).
-
!python {model: res.country }: |
ids = self.search(cr, uid, [('create_date', '<', '2001-01-01 12:00:00')])
-
Verify that invalid expressions are refused, even for magic fields
-
!python {model: res.country }: |
try:
self.search(cr, uid, [('does_not_exist', '=', 'foo')])
raise AssertionError('Invalid fields should not be accepted')
except ValueError:
pass
try:
self.search(cr, uid, [('create_date', '>>', 'foo')])
raise AssertionError('Invalid operators should not be accepted')
except ValueError:
pass
import psycopg2
try:
cr._default_log_exceptions = False
cr.execute('SAVEPOINT expression_failure_test')
self.search(cr, uid, [('create_date', '=', "1970-01-01'); --")])
# if the above search gives no error, the operand was not escaped!
cr.execute('RELEASE SAVEPOINT expression_failure_test')
raise AssertionError('Operands should always be SQL escaped')
except psycopg2.DataError:
# Should give: 'DataError: invalid input syntax for type timestamp' or similar
cr.execute('ROLLBACK TO SAVEPOINT expression_failure_test')
-
Testing for Many2Many field with category vendor and active=False
-
!python {model: res.partner }: |
vals = {'category_id': [(6, 0, [ref("base.res_partner_category_1")])],
'name': 'OpenERP Test',
'active': False,
'child_ids': [(0, 0, {'name': 'address of OpenERP Test', 'country_id': ref("base.be")})]
}
self.create(cr, uid, vals, context=context)
res_ids = self.search(cr, uid, [('category_id', 'ilike', 'vendor'), ('active', '=', False)])
assert len(res_ids) != 0, "Record not Found with category vendor and active False."
-
Testing for One2Many field with country Belgium and active=False
-
!python {model: res.partner }: |
res_ids = self.search(cr, uid, [('child_ids.country_id','=','Belgium'),('active','=',False)])
assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
-
Check that we can exclude translated fields (bug lp:1071710)
-
!python {model: res.country}: |
# first install french language
Modules = self.pool.get('ir.module.module')
base = Modules.browse(cr, uid, Modules.search(cr, uid, [('name', '=', 'base')])[0])
# hack: mark module as installed to allow install of the translation
base_state = base.state
base.write({'state': 'installed'})
base.update_translations('fr_FR')
base.write({'state': base_state})
# tests
be_id = self.search(cr, uid, [('name', '=', 'Belgium')])[0]
ctx = {'lang': 'fr_FR'}
not_be = self.search(cr, uid, [('name', '!=', 'Belgique')], context=ctx)
assert be_id not in not_be, "Search match failed"
# indirect search via m2o
Partners = self.pool.get('res.partner')
agrolait = Partners.search(cr, uid, [('name', '=', 'Agrolait')])[0]
not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgium')])
assert agrolait not in not_be, "Search match failed (m2o)"
not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgique')], context=ctx)
assert agrolait not in not_be, "Search match failed (m2o)"
+64
View File
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
from odoo.tests import common
#: Stores state information across multiple test classes
test_state = None
def setUpModule():
global test_state
test_state = {}
def tearDownModule():
global test_state
test_state = None
class TestPhaseInstall00(unittest.TestCase):
"""
WARNING: Relies on tests being run in alphabetical order
"""
@classmethod
def setUpClass(cls):
cls.state = None
def test_00_setup(self):
type(self).state = 'init'
@common.at_install(False)
def test_01_no_install(self):
type(self).state = 'error'
def test_02_check(self):
self.assertEqual(self.state, 'init',
"Testcase state should not have been transitioned from 00")
class TestPhaseInstall01(unittest.TestCase):
at_install = False
def test_default_norun(self):
self.fail("An unmarket test in a non-at-install case should not run")
@common.at_install(True)
def test_set_run(self):
test_state['set_at_install'] = True
class TestPhaseInstall02(unittest.TestCase):
"""
Can't put the check for test_set_run in the same class: if
@common.at_install does not work for test_set_run, it won't work for
the other one either. Thus move checking of whether test_set_run has
correctly run indeed to a separate class.
Warning: relies on *classes* being run in alphabetical order in test
modules
"""
def test_check_state(self):
self.assertTrue(test_state.get('set_at_install'),
"The flag should be set if local overriding of runstate")
+34 -49
View File
@@ -1,92 +1,78 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import cgi
import collections
import json
import os.path
import glob
import re
import collections
from lxml import etree
import openerp.modules
from openerp.addons.base.ir.ir_qweb import QWebContext, FileSystemLoader
from openerp.tests import common
from odoo.addons.base.ir.ir_qweb import QWebContext, FileSystemLoader
from odoo.modules import get_module_resource
from odoo.tests.common import TransactionCase
class TestQWebTField(common.TransactionCase):
class TestQWebTField(TransactionCase):
def setUp(self):
super(TestQWebTField, self).setUp()
self.env = self.env(context={'inherit_branding': True})
self.engine = self.env['ir.qweb']
self.env_branding = self.env(context={'inherit_branding': True})
self.engine = self.env_branding['ir.qweb']
def context(self, values):
return QWebContext(self.env, values)
return QWebContext(self.env_branding, values)
def test_trivial(self):
field = etree.Element('span', {'t-field': u'company.name'})
company = self.env['res.company'].create({'name': "My Test Company"})
Companies = self.registry('res.company')
company_id = Companies.create(self.cr, self.uid, {
'name': "My Test Company"
})
result = self.engine.render_node(field, self.context({
'company': Companies.browse(self.cr, self.uid, company_id),
}))
result = self.engine.render_node(field, self.context({'company': company}))
self.assertEqual(
result,
'<span data-oe-model="res.company" data-oe-id="%d" '
'data-oe-field="name" data-oe-type="char" '
'data-oe-expression="company.name">%s</span>' % (
company_id,
"My Test Company",))
company.id,
"My Test Company",
),
)
def test_i18n(self):
field = etree.Element('span', {'t-field': u'company.name'})
Companies = self.registry('res.company')
s = u"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"
company_id = Companies.create(self.cr, self.uid, {
'name': s,
})
result = self.engine.render_node(field, self.context({
'company': Companies.browse(self.cr, self.uid, company_id),
}))
company = self.env['res.company'].create({'name': s})
result = self.engine.render_node(field, self.context({'company': company}))
self.assertEqual(
result,
'<span data-oe-model="res.company" data-oe-id="%d" '
'data-oe-field="name" data-oe-type="char" '
'data-oe-expression="company.name">%s</span>' % (
company_id,
cgi.escape(s.encode('utf-8')),))
company.id,
cgi.escape(s.encode('utf-8')),
),
)
def test_reject_crummy_tags(self):
field = etree.Element('td', {'t-field': u'company.name'})
with self.assertRaisesRegexp(
AssertionError,
r'^RTE widgets do not work correctly'):
self.engine.render_node(field, self.context({
'company': None
}))
with self.assertRaisesRegexp(AssertionError, r'^RTE widgets do not work correctly'):
self.engine.render_node(field, self.context({'company': None}))
def test_reject_t_tag(self):
field = etree.Element('t', {'t-field': u'company.name'})
with self.assertRaisesRegexp(
AssertionError,
r'^t-field can not be used on a t element'):
self.engine.render_node(field, self.context({
'company': None
}))
with self.assertRaisesRegexp(AssertionError, r'^t-field can not be used on a t element'):
self.engine.render_node(field, self.context({'company': None}))
class TestQWeb(common.TransactionCase):
matcher = re.compile('^qweb-test-(.*)\.xml$')
class TestQWeb(TransactionCase):
matcher = re.compile(r'^qweb-test-(.*)\.xml$')
@classmethod
def get_cases(cls):
path = cls.qweb_test_file_path()
return (
cls("test_qweb_{}".format(cls.matcher.match(f).group(1)))
for f in os.listdir(path)
@@ -94,12 +80,10 @@ class TestQWeb(common.TransactionCase):
if f != 'qweb-test-extend.xml'
if cls.matcher.match(f)
)
@classmethod
def qweb_test_file_path(cls):
path = os.path.dirname(
openerp.modules.get_module_resource(
'web', 'static', 'lib', 'qweb', 'qweb2.js'))
return path
return os.path.dirname(get_module_resource('web', 'static', 'lib', 'qweb', 'qweb2.js'))
def __getattr__(self, item):
if not item.startswith('test_qweb_'):
@@ -129,9 +113,10 @@ class TestQWeb(common.TransactionCase):
self.assertEqual(
qweb.render(template, qwebcontext=ctx).strip(),
(result or u'').strip().encode('utf-8'),
template
template,
)
def load_tests(loader, suite, _):
# can't override TestQWeb.__dir__ because dir() called on *class* not
# instance
+18 -18
View File
@@ -1,13 +1,15 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp
import openerp.tests.common as common
from odoo import exceptions
from odoo.tests.common import TransactionCase
class test_res_config(common.TransactionCase):
class TestResConfig(TransactionCase):
def setUp(self):
super(test_res_config, self).setUp()
self.res_config = self.registry('res.config.settings')
super(TestResConfig, self).setUp()
self.ResConfig = self.env['res.config.settings']
# Define the test values
self.menu_xml_id = 'base.menu_action_res_users'
@@ -17,15 +19,13 @@ class test_res_config(common.TransactionCase):
# Note: see the get_config_warning() doc for a better example
# Fetch the expected values
module_name, menu_xml_id = self.menu_xml_id.split('.')
dummy, menu_id = self.registry('ir.model.data').get_object_reference(self.cr, self.uid, module_name, menu_xml_id)
ir_ui_menu = self.registry('ir.ui.menu').browse(self.cr, self.uid, menu_id, context=None)
menu = self.env.ref(self.menu_xml_id)
model_name, field_name = self.full_field_name.rsplit('.', 1)
self.expected_path = ir_ui_menu.complete_name
self.expected_action_id = ir_ui_menu.action.id
self.expected_name = self.registry(model_name).fields_get(self.cr, self.uid, allfields=[field_name], context=None)[field_name]['string']
self.expected_path = menu.complete_name
self.expected_action_id = menu.action.id
self.expected_name = self.env[model_name].fields_get([field_name])[field_name]['string']
self.expected_final_error_msg = self.error_msg % {
'field:res.partner.lang': self.expected_name,
'menu:base.menu_action_res_users': self.expected_path
@@ -36,7 +36,7 @@ class test_res_config(common.TransactionCase):
def test_00_get_option_path(self):
""" The get_option_path() method should return a tuple containing a string and an integer """
res = self.res_config.get_option_path(self.cr, self.uid, self.menu_xml_id, context=None)
res = self.ResConfig.get_option_path(self.menu_xml_id)
# Check types
self.assertIsInstance(res, tuple)
@@ -50,7 +50,7 @@ class test_res_config(common.TransactionCase):
def test_10_get_option_name(self):
""" The get_option_name() method should return a string """
res = self.res_config.get_option_name(self.cr, self.uid, self.full_field_name, context=None)
res = self.ResConfig.get_option_name(self.full_field_name)
# Check type
self.assertIsInstance(res, basestring)
@@ -60,10 +60,10 @@ class test_res_config(common.TransactionCase):
def test_20_get_config_warning(self):
""" The get_config_warning() method should return a RedirectWarning """
res = self.res_config.get_config_warning(self.cr, self.error_msg, context=None)
res = self.ResConfig.get_config_warning(self.error_msg)
# Check type
self.assertIsInstance(res, openerp.exceptions.RedirectWarning)
self.assertIsInstance(res, exceptions.RedirectWarning)
# Check returned value
self.assertEqual(res.args[0], self.expected_final_error_msg)
@@ -71,10 +71,10 @@ class test_res_config(common.TransactionCase):
def test_30_get_config_warning_wo_menu(self):
""" The get_config_warning() method should return a Warning exception """
res = self.res_config.get_config_warning(self.cr, self.error_msg_wo_menu, context=None)
res = self.ResConfig.get_config_warning(self.error_msg_wo_menu)
# Check type
self.assertIsInstance(res, openerp.exceptions.Warning)
self.assertIsInstance(res, exceptions.Warning)
# Check returned value
self.assertEqual(res.args[0], self.expected_final_error_msg_wo_menu)
+5 -4
View File
@@ -1,11 +1,12 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp.tests.common as common
from odoo.tests.common import TransactionCase
class test_res_lang(common.TransactionCase):
class test_res_lang(TransactionCase):
def test_00_intersperse(self):
from openerp.addons.base.res.res_lang import intersperse
from odoo.addons.base.res.res_lang import intersperse
assert intersperse("", []) == ("", 0)
assert intersperse("0", []) == ("0", 0)
@@ -3,7 +3,8 @@
# Copyright (c) 2015 ACSONE SA/NV (<http://acsone.eu>)
from openerp.tests.common import TransactionCase
from odoo.tests.common import TransactionCase
class TestResPartnerBank(TransactionCase):
"""Tests acc_number
+78 -92
View File
@@ -1,26 +1,24 @@
import unittest
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp.tests.common as common
from odoo.tests.common import TransactionCase
class test_search(common.TransactionCase):
class test_search(TransactionCase):
def test_00_search_order(self):
registry, cr, uid = self.registry, self.cr, self.uid
# Create 6 partners with a given name, and a given creation order to
# ensure the order of their ID. Some are set as unactive to verify they
# ensure the order of their ID. Some are set as inactive to verify they
# are by default excluded from the searches and to provide a second
# `order` argument.
partners = registry('res.partner')
c = partners.create(cr, uid, {'name': 'test_search_order_C'})
d = partners.create(cr, uid, {'name': 'test_search_order_D', 'active': False})
a = partners.create(cr, uid, {'name': 'test_search_order_A'})
b = partners.create(cr, uid, {'name': 'test_search_order_B'})
ab = partners.create(cr, uid, {'name': 'test_search_order_AB'})
e = partners.create(cr, uid, {'name': 'test_search_order_E', 'active': False})
Partner = self.env['res.partner']
c = Partner.create({'name': 'test_search_order_C'})
d = Partner.create({'name': 'test_search_order_D', 'active': False})
a = Partner.create({'name': 'test_search_order_A'})
b = Partner.create({'name': 'test_search_order_B'})
ab = Partner.create({'name': 'test_search_order_AB'})
e = Partner.create({'name': 'test_search_order_E', 'active': False})
# The tests.
@@ -28,116 +26,109 @@ class test_search(common.TransactionCase):
# The order of the returned ids should be given by the `order`
# parameter of search().
name_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="name asc")
self.assertEqual([a, ab, b, c], name_asc, "Search with 'NAME ASC' order failed.")
name_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="name desc")
self.assertEqual([c, b, ab, a], name_desc, "Search with 'NAME DESC' order failed.")
id_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="id asc")
self.assertEqual([c, a, b, ab], id_asc, "Search with 'ID ASC' order failed.")
id_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%')], order="id desc")
self.assertEqual([ab, b, a, c], id_desc, "Search with 'ID DESC' order failed.")
name_asc = Partner.search([('name', 'like', 'test_search_order%')], order="name asc")
self.assertEqual([a, ab, b, c], list(name_asc), "Search with 'NAME ASC' order failed.")
name_desc = Partner.search([('name', 'like', 'test_search_order%')], order="name desc")
self.assertEqual([c, b, ab, a], list(name_desc), "Search with 'NAME DESC' order failed.")
id_asc = Partner.search([('name', 'like', 'test_search_order%')], order="id asc")
self.assertEqual([c, a, b, ab], list(id_asc), "Search with 'ID ASC' order failed.")
id_desc = Partner.search([('name', 'like', 'test_search_order%')], order="id desc")
self.assertEqual([ab, b, a, c], list(id_desc), "Search with 'ID DESC' order failed.")
# The inactive records shouldn't be excluded as soon as a condition on
# that field is present in the domain. The `order` parameter of
# search() should support any legal coma-separated values.
active_asc_id_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id asc")
self.assertEqual([d, e, c, a, b, ab], active_asc_id_asc, "Search with 'ACTIVE ASC, ID ASC' order failed.")
active_desc_id_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active desc, id asc")
self.assertEqual([c, a, b, ab, d, e], active_desc_id_asc, "Search with 'ACTIVE DESC, ID ASC' order failed.")
active_asc_id_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id desc")
self.assertEqual([e, d, ab, b, a, c], active_asc_id_desc, "Search with 'ACTIVE ASC, ID DESC' order failed.")
active_desc_id_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active desc, id desc")
self.assertEqual([ab, b, a, c, e, d], active_desc_id_desc, "Search with 'ACTIVE DESC, ID DESC' order failed.")
id_asc_active_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id asc, active asc")
self.assertEqual([c, d, a, b, ab, e], id_asc_active_asc, "Search with 'ID ASC, ACTIVE ASC' order failed.")
id_asc_active_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id asc, active desc")
self.assertEqual([c, d, a, b, ab, e], id_asc_active_desc, "Search with 'ID ASC, ACTIVE DESC' order failed.")
id_desc_active_asc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active asc")
self.assertEqual([e, ab, b, a, d, c], id_desc_active_asc, "Search with 'ID DESC, ACTIVE ASC' order failed.")
id_desc_active_desc = partners.search(cr, uid, [('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active desc")
self.assertEqual([e, ab, b, a, d, c], id_desc_active_desc, "Search with 'ID DESC, ACTIVE DESC' order failed.")
active_asc_id_asc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id asc")
self.assertEqual([d, e, c, a, b, ab], list(active_asc_id_asc), "Search with 'ACTIVE ASC, ID ASC' order failed.")
active_desc_id_asc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active desc, id asc")
self.assertEqual([c, a, b, ab, d, e], list(active_desc_id_asc), "Search with 'ACTIVE DESC, ID ASC' order failed.")
active_asc_id_desc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active asc, id desc")
self.assertEqual([e, d, ab, b, a, c], list(active_asc_id_desc), "Search with 'ACTIVE ASC, ID DESC' order failed.")
active_desc_id_desc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="active desc, id desc")
self.assertEqual([ab, b, a, c, e, d], list(active_desc_id_desc), "Search with 'ACTIVE DESC, ID DESC' order failed.")
id_asc_active_asc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id asc, active asc")
self.assertEqual([c, d, a, b, ab, e], list(id_asc_active_asc), "Search with 'ID ASC, ACTIVE ASC' order failed.")
id_asc_active_desc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id asc, active desc")
self.assertEqual([c, d, a, b, ab, e], list(id_asc_active_desc), "Search with 'ID ASC, ACTIVE DESC' order failed.")
id_desc_active_asc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active asc")
self.assertEqual([e, ab, b, a, d, c], list(id_desc_active_asc), "Search with 'ID DESC, ACTIVE ASC' order failed.")
id_desc_active_desc = Partner.search([('name', 'like', 'test_search_order%'), '|', ('active', '=', True), ('active', '=', False)], order="id desc, active desc")
self.assertEqual([e, ab, b, a, d, c], list(id_desc_active_desc), "Search with 'ID DESC, ACTIVE DESC' order failed.")
def test_10_inherits_m2order(self):
registry, cr, uid = self.registry, self.cr, self.uid
users_obj = registry('res.users')
Users = self.env['res.users']
# Find Employee group
group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
group_employee_id = group_employee_ref and group_employee_ref[1] or False
group_employee = self.env.ref('base.group_user')
# Get country/state data
country_us_id = registry('res.country').search(cr, uid, [('code', 'like', 'US')])[0]
state_ids = registry('res.country.state').search(cr, uid, [('country_id', '=', country_us_id)], limit=2)
country_be_id = registry('res.country').search(cr, uid, [('code', 'like', 'BE')])[0]
country_be = self.env.ref('base.be')
country_us = self.env.ref('base.us')
states_us = country_us.state_ids[:2]
# Create test users
search_user = users_obj.create(cr, uid, {'name': '__search', 'login': '__search', 'groups_id': [(6, 0, [group_employee_id])]})
a = users_obj.create(cr, uid, {'name': '__test_A', 'login': '__test_A', 'country_id': country_be_id, 'state_id': country_be_id})
b = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__a_test_B', 'country_id': country_us_id, 'state_id': state_ids[1]})
c = users_obj.create(cr, uid, {'name': '__test_B', 'login': '__z_test_B', 'country_id': country_us_id, 'state_id': state_ids[0]})
u = Users.create({'name': '__search', 'login': '__search', 'groups_id': [(6, 0, [group_employee.id])]})
a = Users.create({'name': '__test_A', 'login': '__test_A', 'country_id': country_be.id, 'state_id': country_be.id})
b = Users.create({'name': '__test_B', 'login': '__a_test_B', 'country_id': country_us.id, 'state_id': states_us[1].id})
c = Users.create({'name': '__test_B', 'login': '__z_test_B', 'country_id': country_us.id, 'state_id': states_us[0].id})
# Search as search user
Users = Users.sudo(u)
# Do: search on res.users, order on a field on res.partner to try inherits'd fields, then res.users
user_ids = users_obj.search(cr, search_user, [], order='name asc, login desc')
expected_ids = [search_user, a, c, b]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
expected_ids = [u.id, a.id, c.id, b.id]
user_ids = Users.search([('id', 'in', expected_ids)], order='name asc, login desc').ids
self.assertEqual(user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one and inherits'd fields
user_ids = users_obj.search(cr, search_user, [], order='state_id asc, country_id desc, name asc, login desc')
expected_ids = [c, b, a, search_user]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
expected_ids = [c.id, b.id, a.id, u.id]
user_ids = Users.search([('id', 'in', expected_ids)], order='state_id asc, country_id desc, name asc, login desc').ids
self.assertEqual(user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one and inherits'd fields
user_ids = users_obj.search(cr, search_user, [], order='country_id desc, state_id desc, name asc, login desc')
expected_ids = [search_user, b, c, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
expected_ids = [u.id, b.id, c.id, a.id]
user_ids = Users.search([('id', 'in', expected_ids)], order='country_id desc, state_id desc, name asc, login desc').ids
self.assertEqual(user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
# Do: order on many2one, but not by specifying in order parameter of search, but by overriding _order of res_users
self.patch_order('res.users', 'country_id desc, name asc, login desc')
user_ids = users_obj.search(cr, search_user, [])
expected_ids = [search_user, c, b, a]
test_user_ids = filter(lambda x: x in expected_ids, user_ids)
self.assertEqual(test_user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
expected_ids = [u.id, c.id, b.id, a.id]
user_ids = Users.search([('id', 'in', expected_ids)]).ids
self.assertEqual(user_ids, expected_ids, 'search on res_users did not provide expected ids or expected order')
def test_11_indirect_inherits_m2o_order(self):
registry, cr, uid = self.registry, self.cr, self.uid
Cron = registry('ir.cron')
Users = registry('res.users')
Cron = self.env['ir.cron']
Users = self.env['res.users']
user_ids = {}
cron_ids = {}
for u in 'BAC':
user_ids[u] = Users.create(cr, uid, {'name': u, 'login': u})
cron_ids[u] = Cron.create(cr, uid, {'name': u, 'user_id': user_ids[u]})
user_ids[u] = Users.create({'name': u, 'login': u}).id
cron_ids[u] = Cron.create({'name': u, 'user_id': user_ids[u]}).id
ids = Cron.search(cr, uid, [('id', 'in', cron_ids.values())], order='user_id')
ids = Cron.search([('id', 'in', cron_ids.values())], order='user_id').ids
expected_ids = [cron_ids[l] for l in 'ABC']
self.assertEqual(ids, expected_ids)
def test_12_m2o_order_loop_self(self):
registry, cr, uid = self.registry, self.cr, self.uid
Cats = registry('ir.module.category')
ids = {}
Cats = self.env['ir.module.category']
cat_ids = {}
def create(name, **kw):
ids[name] = Cats.create(cr, uid, dict(kw, name=name))
cat_ids[name] = Cats.create(dict(kw, name=name)).id
self.patch_order('ir.module.category', 'parent_id desc, name')
create('A')
create('B', parent_id=ids['A'])
create('C', parent_id=ids['A'])
create('B', parent_id=cat_ids['A'])
create('C', parent_id=cat_ids['A'])
create('D')
create('E', parent_id=ids['D'])
create('F', parent_id=ids['D'])
create('E', parent_id=cat_ids['D'])
create('F', parent_id=cat_ids['D'])
expected_order = [ids[x] for x in 'ADEFBC']
domain = [('id', 'in', ids.values())]
search_result = Cats.search(cr, uid, domain)
self.assertEqual(search_result, expected_order)
expected_ids = [cat_ids[x] for x in 'ADEFBC']
found_ids = Cats.search([('id', 'in', cat_ids.values())]).ids
self.assertEqual(found_ids, expected_ids)
def test_13_m2o_order_loop_multi(self):
Users = self.env['res.users']
@@ -154,11 +145,6 @@ class test_search(common.TransactionCase):
u3 = Users.create(dict(name='C', login='c', **kw)).id
u4 = Users.sudo(user=u2).create(dict(name='D', login='z', **kw)).id
expected_order = [u2, u4, u3, u1]
domain = [('id', 'in', [u1, u2, u3, u4])]
search_result = list(Users.search(domain)._ids)
self.assertEqual(search_result, expected_order)
if __name__ == '__main__':
unittest.main()
expected_ids = [u2, u4, u3, u1]
found_ids = Users.search([('id', 'in', expected_ids)]).ids
self.assertEqual(found_ids, expected_ids)
+57 -2
View File
@@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml.etree import XMLSyntaxError
import unittest
from openerp.tools.translate import quote, unquote, xml_translate, html_translate
from odoo.tools.translate import quote, unquote, xml_translate, html_translate
from odoo.tests.common import TransactionCase
class TranslationToolsTestCase(unittest.TestCase):
@@ -167,3 +169,56 @@ class TranslationToolsTestCase(unittest.TestCase):
self.assertEquals(result, """<i class="fa-check"/>""")
result = html_translate(lambda term: term, source)
self.assertEquals(result, source)
class TestTranslation(TransactionCase):
def setUp(self):
super(TestTranslation, self).setUp()
self.env['ir.translation'].load_module_terms(['base'], ['fr_FR'])
self.customers = self.env['res.partner.category'].create({'name': 'Customers'})
self.env['ir.translation'].create({
'type': 'model',
'name': 'res.partner.category,name',
'module':'base',
'lang': 'fr_FR',
'res_id': self.customers.id,
'value': 'Clients',
'state': 'translated',
})
def test_101_create_translated_record(self):
category = self.customers.with_context({})
self.assertEqual(category.name, 'Customers', "Error in basic name_get")
category_fr = category.with_context({'lang': 'fr_FR'})
self.assertEqual(category_fr.name, 'Clients', "Translation not found")
def test_102_duplicate_record(self):
category = self.customers.with_context({'lang': 'fr_FR'}).copy()
category_no = category.with_context({})
self.assertEqual(category_no.name, 'Customers', "Duplication did not set untranslated value")
category_fr = category.with_context({'lang': 'fr_FR'})
self.assertEqual(category_fr.name, 'Clients', "Did not found translation for initial value")
def test_103_duplicate_record_fr(self):
category = self.customers.with_context({'lang': 'fr_FR'}).copy({'name': 'Clients (copie)'})
category_no = category.with_context({})
self.assertEqual(category_no.name, 'Customers', "Duplication erased original untranslated value")
category_fr = category.with_context({'lang': 'fr_FR'})
self.assertEqual(category_fr.name, 'Clients (copie)', "Did not used default value for translated value")
def test_104_orderby_translated_field(self):
""" Test search ordered by a translated field. """
# create a category with a French translation
padawans = self.env['res.partner.category'].create({'name': 'Padawans'})
padawans_fr = padawans.with_context(lang='fr_FR')
padawans_fr.write({'name': 'Apprentis'})
# search for categories, and sort them by (translated) name
categories = padawans_fr.search([('id', 'in', [self.customers.id, padawans.id])], order='name')
self.assertEqual(categories.ids, [padawans.id, self.customers.id],
"Search ordered by translated name should return Padawans (Apprentis) before Customers (Clients)")
+37 -60
View File
@@ -1,57 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
# This assumes an existing but uninitialized database.
from contextlib import contextmanager
import unittest
import openerp
from openerp import SUPERUSER_ID
import common
from openerp import api, registry, SUPERUSER_ID
from openerp.tests import common
from openerp.modules.registry import RegistryManager
ADMIN_USER_ID = common.ADMIN_USER_ID
def registry(model):
return openerp.modules.registry.RegistryManager.get(common.get_db_name())[model]
@contextmanager
def environment():
""" Return an environment with a new cursor for the current database; the
cursor is committed and closed after the context block.
"""
reg = registry(common.get_db_name())
with reg.cursor() as cr:
yield api.Environment(cr, SUPERUSER_ID, {})
cr.commit()
def cursor():
return openerp.modules.registry.RegistryManager.get(common.get_db_name()).cursor()
def get_module(module_name):
registry = openerp.modules.registry.RegistryManager.get(common.get_db_name())
return registry.get(module_name)
MODULE = 'test_uninstall'
MODEL = 'test_uninstall.model'
def reload_registry():
openerp.modules.registry.RegistryManager.new(
common.get_db_name(), update_module=True)
def search_registry(model_name, domain):
cr = cursor()
model = registry(model_name)
record_ids = model.search(cr, SUPERUSER_ID, domain, {})
cr.close()
return record_ids
def install_module(module_name):
ir_module_module = registry('ir.module.module')
cr = cursor()
module_ids = ir_module_module.search(cr, SUPERUSER_ID,
[('name', '=', module_name)], {})
assert len(module_ids) == 1
ir_module_module.button_install(cr, SUPERUSER_ID, module_ids, {})
cr.commit()
cr.close()
reload_registry()
def uninstall_module(module_name):
ir_module_module = registry('ir.module.module')
cr = cursor()
module_ids = ir_module_module.search(cr, SUPERUSER_ID,
[('name', '=', module_name)], {})
assert len(module_ids) == 1
ir_module_module.button_uninstall(cr, SUPERUSER_ID, module_ids, {})
cr.commit()
cr.close()
reload_registry()
class test_uninstall(unittest.TestCase):
class TestUninstall(unittest.TestCase):
"""
Test the install/uninstall of a test module. The module is available in
`openerp.tests` which should be present in the addons-path.
@@ -59,26 +33,29 @@ class test_uninstall(unittest.TestCase):
def test_01_install(self):
""" Check a few things showing the module is installed. """
install_module('test_uninstall')
assert get_module('test_uninstall.model')
with environment() as env:
module = env['ir.module.module'].search([('name', '=', MODULE)])
assert len(module) == 1
module.button_install()
RegistryManager.new(common.get_db_name(), update_module=True)
assert search_registry('ir.model.data',
[('module', '=', 'test_uninstall')])
assert search_registry('ir.model.fields',
[('model', '=', 'test_uninstall.model')])
with environment() as env:
self.assertIn('test_uninstall.model', env.registry)
self.assertTrue(env['ir.model.data'].search([('module', '=', MODULE)]))
self.assertTrue(env['ir.model.fields'].search([('model', '=', MODEL)]))
def test_02_uninstall(self):
""" Check a few things showing the module is uninstalled. """
uninstall_module('test_uninstall')
assert not get_module('test_uninstall.model')
assert not search_registry('ir.model.data',
[('module', '=', 'test_uninstall')])
assert not search_registry('ir.model.fields',
[('model', '=', 'test_uninstall.model')])
with environment() as env:
module = env['ir.module.module'].search([('name', '=', MODULE)])
assert len(module) == 1
module.button_uninstall()
RegistryManager.new(common.get_db_name(), update_module=True)
with environment() as env:
self.assertNotIn('test_uninstall.model', env.registry)
self.assertFalse(env['ir.model.data'].search([('module', '=', MODULE)]))
self.assertFalse(env['ir.model.fields'].search([('model', '=', MODEL)]))
if __name__ == '__main__':
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
import openerp.tests
# Part of Odoo. See LICENSE file for full copyright and licensing details.
class TestHasGroup(openerp.tests.TransactionCase):
from odoo.tests.common import TransactionCase
class TestHasGroup(TransactionCase):
def setUp(self):
super(TestHasGroup, self).setUp()
@@ -1,13 +1,15 @@
# This test can be run stand-alone with something like:
# > PYTHONPATH=. python2 openerp/tests/test_view_validation.py
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from StringIO import StringIO
import unittest
from openerp.tools.view_validation import (valid_page_in_book, valid_att_in_form, valid_type_in_colspan,
valid_type_in_col, valid_att_in_field, valid_att_in_label,
valid_field_in_graph, valid_field_in_tree
)
from odoo.tools.view_validation import (
valid_page_in_book, valid_att_in_form, valid_type_in_colspan,
valid_type_in_col, valid_att_in_field, valid_att_in_label,
valid_field_in_graph, valid_field_in_tree,
)
invalid_form = etree.parse(StringIO('''\
<form>
@@ -92,7 +94,7 @@ valid_tree = etree.parse(StringIO('''\
''')).getroot()
class test_view_validation(unittest.TestCase):
class TestViewValidation(unittest.TestCase):
""" Test the view validation code (but not the views themselves). """
def test_page_validation(self):
@@ -125,7 +127,3 @@ class test_view_validation(unittest.TestCase):
def test_col_datatype_validation(self):
assert not valid_type_in_col(invalid_form)
assert valid_type_in_col(valid_form)
if __name__ == '__main__':
unittest.main()
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -1,21 +1,23 @@
# -*- coding: utf-8 -*-
import openerp.tests.common
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests import common
class test_xmlrpc(openerp.tests.common.HttpCase):
class TestXMLRPC(common.HttpCase):
at_install = False
post_install = True
def test_01_xmlrpc_login(self):
""" Try to login on the common service. """
db_name = openerp.tests.common.get_db_name()
db_name = common.get_db_name()
uid = self.xmlrpc_common.login(db_name, 'admin', 'admin')
self.assertEqual(uid, 1)
def test_xmlrpc_ir_model_search(self):
""" Try a search on the object service. """
o = self.xmlrpc_object
db_name = openerp.tests.common.get_db_name()
db_name = common.get_db_name()
ids = o.execute(db_name, 1, 'admin', 'ir.model', 'search', [])
self.assertIsInstance(ids, list)
ids = o.execute(db_name, 1, 'admin', 'ir.model', 'search', [], {})
+134 -134
View File
@@ -1,183 +1,183 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.exceptions import UserError
from openerp.osv import fields, osv
from openerp.tools.translate import _
import openerp.workflow
from odoo import api, fields, models, _
from odoo.exceptions import UserError
import odoo.workflow
class workflow(osv.osv):
class Workflow(models.Model):
_name = "workflow"
_table = "wkf"
_order = "name"
_columns = {
'name': fields.char('Name', required=True),
'osv': fields.char('Resource Object', required=True,select=True),
'on_create': fields.boolean('On Create', select=True),
'activities': fields.one2many('workflow.activity', 'wkf_id', 'Activities'),
}
_defaults = {
'on_create': lambda *a: True
}
def copy(self, cr, uid, id, values, context=None):
name = fields.Char('Name', required=True)
osv = fields.Char('Resource Object', required=True, index=True)
on_create = fields.Boolean('On Create', default=True, index=True)
activities = fields.One2many('workflow.activity', 'wkf_id', string='Activities')
@api.multi
def copy(self, values):
raise UserError(_("Duplicating workflows is not possible, please create a new workflow"))
def write(self, cr, user, ids, vals, context=None):
if not context:
context={}
openerp.workflow.clear_cache(cr, user)
return super(workflow, self).write(cr, user, ids, vals, context=context)
@api.multi
def write(self, vals):
odoo.workflow.clear_cache(self._cr, self._uid)
return super(Workflow, self).write(vals)
def get_active_workitems(self, cr, uid, res, res_id, context=None):
cr.execute('select * from wkf where osv=%s limit 1',(res,))
@api.model
def get_active_workitems(self, res_model, res_id):
cr = self._cr
cr.execute("SELECT * FROM wkf WHERE osv=%s LIMIT 1", (res_model,))
wkfinfo = cr.dictfetchone()
workitems = []
if wkfinfo:
cr.execute('SELECT id FROM wkf_instance \
WHERE res_id=%s AND wkf_id=%s \
ORDER BY state LIMIT 1',
(res_id, wkfinfo['id']))
query = """ SELECT id FROM wkf_instance
WHERE res_id=%s AND wkf_id=%s
ORDER BY state LIMIT 1 """
cr.execute(query, (res_id, wkfinfo['id']))
inst_id = cr.fetchone()
cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%s group by act_id', (inst_id,))
query = """ SELECT act_id, COUNT(*) FROM wkf_workitem
WHERE inst_id=%s GROUP BY act_id """
cr.execute(query, (inst_id,))
workitems = dict(cr.fetchall())
return {'wkf': wkfinfo, 'workitems': workitems}
return {'wkf': wkfinfo, 'workitems': workitems}
def create(self, cr, user, vals, context=None):
if not context:
context={}
openerp.workflow.clear_cache(cr, user)
return super(workflow, self).create(cr, user, vals, context=context)
@api.model
def create(self, vals):
odoo.workflow.clear_cache(self._cr, self._uid)
return super(Workflow, self).create(vals)
workflow()
class wkf_activity(osv.osv):
class WorkflowActivity(models.Model):
_name = "workflow.activity"
_table = "wkf_activity"
_order = "name"
_columns = {
'name': fields.char('Name', required=True),
'wkf_id': fields.many2one('workflow', 'Workflow', required=True, select=True, ondelete='cascade'),
'split_mode': fields.selection([('XOR', 'Xor'), ('OR','Or'), ('AND','And')], 'Split Mode', size=3, required=True),
'join_mode': fields.selection([('XOR', 'Xor'), ('AND', 'And')], 'Join Mode', size=3, required=True),
'kind': fields.selection([('dummy', 'Dummy'), ('function', 'Function'), ('subflow', 'Subflow'), ('stopall', 'Stop All')], 'Kind', required=True),
'action': fields.text('Python Action'),
'action_id': fields.many2one('ir.actions.server', 'Server Action', ondelete='set null'),
'flow_start': fields.boolean('Flow Start'),
'flow_stop': fields.boolean('Flow Stop'),
'subflow_id': fields.many2one('workflow', 'Subflow'),
'signal_send': fields.char('Signal (subflow.*)'),
'out_transitions': fields.one2many('workflow.transition', 'act_from', 'Outgoing Transitions'),
'in_transitions': fields.one2many('workflow.transition', 'act_to', 'Incoming Transitions'),
}
_defaults = {
'kind': lambda *a: 'dummy',
'join_mode': lambda *a: 'XOR',
'split_mode': lambda *a: 'XOR',
}
def unlink(self, cr, uid, ids, context=None):
if context is None: context = {}
if not context.get('_force_unlink') and self.pool.get('workflow.workitem').search(cr, uid, [('act_id', 'in', ids)]):
name = fields.Char('Name', required=True)
wkf_id = fields.Many2one('workflow', string='Workflow', ondelete='cascade',
required=True, index=True)
split_mode = fields.Selection([('XOR', 'Xor'), ('OR','Or'), ('AND','And')],
string='Split Mode', size=3, required=True, default='XOR')
join_mode = fields.Selection([('XOR', 'Xor'), ('AND', 'And')],
string='Join Mode', size=3, required=True, default='XOR')
kind = fields.Selection([('dummy', 'Dummy'), ('function', 'Function'),
('subflow', 'Subflow'), ('stopall', 'Stop All')],
string='Kind', required=True, default='dummy')
action = fields.Text('Python Action')
action_id = fields.Many2one('ir.actions.server', string='Server Action', ondelete='set null')
flow_start = fields.Boolean('Flow Start')
flow_stop = fields.Boolean('Flow Stop')
subflow_id = fields.Many2one('workflow', string='Subflow')
signal_send = fields.Char('Signal (subflow.*)')
out_transitions = fields.One2many('workflow.transition', 'act_from', string='Outgoing Transitions')
in_transitions = fields.One2many('workflow.transition', 'act_to', string='Incoming Transitions')
@api.multi
def unlink(self):
if not self._context.get('_force_unlink') and \
self.env['workflow.workitem'].search([('act_id', 'in', self.ids)]):
raise UserError(_('Please make sure no workitems refer to an activity before deleting it!'))
super(wkf_activity, self).unlink(cr, uid, ids, context=context)
super(WorkflowActivity, self).unlink()
wkf_activity()
class wkf_transition(osv.osv):
_table = "wkf_transition"
class WorkflowTransition(models.Model):
_name = "workflow.transition"
_table = "wkf_transition"
_rec_name = 'signal'
_columns = {
'trigger_model': fields.char('Trigger Object'),
'trigger_expr_id': fields.char('Trigger Expression'),
'sequence': fields.integer('Sequence'),
'signal': fields.char('Signal (Button Name)',
help="When the operation of transition comes from a button pressed in the client form, "\
"signal tests the name of the pressed button. If signal is NULL, no button is necessary to validate this transition."),
'group_id': fields.many2one('res.groups', 'Group Required',
help="The group that a user must have to be authorized to validate this transition."),
'condition': fields.char('Condition', required=True,
help="Expression to be satisfied if we want the transition done."),
'act_from': fields.many2one('workflow.activity', 'Source Activity', required=True, select=True, ondelete='cascade',
help="Source activity. When this activity is over, the condition is tested to determine if we can start the ACT_TO activity."),
'act_to': fields.many2one('workflow.activity', 'Destination Activity', required=True, select=True, ondelete='cascade',
help="The destination activity."),
'wkf_id': fields.related('act_from','wkf_id', type='many2one', relation='workflow', string='Workflow', select=True),
}
_defaults = {
'condition': lambda *a: 'True',
'sequence': 10,
}
_order = 'sequence,id'
def name_get(self, cr, uid, ids, context=None):
return [(line.id, (line.act_from.name) + '+' + (line.act_to.name)) if line.signal == False else (line.id, line.signal) for line in self.browse(cr, uid, ids, context=context)]
trigger_model = fields.Char('Trigger Object')
trigger_expr_id = fields.Char('Trigger Expression')
sequence = fields.Integer('Sequence', default=10)
signal = fields.Char('Signal (Button Name)',
help="When the operation of transition comes from a button pressed in the client form, "
"signal tests the name of the pressed button. If signal is NULL, no button is necessary to validate this transition.")
group_id = fields.Many2one('res.groups', string='Group Required',
help="The group that a user must have to be authorized to validate this transition.")
condition = fields.Char('Condition', required=True, default='True',
help="Expression to be satisfied if we want the transition done.")
act_from = fields.Many2one('workflow.activity', string='Source Activity',
ondelete='cascade', required=True, index=True,
help="Source activity. When this activity is over, the condition is tested to determine if we can start the ACT_TO activity.")
act_to = fields.Many2one('workflow.activity', string='Destination Activity',
ondelete='cascade', required=True, index=True,
help="The destination activity.")
wkf_id = fields.Many2one('workflow', related='act_from.wkf_id', string='Workflow')
def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
if args is None:
args = []
@api.multi
def name_get(self):
return [
(line.id, line.signal or "%s+%s" % (line.act_from.name, line.act_to.name))
for line in self
]
@api.model
def name_search(self, name, args=None, operator='ilike', limit=100):
if name:
ids = self.search(cr, user, ['|',('act_from', operator, name),('act_to', operator, name)] + args, limit=limit)
return self.name_get(cr, user, ids, context=context)
return super(wkf_transition, self).name_search(cr, user, name, args=args, operator=operator, context=context, limit=limit)
domain = ['|', ('act_from', operator, name), ('act_to', operator, name)] + (args or [])
return self.search(domain, limit=limit).name_get()
return super(WorkflowTransition, self).name_search(name, args, operator, limit=limit)
wkf_transition()
class wkf_instance(osv.osv):
_table = "wkf_instance"
class WorkflowInstance(models.Model):
_name = "workflow.instance"
_table = "wkf_instance"
_rec_name = 'res_type'
_log_access = False
_columns = {
'uid': fields.integer('User'), # FIXME no constraint??
'wkf_id': fields.many2one('workflow', 'Workflow', ondelete='cascade', select=True),
'res_id': fields.integer('Resource ID'),
'res_type': fields.char('Resource Object'),
'state': fields.char('Status'),
'transition_ids': fields.many2many('workflow.transition', 'wkf_witm_trans', 'inst_id', 'trans_id'),
}
def _auto_init(self, cr, context=None):
super(wkf_instance, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_type_res_id_state_index\'')
uid = fields.Integer('User') # FIXME no constraint??
wkf_id = fields.Many2one('workflow', string='Workflow', ondelete='cascade', index=True)
res_id = fields.Integer('Resource ID')
res_type = fields.Char('Resource Object')
state = fields.Char('Status')
transition_ids = fields.Many2many('workflow.transition', 'wkf_witm_trans', 'inst_id', 'trans_id')
@api.model_cr_context
def _auto_init(self):
super(WorkflowInstance, self)._auto_init()
cr = self._cr
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname=%s', ['wkf_instance_res_type_res_id_state_index'])
if not cr.fetchone():
cr.execute('CREATE INDEX wkf_instance_res_type_res_id_state_index ON wkf_instance (res_type, res_id, state)')
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_id_wkf_id_index\'')
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname=%s', ['wkf_instance_res_id_wkf_id_index'])
if not cr.fetchone():
cr.execute('CREATE INDEX wkf_instance_res_id_wkf_id_index ON wkf_instance (res_id, wkf_id)')
wkf_instance()
class wkf_workitem(osv.osv):
_table = "wkf_workitem"
class WorkflowWorkitem(models.Model):
_name = "workflow.workitem"
_log_access = False
_table = "wkf_workitem"
_rec_name = 'state'
_columns = {
'act_id': fields.many2one('workflow.activity', 'Activity', required=True, ondelete="cascade", select=True),
'wkf_id': fields.related('act_id','wkf_id', type='many2one', relation='workflow', string='Workflow'),
'subflow_id': fields.many2one('workflow.instance', 'Subflow', ondelete="set null", select=True),
'inst_id': fields.many2one('workflow.instance', 'Instance', required=True, ondelete="cascade", select=True),
'state': fields.char('Status', select=True),
}
wkf_workitem()
class wkf_triggers(osv.osv):
_table = "wkf_triggers"
_name = "workflow.triggers"
_log_access = False
_columns = {
'res_id': fields.integer('Resource ID', size=128),
'model': fields.char('Object'),
'instance_id': fields.many2one('workflow.instance', 'Destination Instance', ondelete="cascade"),
'workitem_id': fields.many2one('workflow.workitem', 'Workitem', required=True, ondelete="cascade"),
}
def _auto_init(self, cr, context=None):
super(wkf_triggers, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_triggers_res_id_model_index\'')
act_id = fields.Many2one('workflow.activity', string='Activity',
ondelete="cascade", required=True, index=True)
wkf_id = fields.Many2one('workflow', related='act_id.wkf_id', string='Workflow')
subflow_id = fields.Many2one('workflow.instance', string='Subflow',
ondelete="set null", index=True)
inst_id = fields.Many2one('workflow.instance', string='Instance',
ondelete="cascade", required=True, index=True)
state = fields.Char('Status', index=True)
class WorkflowTriggers(models.Model):
_name = "workflow.triggers"
_table = "wkf_triggers"
_log_access = False
res_id = fields.Integer('Resource ID', size=128)
model = fields.Char('Object')
instance_id = fields.Many2one('workflow.instance', string='Destination Instance',
ondelete="cascade")
workitem_id = fields.Many2one('workflow.workitem', string='Workitem',
ondelete="cascade", required=True)
@api.model_cr_context
def _auto_init(self):
super(WorkflowTriggers, self)._auto_init()
cr = self._cr
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname=%s', ['wkf_triggers_res_id_model_index'])
if not cr.fetchone():
cr.execute('CREATE INDEX wkf_triggers_res_id_model_index ON wkf_triggers (res_id, model)')
wkf_triggers()
+2
View File
@@ -1972,6 +1972,7 @@ class Many2many(_RelationalMulti):
'relation': None, # name of table
'column1': None, # column of table referring to model
'column2': None, # column of table referring to comodel
'auto_join': False, # whether joins are generated upon search
'limit': None, # optional limit to use upon read
}
@@ -2014,6 +2015,7 @@ class Many2many(_RelationalMulti):
_column_rel = property(attrgetter('relation'))
_column_id1 = property(attrgetter('column1'))
_column_id2 = property(attrgetter('column2'))
_column_auto_join = property(attrgetter('auto_join'))
_column_limit = property(attrgetter('limit'))
+1 -1
View File
@@ -5,7 +5,7 @@
The main duty of this module is to compile a domain expression into a
SQL query. A lot of things should be documented here, but as a first
step in the right direction, some tests in test_osv_expression.yml
step in the right direction, some tests in test_expression.py
might give you some additional information.
For legacy reasons, a domain uses an inconsistent two-levels abstract
+1
View File
@@ -947,6 +947,7 @@ class many2many(_column):
args['relation'] = self._rel
args['column1'] = self._id1
args['column2'] = self._id2
args['auto_join'] = self._auto_join
args['limit'] = self._limit
return args