From 1a1e3463023947db05d6c0e9b371e161253d4cbf Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 4 Mar 2010 10:44:58 +0100 Subject: [PATCH 01/99] [IMP] yaml test: init bzr revid: jth@openerp.com-20100304094458-oru0mdc523veqsbp --- bin/addons/__init__.py | 2 + bin/netsvc.py | 12 +- bin/tools/__init__.py | 1 + bin/tools/yaml_test.py | 406 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 bin/tools/yaml_test.py diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index 2910b9fe7c1..ebf0c2fcd05 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -644,6 +644,8 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): new_query = ' '.join(query.split()) if new_query: cr.execute(new_query) + elif ext == '.yaml': + tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs) else: tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs) fp.close() diff --git a/bin/netsvc.py b/bin/netsvc.py index d8548db5d6a..eae53826a74 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -127,6 +127,7 @@ class ExportService(object): LOG_NOTSET = 'notset' LOG_DEBUG_RPC = 'debug_rpc' LOG_DEBUG = 'debug' +LOG_TEST = 'test' LOG_INFO = 'info' LOG_WARNING = 'warn' LOG_ERROR = 'error' @@ -135,6 +136,9 @@ LOG_CRITICAL = 'critical' logging.DEBUG_RPC = logging.DEBUG - 2 logging.addLevelName(logging.DEBUG_RPC, 'DEBUG_RPC') +logging.TEST = logging.INFO - 5 +logging.addLevelName(logging.TEST, 'TEST') + def init_logger(): import os from tools.translate import resetlocale @@ -195,6 +199,7 @@ def init_logger(): 'DEBUG_RPC': ('blue', 'white'), 'DEBUG': ('blue', 'default'), 'INFO': ('green', 'default'), + 'TEST': ('white', 'blue'), 'WARNING': ('yellow', 'default'), 'ERROR': ('red', 'default'), 'CRITICAL': ('white', 'red'), @@ -220,9 +225,10 @@ class Logger(object): log = logging.getLogger(tools.ustr(name)) - if level == LOG_DEBUG_RPC and not hasattr(log, level): - fct = lambda msg, *args, **kwargs: log.log(logging.DEBUG_RPC, msg, *args, **kwargs) - setattr(log, LOG_DEBUG_RPC, fct) + if level in [LOG_DEBUG_RPC, LOG_TEST] and not hasattr(log, level): + fct = lambda msg, *args, **kwargs: log.log(getattr(logging, level.upper()), msg, *args, **kwargs) + setattr(log, level, fct) + level_method = getattr(log, level) diff --git a/bin/tools/__init__.py b/bin/tools/__init__.py index 9b7bb84a57f..78c77edf394 100644 --- a/bin/tools/__init__.py +++ b/bin/tools/__init__.py @@ -24,6 +24,7 @@ import win32 from config import * from misc import * from convert import * +from yaml_test import * from translate import * from graph import graph from amount_to_text import * diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py new file mode 100644 index 00000000000..52ce059a5fd --- /dev/null +++ b/bin/tools/yaml_test.py @@ -0,0 +1,406 @@ +# -*- encoding: utf-8 -*- +import types +import yaml +import time # used to eval time.strftime expressions +import logging + +import pooler +import netsvc + +from config import config + +logger_channel = 'tests' + +class YamlImportException(Exception): + pass + +class YamlImportAbortion(Exception): + pass + +class YamlTag(object): + """Superclass for constructors of custom tags defined in yaml file.""" + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + def __getitem__(self, key): + return getattr(self, key) + def __getattr__(self, attr): + return None + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items())) + +class Assert(YamlTag): + def __init__(self, model, id, severity=netsvc.LOG_ERROR, string="NONAME", **kwargs): + self.model = model + self.id = id + self.severity = severity + self.string = string + super(Assert, self).__init__(**kwargs) + +class Record(YamlTag): + def __init__(self, model, id, **kwargs): + self.model = model + self.id = id + super(Record, self).__init__(**kwargs) + +class Python(YamlTag): + def __init__(self, model, severity=netsvc.LOG_ERROR, name="NONAME", **kwargs): + self.model= model + self.severity = severity + self.name = name + super(Python, self).__init__(**kwargs) + +class Workflow(YamlTag): + def __init__(self, model, action, **kwargs): + self.model = model + self.action = action + super(Workflow, self).__init__(**kwargs) + +class Context(YamlTag): + def __init__(self, **kwargs): + super(Context, self).__init__(**kwargs) + +class Eval(YamlTag): + def __init__(self, expression): + self.expression = expression + super(Eval, self).__init__() + +class Ref(YamlTag): + def __init__(self, xmlid): + self.xmlid = xmlid + super(Ref, self).__init__() + +def assert_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Assert(**kwargs) + +def record_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Record(**kwargs) + +def python_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Python(**kwargs) + +def workflow_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Workflow(**kwargs) + +def context_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Context(**kwargs) + +def eval_constructor(loader, node): + expression = loader.construct_scalar(node) + return Eval(expression) + +def ref_constructor(loader, node): + xmlid = loader.construct_scalar(node) + return Ref(xmlid) + +yaml.add_constructor(u"!assert", assert_constructor) +yaml.add_constructor(u"!record", record_constructor) +yaml.add_constructor(u"!python", python_constructor) +yaml.add_constructor(u"!workflow", workflow_constructor) +yaml.add_constructor(u"!context", context_constructor) +yaml.add_constructor(u"!eval", eval_constructor) +yaml.add_constructor(u"!ref", ref_constructor) + +def _is_yaml_mapping(node, tag_constructor): + value = isinstance(node, types.DictionaryType) \ + and len(node.keys()) == 1 \ + and isinstance(node.keys()[0], tag_constructor) + return value + +def is_comment(node): + return isinstance(node, types.StringTypes) + +def is_assert(node): + return _is_yaml_mapping(node, Assert) + +def is_record(node): + return _is_yaml_mapping(node, Record) + +def is_python(node): + return _is_yaml_mapping(node, Python) + +def is_workflow(node): + return isinstance(node, Workflow) \ + or _is_yaml_mapping(node, Workflow) + +def is_context(node): + return isinstance(node, Context) + +def is_eval(node): + return isinstance(node, Eval) + +def is_ref(node): + return isinstance(node, Ref) + + +class TestReport(object): + def __init__(self): + self._report = {} + + def record(self, success, severity): + """Records the result of an assertion for the failed/success count. + Returns success.""" + + if severity in self._report: + self._report[severity][success] += 1 + else: + self._report[severity] = {success: 1, not success: 0} + return success + + def __str__(self): + res = [] + res.append('\nAssertions report:\nLevel\tsuccess\tfailed') + success = failure = 0 + for severity in self._report: + res.append("%s\t%s\t%s" % (severity, self._report[severity][True], self._report[severity][False])) + success += self._report[severity][True] + failure += self._report[severity][False] + res.append("total\t%s\t%s" % (success, failure)) + res.append("end of report (%s assertion(s) checked)" % success + failure) + return "\n".join(res) + + +class YamlInterpreter(object): + def __init__(self, cr, module, id_map, mode, filename, noupdate=False): + self.cr = cr + self.module = module + self.id_map = id_map + self.mode = mode + self.filename = filename + self.assert_report = TestReport() + self.noupdate = noupdate + self.logger = netsvc.Logger() + self.pool = pooler.get_pool(cr.dbname) + self.uid = 1 + self.context = {} + + def _ref(self): + return lambda xml_id: self.get_id(xml_id) + + def get_model(self, model_name): + model = self.pool.get(model_name) + assert model, "The model %s does not exist." % (model_name,) + return model + + def validate_xml_id(self, xml_id): + id = xml_id + if '.' in xml_id: + module, id = xml_id.split('.', 1) + assert '.' not in id, "The ID reference '%s' must contains maximum one dot.\n" \ + "It is used to refer to other modules ID, in the form: module.record_id" \ + % (xml_id,) + if module != self.module: + module_count = self.pool.get('ir.module.module').search_count(self.cr, self.uid, \ + ['&', ('name', '=', module), ('state', 'in', ['installed'])]) + assert module_count == 1, """The ID "%s" refers to an uninstalled module""" % (xml_id,) + if len(id) > 64: # TODO where does 64 come from (DB is 128)? should be a constant or loaded form DB + self.logger.notifyChannel(logger_channel, netsvc.LOG_ERROR, 'id: %s is to long (max: 64)' % (id,)) + + def get_id(self, xml_id): + if isinstance(xml_id, types.IntType): + id = xml_id + elif xml_id in self.id_map: + id = self.id_map[xml_id] + else: + if '.' in xml_id: + module, xml_id = xml_id.split('.', 1) + else: + module = self.module + ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, xml_id) + obj = self.pool.get('ir.model.data').read(self.cr, self.uid, ir_id, ['res_id']) + id = int(obj['res_id']) + self.id_map[xml_id] = id + return id + + def process_comment(self, node): + return node + + def _log_assert_failure(self, severity, msg, *args): + self.assert_report.record(False, severity) + self.logger.notifyChannel(logger_channel, severity, msg % (args)) + if getattr(logging, severity.upper()) >= config['assert_exit_level']: + raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % (severity,)) + return + + def _get_assertion_id(self, assertion): + if assertion.id: + ids = [self.get_id(assertion.id)] + elif assertion.search: + q = eval(assertion.search, {'ref': self._ref}) + ids = self.pool.get(assertion.model).search(self.cr, self.uid, q, context=assertion.context) + if not ids: + raise YamlImportException('Nothing to assert: you must give either an id or a search criteria.') + return ids + + def process_assert(self, node): + assertion, expressions = node.items()[0] + + model = self.get_model(assertion.model) + ids = self._get_assertion_id(assertion) + if assertion.count and len(ids) != assertion.count: + msg = 'assertion "%s" failed!\n' \ + ' Incorrect search count:\n' \ + ' expected count: %d\n' \ + ' obtained count: %d\n' + args = (assertion.string, assertion.count, len(ids)) + self._log_assert_failure(assertion.severity, msg, *args) + else: + test_context = {'ref': self._ref, '_ref': self._ref} # added '_ref' so that record['ref'] is possible + for id in ids: + record = model.browse(self.cr, self.uid, id, assertion.context) + for test in expressions.get('test', ''): + success = eval(test, test_context, record) + if not success: + msg = 'Assertion "%s" FAILED\ntest: %s\n' + args = (assertion.string, test) + self._log_assert_failure(assertion.severity, msg, *args) + return + else: # all tests were successful for this assertion tag (no break) + self.assert_report.record(True, assertion.severity) + + def process_record(self, node): + record, fields = node.items()[0] + + self.validate_xml_id(record.id) + model = self.get_model(record.model) + record_dict = self._create_record(model, fields) + id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \ + self.module, record_dict, record.id, mode=self.mode) + self.id_map[record.id] = int(id) + if config.get('import_partial', False): + self.cr.commit() + + def _create_record(self, model, fields): + record_dict = {} + for field_name, expression in fields.items(): + field_value = self._eval_field(model, field_name, expression) + record_dict[field_name] = field_value + return record_dict + + def _eval_field(self, model, field_name, expression): + column = model._columns[field_name] + if column._type == "many2one": + value = self.get_id(expression) + elif column._type == "one2many": + other_model = self.get_model(column._obj) + value = [(0, 0, self._create_record(other_model, fields)) for fields in expression] + elif column._type == "many2many": + ids = [self.get_id(xml_id) for xml_id in expression] + value= [(6, 0, ids)] + else: # scalar field + if isinstance(expression, Eval): + value = eval(expression.expression) + else: + value = expression + # raise YamlImportException('Unsupported column "%s" or value %s:%s' % (field_name, type(expression), expression)) + return value + + def process_context(self, node): + self.context = node.__dict__ + if 'uid' in node.__dict__: + self.uid = self.get_id(node.__dict__['uid']) + + def process_python(self, node): + def log(msg): + self.logger.notifyChannel(logger_channel, netsvc.LOG_TEST, msg) + python, statements = node.items()[0] + model = self.get_model(python.model) + statements = statements.replace("\r\n", "\n").replace("\n", ";") + code_context = {'self': model, 'cr': self.cr, 'uid': self.uid, 'log': log, 'context': self.context} + try: + code = compile(statements, self.filename, 'exec') + eval(code, code_context) + except Exception, e: + raise YamlImportException(e) + # TODO log success/failure + + def process_workflow(self, node): + workflow, values = node.items()[0] + model = self.get_model(workflow.model) + if workflow.ref: + id = self.get_id(workflow.ref) + else: + if not values: + raise YamlImportException('You must define a child node if you do not give a ref.') + if not len(values) == 1: + raise YamlImportException('Only one child node is accepted (%d given).' % len(values)) + value = values[0] + if not 'model' in value and not 'eval' in value: + raise YamlImportException('You must provide a model and an expression to evaluate for the workflow.') + value_model = self.get_model(value['model']) + local_context = {'ref': self._ref, '_ref': self._ref} + local_context['obj'] = lambda x: value_model.browse(self.cr, self.uid, x, context=self.context) + local_context.update(self.id_map) + id = eval(value['eval'], local_context) + + if workflow.uid is not None: + uid = workflow.uid + else: + uid = self.uid + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, model, id, workflow.action, self.cr) + + def process(self, yaml_string): + """Processes a Yaml string. Custom tags are interpreted by 'process_' instance methods.""" + + is_preceded_by_comment = False + for node in yaml.load(yaml_string): + is_preceded_by_comment = self._log(node, is_preceded_by_comment) + try: + self._process_node(node) + except YamlImportException, e: + self.logger.notifyChannel(logger_channel, netsvc.LOG_ERROR, e) + except YamlImportAbortion, e: + self.logger.notifyChannel(logger_channel, netsvc.LOG_ERROR, e) + return + + def _process_node(self, node): + if is_comment(node): + self.process_comment(node) + elif is_assert(node): + self.process_assert(node) + elif is_record(node): + self.process_record(node) + elif is_python(node): + self.process_python(node) + elif is_context(node): + self.process_context(node) + elif is_workflow(node): + if isinstance(node, types.DictionaryType): + self.process_workflow(node) + else: + self.process_workflow({node: []}) + else: + raise YamlImportException("Can not process YAML block: %s" % node) + + def _log(self, node, is_preceded_by_comment): + if is_comment(node): + is_preceded_by_comment = True + self.logger.notifyChannel(logger_channel, netsvc.LOG_TEST, node) + elif not is_preceded_by_comment: + if isinstance(node, types.DictionaryType): + k, v = node.items()[0] + msg = "Creating %s\n with %s" % (k, v) + self.logger.notifyChannel(logger_channel, netsvc.LOG_TEST, msg) + else: + self.logger.notifyChannel(logger_channel, netsvc.LOG_TEST, node) + else: + is_preceded_by_comment = False + return is_preceded_by_comment + +def yaml_import(cr, module, yamlfile, idref=None, mode='init', noupdate=False, report=None): + if idref is None: + idref = {} + yaml_string = yamlfile.read() + yaml_interpreter = YamlInterpreter(cr, module, idref, mode, filename=yamlfile.name, noupdate=noupdate) + yaml_interpreter.process(yaml_string) + +# keeps convention of convert.py +convert_yaml_import = yaml_import + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From b8b60d44363c0062e0588178dc02d641baacb5e3 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 4 Mar 2010 11:03:55 +0100 Subject: [PATCH 02/99] [FIX] YAML files have '.yml' extension, not '.yaml' bzr revid: jth@openerp.com-20100304100355-0je51f1c0261b25u --- bin/addons/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index ebf0c2fcd05..8137a7fecbb 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -644,7 +644,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): new_query = ' '.join(query.split()) if new_query: cr.execute(new_query) - elif ext == '.yaml': + elif ext == '.yml': tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs) else: tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs) From b8ebcf689bb31ca3cbefbd954c9d742c0a59c4e0 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 4 Mar 2010 16:41:59 +0100 Subject: [PATCH 03/99] [IMP] Record python block success in assertion report. bzr revid: jth@openerp.com-20100304154159-m1i6zuqsvg08s1vi --- bin/tools/yaml_test.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py index 52ce059a5fd..b91e457791e 100644 --- a/bin/tools/yaml_test.py +++ b/bin/tools/yaml_test.py @@ -315,9 +315,15 @@ class YamlInterpreter(object): try: code = compile(statements, self.filename, 'exec') eval(code, code_context) + except AssertionError, e: + msg = 'Assertion "%s" FAILED in Python code.' + args = (python.name,) + self._log_assert_failure(python.severity, msg, *args) + return except Exception, e: - raise YamlImportException(e) - # TODO log success/failure + raise YamlImportAbortion(e) + else: + self.assert_report.record(True, python.severity) def process_workflow(self, node): workflow, values = node.items()[0] From af48a29843cabc84880e3fdbd35dd7360a0d8ff1 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 5 Mar 2010 10:12:18 +0100 Subject: [PATCH 04/99] [IMP] Added more syntax. bzr revid: jth@openerp.com-20100305091218-2t25717pfnw6iie8 --- bin/tools/yaml_test.py | 284 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 279 insertions(+), 5 deletions(-) diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py index b91e457791e..5cee9b92d5e 100644 --- a/bin/tools/yaml_test.py +++ b/bin/tools/yaml_test.py @@ -50,16 +50,45 @@ class Python(YamlTag): self.name = name super(Python, self).__init__(**kwargs) +class Menuitem(YamlTag): + def __init__(self, id, name, **kwargs): + self.id = id + self.name = name + super(Menuitem, self).__init__(**kwargs) + class Workflow(YamlTag): def __init__(self, model, action, **kwargs): self.model = model self.action = action super(Workflow, self).__init__(**kwargs) +class ActWindow(YamlTag): + def __init__(self, model, action, **kwargs): + self.model = model + self.action = action + super(Workflow, self).__init__(**kwargs) + +class Function(YamlTag): + def __init__(self, model, name, **kwargs): + self.model = model + self.name = name + super(Function, self).__init__(**kwargs) + +class Delete(YamlTag): + def __init__(self, model, id, search, **kwargs): + self.model = model + self.id = id + self.search = search + super(Delete, self).__init__(**kwargs) + class Context(YamlTag): def __init__(self, **kwargs): super(Context, self).__init__(**kwargs) +class Url(YamlTag): + def __init__(self, **kwargs): + super(Url, self).__init__(**kwargs) + class Eval(YamlTag): def __init__(self, expression): self.expression = expression @@ -82,14 +111,34 @@ def python_constructor(loader, node): kwargs = loader.construct_mapping(node) return Python(**kwargs) +def menuitem_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Menuitem(**kwargs) + def workflow_constructor(loader, node): kwargs = loader.construct_mapping(node) return Workflow(**kwargs) +def act_window_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return ActWindow(**kwargs) + +def function_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Function(**kwargs) + +def delete_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Delete(**kwargs) + def context_constructor(loader, node): kwargs = loader.construct_mapping(node) return Context(**kwargs) +def url_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Url(**kwargs) + def eval_constructor(loader, node): expression = loader.construct_scalar(node) return Eval(expression) @@ -101,8 +150,13 @@ def ref_constructor(loader, node): yaml.add_constructor(u"!assert", assert_constructor) yaml.add_constructor(u"!record", record_constructor) yaml.add_constructor(u"!python", python_constructor) +yaml.add_constructor(u"!menuitem", menuitem_constructor) yaml.add_constructor(u"!workflow", workflow_constructor) +yaml.add_constructor(u"!act_window", act_window_constructor) +yaml.add_constructor(u"!function", function_constructor) yaml.add_constructor(u"!context", context_constructor) +yaml.add_constructor(u"!delete", delete_constructor) +yaml.add_constructor(u"!url", url_constructor) yaml.add_constructor(u"!eval", eval_constructor) yaml.add_constructor(u"!ref", ref_constructor) @@ -124,13 +178,29 @@ def is_record(node): def is_python(node): return _is_yaml_mapping(node, Python) +def is_menuitem(node): + return isinstance(node, Menuitem) \ + or _is_yaml_mapping(node, Menuitem) + +def is_function(node): + return isinstance(node, Function) \ + or _is_yaml_mapping(node, Function) + def is_workflow(node): - return isinstance(node, Workflow) \ - or _is_yaml_mapping(node, Workflow) + return isinstance(node, Workflow) + +def is_act_window(node): + return isinstance(node, ActWindow) + +def is_delete(node): + return isinstance(node, Delete) def is_context(node): return isinstance(node, Context) +def is_url(node): + return isinstance(node, Url) + def is_eval(node): return isinstance(node, Eval) @@ -154,7 +224,7 @@ class TestReport(object): def __str__(self): res = [] - res.append('\nAssertions report:\nLevel\tsuccess\tfailed') + res.append('\nAssertions report:\nLevel\tsuccess\tfailure') success = failure = 0 for severity in self._report: res.append("%s\t%s\t%s" % (severity, self._report[severity][True], self._report[severity][False])) @@ -217,6 +287,12 @@ class YamlInterpreter(object): self.id_map[xml_id] = id return id + def get_context(self, node, locals_dict): + context = self.context.copy() + if node.context: + context.update(eval(node.context, locals_dict)) + return context + def process_comment(self, node): return node @@ -336,8 +412,8 @@ class YamlInterpreter(object): if not len(values) == 1: raise YamlImportException('Only one child node is accepted (%d given).' % len(values)) value = values[0] - if not 'model' in value and not 'eval' in value: - raise YamlImportException('You must provide a model and an expression to evaluate for the workflow.') + if not 'model' in value and (not 'eval' in value or not 'search' in value): + raise YamlImportException('You must provide a "model" and an "eval" or "search" to evaluate.') value_model = self.get_model(value['model']) local_context = {'ref': self._ref, '_ref': self._ref} local_context['obj'] = lambda x: value_model.browse(self.cr, self.uid, x, context=self.context) @@ -350,7 +426,192 @@ class YamlInterpreter(object): uid = self.uid wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, model, id, workflow.action, self.cr) + + def process_function(self, node): + if self.mode != 'init': + return + function, values = node.items()[0] + local_context = {'ref': self._ref, '_ref': self._ref} + context = self.get_context(node, local_context) + args = [] + if function.eval: + args = eval(function.eval, local_context) + for value in values: + if not 'model' in value and (not 'eval' in value or not 'search' in value): + raise YamlImportException('You must provide a "model" and an "eval" or "search" to evaluate.') + value_model = self.get_model(value['model']) + local_context = {'ref': self._ref, '_ref': self._ref} + local_context['obj'] = lambda x: value_model.browse(self.cr, self.uid, x, context=context) + local_context.update(self.id_map) + id = eval(value['eval'], local_context) + if id != None: + args.append(id) + model = self.get_model(function.model) + method = function.name + getattr(model, method)(self.cr, self.uid, *args) + def _set_group_values(self, node, values): + if node.groups: + group_names = node.groups.split(',') + groups_value = [] + for group in group_names: + if group.startswith('-'): + group_id = self.get_id(group[1:]) + groups_value.append((3, group_id)) + else: + group_id = self.get_id(group) + groups_value.append((4, group_id)) + values['groups_id'] = groups_value + + def process_menuitem(self, node): + self.validate_xml_id(node.id) + + if not node.parent: + parent_id = False + self.cr.execute('select id from ir_ui_menu where parent_id is null and name=%s', (node.name,)) + res = self.cr.fetchone() + values = {'parent_id': parent_id, 'name': node.name} + else: + parent_id = self.get_id(node.parent) + values = {'parent_id': parent_id} + if node.name: + values['name'] = node.name + try: + res = [ self.get_id(node.id) ] + except: # which exception ? + res = None + + if node.action: + action_type = node.type or 'act_window' + icons = { + "act_window": 'STOCK_NEW', + "report.xml": 'STOCK_PASTE', + "wizard": 'STOCK_EXECUTE', + "url": 'STOCK_JUMP_TO', + } + values['icon'] = icons.get(action_type, 'STOCK_NEW') + if action_type == 'act_window': + action_id = self.get_id(node.action) + self.cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%s', (action_id,)) + ir_act_window_result = self.cr.fetchone() + assert ir_act_window_result, "No window action defined for this id %s !\n" \ + "Verify that this is a window action or add a type argument." % (node.action,) + action_type, action_mode, action_name, view_id, target = ir_act_window_result + if view_id: + self.cr.execute('SELECT type FROM ir_ui_view WHERE id=%s', (view_id,)) + # TODO guess why action_mode is ir_act_window.view_mode above and ir_ui_view.type here + action_mode = self.cr.fetchone() + self.cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%s ORDER BY sequence LIMIT 1', (action_id,)) + if self.cr.rowcount: + action_mode = self.cr.fetchone() + if action_type == 'tree': + values['icon'] = 'STOCK_INDENT' + elif action_mode and action_mode.startswith('tree'): + values['icon'] = 'STOCK_JUSTIFY_FILL' + elif action_mode and action_mode.startswith('graph'): + values['icon'] = 'terp-graph' + elif action_mode and action_mode.startswith('calendar'): + values['icon'] = 'terp-calendar' + if target == 'new': + values['icon'] = 'STOCK_EXECUTE' + if not values.get('name', False): + values['name'] = action_name + elif action_type == 'wizard': + action_id = self.get_id(node.action) + self.cr.execute('select name from ir_act_wizard where id=%s', (action_id,)) + ir_act_wizard_result = self.cr.fetchone() + if (not values.get('name', False)) and ir_act_wizard_result: + values['name'] = ir_act_wizard_result[0] + else: + raise YamlImportException("Unsupported type '%s' in menuitem tag." % action_type) + if node.sequence: + values['sequence'] = node.sequence + if node.icon: + values['icon'] = node.icon + + self._set_group_values(node, values) + + pid = self.pool.get('ir.model.data')._update(self.cr, self.uid, \ + 'ir.ui.menu', self.module, values, node.id, mode=self.mode, \ + res_id=res and res[0] or False) + + if node.id and parent_id: + self.id_map[node.id] = int(parent_id) + + if node.action and pid: + action_type = node.type or 'act_window' + action_id = self.get_id(node.action) + action = "ir.actions.%s,%d" % (action_type, action_id) + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, 'action', \ + 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(parent_id))], action, True, True, xml_id=node.id) + + def process_act_window(self, node): + self.validate_xml_id(node.id) + view_id = False + if node.view: + view_id = self.get_id(node.view) + context = eval(node.context, {'ref': self._ref, '_ref': self._ref}) + + values = { + 'name': node.name, + 'type': type or 'ir.actions.act_window', + 'view_id': view_id, + 'domain': node.domain, + 'context': context, + 'res_model': node.res_model, + 'src_model': node.src_model, + 'view_type': node.view_type or 'form', + 'view_mode': node.view_mode or 'tree,form', + 'usage': node.usage, + 'limit': node.limit, + 'auto_refresh': node.auto_refresh, + } + + self._set_group_values(node, values) + + if node.target: + values['target'] = node.target + id = self.pool.get('ir.model.data')._update(self.cr, self.uid, \ + 'ir.actions.act_window', self.module, values, node.id, mode=self.mode) + self.id_map[node.id] = int(id) + + if node.src_model: + keyword = 'client_action_relate' + value = 'ir.actions.act_window,%s' % id + replace = node.replace or True + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, 'action', \ + keyword, node.id, [node.src_model], value, replace=replace, isobject=True, xml_id=node.id) + # TODO add remove ir.model.data + + def process_delete(self, node): + ids = [] + if len(node.search): + ids = self.pool.get(node.model).search(self.cr, self.uid, eval(node.search)) + if len(node.id): + try: + ids.append(self.get_id(node.id)) + except: + pass + if len(ids): + self.pool.get(node.model).unlink(self.cr, self.uid, ids) + self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids, direct=True) + + def process_url(self, node): + self.validate_xml_id(node.id) + + res = {'name': node.name, 'url': node.url, 'target': node.target} + + id = self.pool.get('ir.model.data')._update(self.cr, self.uid, \ + "ir.actions.url", self.module, res, node.id, mode=self.mode) + self.id_map[node.id] = int(id) + # ir_set + if (not node.menu or eval(node.menu)) and id: + keyword = node.keyword or 'client_action_multi' + value = 'ir.actions.url,%s' % id + replace = node.replace or True + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, \ + 'action', keyword, node.url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=node.id) + def process(self, yaml_string): """Processes a Yaml string. Custom tags are interpreted by 'process_' instance methods.""" @@ -374,13 +635,26 @@ class YamlInterpreter(object): self.process_record(node) elif is_python(node): self.process_python(node) + elif is_menuitem(node): + self.process_menuitem(node) + elif is_delete(node): + self.process_delete(node) + elif is_url(node): + self.process_url(node) elif is_context(node): self.process_context(node) + elif is_act_window(node): + self.process_act_window(node) elif is_workflow(node): if isinstance(node, types.DictionaryType): self.process_workflow(node) else: self.process_workflow({node: []}) + elif is_function(node): + if isinstance(node, types.DictionaryType): + self.process_function(node) + else: + self.process_function({node: []}) else: raise YamlImportException("Can not process YAML block: %s" % node) From 2b3611cb376cd49c57243e74387b0facedd4f537 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 5 Mar 2010 10:37:22 +0100 Subject: [PATCH 05/99] [IMP] Added ir_set. [FIX] ActWindow __init__ bzr revid: jth@openerp.com-20100305093722-t1ndcr0v8preofkw --- bin/tools/yaml_test.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py index 5cee9b92d5e..442370ab5dc 100644 --- a/bin/tools/yaml_test.py +++ b/bin/tools/yaml_test.py @@ -66,7 +66,7 @@ class ActWindow(YamlTag): def __init__(self, model, action, **kwargs): self.model = model self.action = action - super(Workflow, self).__init__(**kwargs) + super(ActWindow, self).__init__(**kwargs) class Function(YamlTag): def __init__(self, model, name, **kwargs): @@ -94,10 +94,9 @@ class Eval(YamlTag): self.expression = expression super(Eval, self).__init__() -class Ref(YamlTag): - def __init__(self, xmlid): - self.xmlid = xmlid - super(Ref, self).__init__() +class IrSet(YamlTag): + def __init__(self): + super(IrSet, self).__init__() def assert_constructor(loader, node): kwargs = loader.construct_mapping(node) @@ -143,9 +142,9 @@ def eval_constructor(loader, node): expression = loader.construct_scalar(node) return Eval(expression) -def ref_constructor(loader, node): - xmlid = loader.construct_scalar(node) - return Ref(xmlid) +def ir_set_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return IrSet(**kwargs) yaml.add_constructor(u"!assert", assert_constructor) yaml.add_constructor(u"!record", record_constructor) @@ -158,7 +157,7 @@ yaml.add_constructor(u"!context", context_constructor) yaml.add_constructor(u"!delete", delete_constructor) yaml.add_constructor(u"!url", url_constructor) yaml.add_constructor(u"!eval", eval_constructor) -yaml.add_constructor(u"!ref", ref_constructor) +yaml.add_constructor(u"!ir_set", ir_set_constructor) def _is_yaml_mapping(node, tag_constructor): value = isinstance(node, types.DictionaryType) \ @@ -204,8 +203,8 @@ def is_url(node): def is_eval(node): return isinstance(node, Eval) -def is_ref(node): - return isinstance(node, Ref) +def is_ir_set(node): + return _is_yaml_mapping(node, IrSet) class TestReport(object): @@ -611,6 +610,21 @@ class YamlInterpreter(object): replace = node.replace or True self.pool.get('ir.model.data').ir_set(self.cr, self.uid, \ 'action', keyword, node.url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=node.id) + + def process_ir_set(self, node): + if not self.mode == 'init': + return False + _, fields = node.items()[0] + res = {} + for fieldname, expression in fields.items(): + if isinstance(expression, Eval): + value = eval(expression.expression, {'ref': self._ref, '_ref': self._ref}) + else: + value = expression + res[fieldname] = value + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, res['key'], res['key2'], \ + res['name'], res['models'], res['value'], replace=res.get('replace',True), \ + isobject=res.get('isobject', False), meta=res.get('meta',None)) def process(self, yaml_string): """Processes a Yaml string. Custom tags are interpreted by 'process_' instance methods.""" @@ -643,6 +657,8 @@ class YamlInterpreter(object): self.process_url(node) elif is_context(node): self.process_context(node) + elif is_ir_set(node): + self.process_ir_set(node) elif is_act_window(node): self.process_act_window(node) elif is_workflow(node): From 88635654ce8aa65af2e0d311178c356dd8e2cd42 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 5 Mar 2010 11:05:37 +0100 Subject: [PATCH 06/99] [FIX] fixed missing isnoupdate and get_context bzr revid: jth@openerp.com-20100305100537-fnf2dkkylpylrwhq --- bin/tools/yaml_test.py | 46 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py index 442370ab5dc..d284824e1c1 100644 --- a/bin/tools/yaml_test.py +++ b/bin/tools/yaml_test.py @@ -291,6 +291,9 @@ class YamlInterpreter(object): if node.context: context.update(eval(node.context, locals_dict)) return context + + def isnoupdate(self, node): + return self.noupdate or node.noupdate or False def process_comment(self, node): return node @@ -315,6 +318,8 @@ class YamlInterpreter(object): def process_assert(self, node): assertion, expressions = node.items()[0] + if self.isnoupdate(assertion) and self.mode != 'init': + return model = self.get_model(assertion.model) ids = self._get_assertion_id(assertion) if assertion.count and len(ids) != assertion.count: @@ -325,11 +330,12 @@ class YamlInterpreter(object): args = (assertion.string, assertion.count, len(ids)) self._log_assert_failure(assertion.severity, msg, *args) else: - test_context = {'ref': self._ref, '_ref': self._ref} # added '_ref' so that record['ref'] is possible + local_context = {'ref': self._ref, '_ref': self._ref} # added '_ref' so that record['ref'] is possible + context = self.get_context(assertion, local_context) for id in ids: - record = model.browse(self.cr, self.uid, id, assertion.context) + record = model.browse(self.cr, self.uid, id, context) for test in expressions.get('test', ''): - success = eval(test, test_context, record) + success = eval(test, local_context, record) if not success: msg = 'Assertion "%s" FAILED\ntest: %s\n' args = (assertion.string, test) @@ -342,13 +348,14 @@ class YamlInterpreter(object): record, fields = node.items()[0] self.validate_xml_id(record.id) - model = self.get_model(record.model) - record_dict = self._create_record(model, fields) - id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \ - self.module, record_dict, record.id, mode=self.mode) - self.id_map[record.id] = int(id) - if config.get('import_partial', False): - self.cr.commit() + if self.isnoupdate(record) and self.mode != 'init': + model = self.get_model(record.model) + record_dict = self._create_record(model, fields) + id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \ + self.module, record_dict, record.id, noupdate=self.isnoupdate(node), mode=self.mode) + self.id_map[record.id] = int(id) + if config.get('import_partial', False): + self.cr.commit() def _create_record(self, model, fields): record_dict = {} @@ -402,6 +409,8 @@ class YamlInterpreter(object): def process_workflow(self, node): workflow, values = node.items()[0] + if self.isnoupdate(workflow) and self.mode != 'init': + return model = self.get_model(workflow.model) if workflow.ref: id = self.get_id(workflow.ref) @@ -427,11 +436,11 @@ class YamlInterpreter(object): wf_service.trg_validate(uid, model, id, workflow.action, self.cr) def process_function(self, node): - if self.mode != 'init': - return function, values = node.items()[0] + if self.isnoupdate(function) and self.mode != 'init': + return local_context = {'ref': self._ref, '_ref': self._ref} - context = self.get_context(node, local_context) + context = self.get_context(function, local_context) args = [] if function.eval: args = eval(function.eval, local_context) @@ -532,7 +541,7 @@ class YamlInterpreter(object): pid = self.pool.get('ir.model.data')._update(self.cr, self.uid, \ 'ir.ui.menu', self.module, values, node.id, mode=self.mode, \ - res_id=res and res[0] or False) + noupdate=self.isnoupdate(node), res_id=res and res[0] or False) if node.id and parent_id: self.id_map[node.id] = int(parent_id) @@ -578,8 +587,8 @@ class YamlInterpreter(object): keyword = 'client_action_relate' value = 'ir.actions.act_window,%s' % id replace = node.replace or True - self.pool.get('ir.model.data').ir_set(self.cr, self.uid, 'action', \ - keyword, node.id, [node.src_model], value, replace=replace, isobject=True, xml_id=node.id) + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, 'action', keyword, \ + node.id, [node.src_model], value, replace=replace, noupdate=self.isnoupdate(node), isobject=True, xml_id=node.id) # TODO add remove ir.model.data def process_delete(self, node): @@ -608,8 +617,9 @@ class YamlInterpreter(object): keyword = node.keyword or 'client_action_multi' value = 'ir.actions.url,%s' % id replace = node.replace or True - self.pool.get('ir.model.data').ir_set(self.cr, self.uid, \ - 'action', keyword, node.url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=node.id) + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, 'action', \ + keyword, node.url, ["ir.actions.url"], value, replace=replace, \ + noupdate=self.isnoupdate(node), isobject=True, xml_id=node.id) def process_ir_set(self, node): if not self.mode == 'init': From c51be2604d770f2feaf9bbfbe7834c3168551dc7 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 5 Mar 2010 11:42:04 +0100 Subject: [PATCH 07/99] [IMP] Added report tag. bzr revid: jth@openerp.com-20100305104204-yg9ees4miw0hunjs --- bin/tools/yaml_test.py | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py index d284824e1c1..65bf16475c3 100644 --- a/bin/tools/yaml_test.py +++ b/bin/tools/yaml_test.py @@ -6,6 +6,7 @@ import logging import pooler import netsvc +import misc from config import config @@ -74,6 +75,13 @@ class Function(YamlTag): self.name = name super(Function, self).__init__(**kwargs) +class Report(YamlTag): + def __init__(self, model, name, string, **kwargs): + self.model = model + self.name = name + self.string = string + super(Function, self).__init__(**kwargs) + class Delete(YamlTag): def __init__(self, model, id, search, **kwargs): self.model = model @@ -126,6 +134,10 @@ def function_constructor(loader, node): kwargs = loader.construct_mapping(node) return Function(**kwargs) +def report_constructor(loader, node): + kwargs = loader.construct_mapping(node) + return Report(**kwargs) + def delete_constructor(loader, node): kwargs = loader.construct_mapping(node) return Delete(**kwargs) @@ -153,6 +165,7 @@ yaml.add_constructor(u"!menuitem", menuitem_constructor) yaml.add_constructor(u"!workflow", workflow_constructor) yaml.add_constructor(u"!act_window", act_window_constructor) yaml.add_constructor(u"!function", function_constructor) +yaml.add_constructor(u"!report", report_constructor) yaml.add_constructor(u"!context", context_constructor) yaml.add_constructor(u"!delete", delete_constructor) yaml.add_constructor(u"!url", url_constructor) @@ -185,6 +198,9 @@ def is_function(node): return isinstance(node, Function) \ or _is_yaml_mapping(node, Function) +def is_report(node): + return isinstance(node, Report) + def is_workflow(node): return isinstance(node, Workflow) @@ -636,6 +652,38 @@ class YamlInterpreter(object): res['name'], res['models'], res['value'], replace=res.get('replace',True), \ isobject=res.get('isobject', False), meta=res.get('meta',None)) + def process_report(self, node): + values = {} + for dest, f in (('name','string'), ('model','model'), ('report_name','name')): + values[dest] = getattr(node, f) + assert values[dest], "Attribute %s of report is empty !" % (f,) + for field,dest in (('rml','report_rml'),('xml','report_xml'),('xsl','report_xsl'),('attachment','attachment'),('attachment_use','attachment_use')): + if getattr(node, field): + values[dest] = getattr(node, field) + if node.auto: + values['auto'] = eval(node.auto) + if node.sxw: + sxw_content = misc.file_open(node.sxw).read() + values['report_sxw_content'] = sxw_content + if node.header: + values['header'] = eval(node.header) + values['multi'] = node.multi and eval(node.multi) + xml_id = node.id + self.validate_xml_id(xml_id) + + self._set_group_values(node, values) + + id = self.pool.get('ir.model.data')._update(self.cr, self.uid, "ir.actions.report.xml", \ + self.module, values, xml_id, noupdate=self.isnoupdate(node), mode=self.mode) + self.id_map[xml_id] = int(id) + + if not node.menu or eval(node.menu): + keyword = node.keyword or 'client_print_multi' + value = 'ir.actions.report.xml,%s' % id + replace = node.replace or True + self.pool.get('ir.model.data').ir_set(self.cr, self.uid, 'action', \ + keyword, values['name'], [values['model']], value, replace=replace, isobject=True, xml_id=xml_id) + def process(self, yaml_string): """Processes a Yaml string. Custom tags are interpreted by 'process_' instance methods.""" @@ -671,6 +719,8 @@ class YamlInterpreter(object): self.process_ir_set(node) elif is_act_window(node): self.process_act_window(node) + elif is_report(node): + self.process_report(node) elif is_workflow(node): if isinstance(node, types.DictionaryType): self.process_workflow(node) From 83d7ae2d21b8255ec8d4844b06ba1e56ae2fbda3 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 5 Mar 2010 12:01:28 +0100 Subject: [PATCH 08/99] [FIX] fixde Report __init__ bzr revid: jth@openerp.com-20100305110128-689067jl31nxy547 --- bin/tools/yaml_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tools/yaml_test.py b/bin/tools/yaml_test.py index 65bf16475c3..b495989991a 100644 --- a/bin/tools/yaml_test.py +++ b/bin/tools/yaml_test.py @@ -80,7 +80,7 @@ class Report(YamlTag): self.model = model self.name = name self.string = string - super(Function, self).__init__(**kwargs) + super(Report, self).__init__(**kwargs) class Delete(YamlTag): def __init__(self, model, id, search, **kwargs): From b7489580822088c01baf8149a8b38351d165dc93 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 18 Mar 2010 18:39:33 +0100 Subject: [PATCH 09/99] [REF] hr,hr_attendance,hr_expense,hr_holidays,hr_timesheets,stock: Review some layouts bzr revid: stephane@openerp.com-20100318173933-5mdevsd8oeon8vu7 --- addons/hr/hr_view.xml | 50 +++++++------ addons/hr_attendance/hr_attendance.py | 8 +++ addons/hr_attendance/hr_attendance_view.xml | 10 ++- addons/hr_expense/hr_expense_view.xml | 70 ++++++++++--------- addons/hr_holidays/hr_view.xml | 45 ++++++------ addons/hr_holidays/wizard/holidays_summary.py | 4 +- .../wizard/holidays_summary_employees.py | 4 +- .../hr_timesheet_sheet_view.xml | 16 ++--- .../report/report_stock_picking_view.xml | 4 +- 9 files changed, 119 insertions(+), 92 deletions(-) diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index f98150f815b..ce12d776baf 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -38,20 +38,6 @@ - - - - - - - - - - - - - - @@ -62,6 +48,13 @@ + + + + + + + @@ -70,6 +63,14 @@ + + + + + + + + @@ -87,10 +88,11 @@ - + + @@ -101,18 +103,18 @@ search - - - - - - - - + + + + + + + + @@ -152,6 +154,7 @@ form tree,form [] + + + + Partner + res.partner diff --git a/bin/addons/base/res/partner/partner_demo.xml b/bin/addons/base/res/partner/partner_demo.xml index f76b4a04e64..c6de312bc4c 100644 --- a/bin/addons/base/res/partner/partner_demo.xml +++ b/bin/addons/base/res/partner/partner_demo.xml @@ -4,7 +4,7 @@ - + Customer @@ -43,7 +43,7 @@ Bad customers - + Supplier @@ -59,12 +59,12 @@ Textile Suppliers - - + + - + ASUStek @@ -177,12 +177,12 @@ Université de Liège - - + + - + Bruxelles Michel Schumacher @@ -349,6 +349,15 @@ Université de Liège - + + + + + ASUSTEK-Subject + + res.partner,2 + diff --git a/bin/addons/base/test/base_test.xml b/bin/addons/base/test/base_test.xml new file mode 100644 index 00000000000..d3c1df9bd21 --- /dev/null +++ b/bin/addons/base/test/base_test.xml @@ -0,0 +1,14 @@ + + + + + + + + ASUStek + + + + \ No newline at end of file From 3ebd4313d94faa3dfb77a7b6b9e6d81889b9d114 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 12:10:13 +0200 Subject: [PATCH 20/99] [IMP] Warn if an assertion has not been evaluated. bzr revid: jth@openerp.com-20100401101013-gi2zpo47iu7pbpn4 --- bin/tools/yaml_import.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 36eeadd0cf7..6b098289611 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -202,6 +202,7 @@ class YamlInterpreter(object): assertion, expressions = node.items()[0] if self.isnoupdate(assertion) and self.mode != 'init': + self.logger.warn('This assertion was not evaluated ("%s").' % assertion.string) return model = self.get_model(assertion.model) ids = self._get_assertion_id(assertion) From 16749901c7f77b6bf85aa6d82894a23142950259 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 1 Apr 2010 14:00:24 +0200 Subject: [PATCH 21/99] [FIX] changed loglevel to debug for missing module descriptor messages bzr revid: odo@openerp.com-20100401120024-6s60u16p29t6mvkt --- bin/addons/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index e07fdd07bbc..b599a1002cc 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -306,7 +306,10 @@ def load_information_from_description_file(module): if os.path.isfile(description_file): return eval(tools.file_open(description_file).read()) - logging.warning('The module %s does not contain a description file: __openerp__.py or __terp__.py (deprecated)' % module) + #TODO: refactor the logger in this file to follow the logging guidelines + # for 6.0 + logging.getLogger('addons').debug('The module %s does not contain a description file:'\ + '__openerp__.py or __terp__.py (deprecated)', module) return {} def get_modules_with_version(): From 8d62e97ff5030095ea3ab415a09fe5d8062d5cf0 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 15:26:03 +0200 Subject: [PATCH 22/99] [FIX] Assertion expressions list is a direct child of !assert block. bzr revid: jth@openerp.com-20100401132603-2ar1hsmi62c0vs3p --- bin/tools/yaml_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 6b098289611..df273103130 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -217,7 +217,7 @@ class YamlInterpreter(object): context = self.get_context(assertion, self.eval_context) for id in ids: record = model.browse(self.cr, self.uid, id, context) - for test in expressions.get('test', ''): + for test in expressions: try: success = eval(test, self.eval_context, RecordDictWrapper(record)) except Exception, e: From 44f6ef941a26abbdc5bc9528390c2d03a9e7e1dc Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 15:26:51 +0200 Subject: [PATCH 23/99] [FIX] Logs the stack trace. bzr revid: jth@openerp.com-20100401132651-dgcz26yhw152f4sh --- bin/tools/yaml_import.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index df273103130..0136bcf69e0 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -607,9 +607,9 @@ class YamlInterpreter(object): try: self._process_node(node) except YamlImportException, e: - self.logger.log(logging.ERROR, e) + self.logger.exception(e) except Exception, e: - self.logger.log(logging.ERROR, e) + self.logger.exception(e) raise e def _process_node(self, node): From 51f8a94d874c0eebd9e404078a02e787e2a14198 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 15:27:09 +0200 Subject: [PATCH 24/99] [FIX] Workflow transition takes the model name, not the model object. bzr revid: jth@openerp.com-20100401132709-1tyxfoh9550za6y1 --- bin/tools/yaml_import.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 0136bcf69e0..06dfb151cc9 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -336,7 +336,6 @@ class YamlInterpreter(object): workflow, values = node.items()[0] if self.isnoupdate(workflow) and self.mode != 'init': return - model = self.get_model(workflow.model) if workflow.ref: id = self.get_id(workflow.ref) else: @@ -357,7 +356,7 @@ class YamlInterpreter(object): else: uid = self.uid wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, model, id, workflow.action, self.cr) + wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr) def process_function(self, node): function, values = node.items()[0] From d74b2824048b43e9df4a6090a327ec572500bd21 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 1 Apr 2010 17:08:54 +0200 Subject: [PATCH 25/99] [IMP] spellchecked error messages and made logging compliant according to new logging in 6.0 bzr revid: odo@openerp.com-20100401150854-ob4184vyir37jkjo --- bin/osv/osv.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/bin/osv/osv.py b/bin/osv/osv.py index 493db9fcdf4..9d625c5541b 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -57,8 +57,7 @@ class osv_pool(netsvc.Service): return f(self, dbname, *args, **kwargs) except orm.except_orm, inst: if inst.name == 'AccessError': - tb_s = "AccessError\n" + "".join(traceback.format_exception(*sys.exc_info())) - self.logger.notifyChannel('web-services', netsvc.LOG_DEBUG, tb_s) + self.logger.debug("AccessError", exc_info=True) self.abortResponse(1, inst.name, 'warning', inst.value) except except_osv, inst: self.abortResponse(1, inst.name, inst.exc_type, inst.value) @@ -68,8 +67,7 @@ class osv_pool(netsvc.Service): self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key]) self.abortResponse(1, 'Integrity Error', 'warning', inst[0]) except Exception, e: - tb_s = "".join(traceback.format_exception(*sys.exc_info())) - self.logger.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s) + self.logger.exception("Uncaught exception") raise return wrapper @@ -84,7 +82,7 @@ class osv_pool(netsvc.Service): self._store_function = {} self._init = True self._init_parent = {} - self.logger = netsvc.Logger() + self.logger = logging.getLogger("web-services") netsvc.Service.__init__(self, 'object_proxy', audience='') self.exportMethod(self.obj_list) self.exportMethod(self.exec_workflow) @@ -116,11 +114,10 @@ class osv_pool(netsvc.Service): try: try: if method.startswith('_'): - raise except_osv('Method Error', 'Private method %s can not be calleble.' % (method,)) + raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,)) res = pool.execute_cr(cr, uid, obj, method, *args, **kw) if res is None: - self.logger.notifyChannel("web-services", netsvc.LOG_WARNING, - 'Method can not return a None value (crash in XML-RPC)') + self.logger.warning('RPC methods cannot return `None` at the moment!') cr.commit() except Exception: cr.rollback() From 41fc8c369af3cb3562b6ce44e333befda15837c8 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 19:22:27 +0200 Subject: [PATCH 26/99] [FIX] Added support for string and constant (int) logging level. bzr revid: jth@openerp.com-20100401172227-q5geonsglidfsuqa --- bin/tools/yaml_import.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 06dfb151cc9..2f48ca51618 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -182,10 +182,16 @@ class YamlInterpreter(object): return node def _log_assert_failure(self, severity, msg, *args): - self.assert_report.record(False, severity) - self.logger.log(severity, msg, *args) - if severity >= config['assert_exit_level']: - raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % logging.getLevelName(severity)) + if isinstance(severity, types.StringTypes): + levelname = severity.strip().upper() + level = logging.getLevelName(levelname) + else: + level = severity + levelname = logging.getLevelName(level) + self.assert_report.record(False, levelname) + self.logger.log(level, msg, *args) + if level >= config['assert_exit_level']: + raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % levelname) return def _get_assertion_id(self, assertion): From 0eb45de95c08a189b997b3af307bedb4407769b5 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 21:17:49 +0200 Subject: [PATCH 27/99] [FIX] Removed ANSI output in logging level. ANSI output changed the builtin log level name. This actually broke tests as we rely on the log level name for assertion report. See comments for indications to a more appropriate implementation. bzr revid: jth@openerp.com-20100401191749-j562qu28o0a066m1 --- bin/netsvc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/netsvc.py b/bin/netsvc.py index 475bbc33ba7..6aaff59ead3 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -205,7 +205,10 @@ def init_logger(): for level, (fg, bg) in mapping.items(): msg = "\x1b[%dm\x1b[%dm%s\x1b[0m" % (foreground(fg), background(bg), level) - logging.addLevelName(getattr(logging, level), msg) +# jth: we should not override default level name +# jth: use this instead http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored +# jth: also, do not output ANSI if terminal doesn't support it (test it in Eclipse console and on windows) + #logging.addLevelName(getattr(logging, level), msg) class Logger(object): From f1b72c67438c626e33fd124307e4d540c40967cd Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 21:24:14 +0200 Subject: [PATCH 28/99] [FIX] Fixed eval context. bzr revid: jth@openerp.com-20100401192414-s6mfpwtkph3rxsn8 --- bin/tools/yaml_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 2f48ca51618..4e974720c6c 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -126,7 +126,7 @@ class YamlInterpreter(object): self.pool = pooler.get_pool(cr.dbname) self.uid = 1 self.context = {} # opererp context - self.eval_context = {'ref': self._ref, '_ref': self._ref, 'time': time} # added '_ref' so that record['ref'] is possible + self.eval_context = {'ref': self._ref(), '_ref': self._ref(), 'time': time} # added '_ref' so that record['ref'] is possible def _ref(self): return lambda xml_id: self.get_id(xml_id) From 7043023dd859943a2cf5fa3e3e86652ad427f6e5 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 21:25:02 +0200 Subject: [PATCH 29/99] [FIX] Safer assignment to many2one. bzr revid: jth@openerp.com-20100401192502-zojg3dxhup49oz1m --- bin/tools/yaml_import.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 4e974720c6c..b4278abd5c8 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -178,6 +178,15 @@ class YamlInterpreter(object): def isnoupdate(self, node): return self.noupdate or node.noupdate or False + def _get_first_result(self, results, default=False): + if len(results): + value = results[0] + if isinstance(value, types.TupleType): + value = value[0] + else: + value = default + return value + def process_comment(self, node): return node @@ -296,7 +305,7 @@ class YamlInterpreter(object): if column._type in ("many2many", "one2many"): value = [(6, 0, elements)] else: # many2one - value = elements[0] + value = self._get_first_result(elements) elif column._type == "many2one": value = self.get_id(expression) elif column._type == "one2many": From 60cd823fa36df6749813124441c9c36d3eb493a7 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Thu, 1 Apr 2010 21:26:47 +0200 Subject: [PATCH 30/99] [FIX] Fixed function evaluation. bzr revid: jth@openerp.com-20100401192647-4ew1sci1l34q8ft3 --- bin/tools/yaml_import.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index b4278abd5c8..8fe1d634764 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -374,23 +374,30 @@ class YamlInterpreter(object): wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr) def process_function(self, node): - function, values = node.items()[0] + function, params = node.items()[0] if self.isnoupdate(function) and self.mode != 'init': return + model = self.get_model(function.model) context = self.get_context(function, self.eval_context) args = [] if function.eval: args = eval(function.eval, self.eval_context) - for value in values: - if not 'model' in value and (not 'eval' in value or not 'search' in value): - raise YamlImportException('You must provide a "model" and an "eval" or "search" to evaluate.') - value_model = self.get_model(value['model']) - local_context = {'obj': lambda x: value_model.browse(self.cr, self.uid, x, context=context)} - local_context.update(self.id_map) - id = eval(value['eval'], self.eval_context, local_context) - if id != None: - args.append(id) - model = self.get_model(function.model) + for i, param in enumerate(params): + if 'model' in param: + param_model = self.get_model(param['model']) + else: + param_model = model + if 'search' in param: + q = eval(param['search'], self.eval_context) + ids = param_model.search(cr, uid, q) + value = self._get_first_result(ids) + elif 'eval' in param: + local_context = {'obj': lambda x: value_model.browse(self.cr, self.uid, x, context)} + local_context.update(self.id_map) + value = eval(param['eval'], self.eval_context, local_context) + else: + raise YamlImportException('You must provide at least a "eval" or a "search" to function parameter #%d.' % i) + args.append(value) method = function.name getattr(model, method)(self.cr, self.uid, *args) From e1aa0bd19a1e90a2d2f433149bd0646734d86140 Mon Sep 17 00:00:00 2001 From: "nch@tinyerp.com" <> Date: Fri, 2 Apr 2010 12:38:23 +0530 Subject: [PATCH 31/99] [FIX]: missing import logging bzr revid: nch@tinyerp.com-20100402070823-106ezqymrkhk6yql --- bin/osv/osv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/osv/osv.py b/bin/osv/osv.py index 9d625c5541b..a528d418089 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -29,7 +29,7 @@ import pooler import copy import sys import traceback - +import logging from psycopg2 import IntegrityError from tools.func import wraps From 336f0e40a43a5a7f7dc6511417b839ce8a06ec2a Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 2 Apr 2010 10:08:55 +0200 Subject: [PATCH 32/99] [REF] Cleanup of delete tag. bzr revid: jth@openerp.com-20100402080855-8pvhqcspi9mhojpa --- bin/import_xml.rng | 6 ++++-- bin/tools/yaml_import.py | 8 ++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/bin/import_xml.rng b/bin/import_xml.rng index e637d42f123..c852d421e17 100644 --- a/bin/import_xml.rng +++ b/bin/import_xml.rng @@ -148,8 +148,10 @@ - - + + + + diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 8fe1d634764..0a5d8d7e09b 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -535,14 +535,10 @@ class YamlInterpreter(object): # TODO add remove ir.model.data def process_delete(self, node): - ids = [] if len(node.search): ids = self.pool.get(node.model).search(self.cr, self.uid, eval(node.search, self.eval_context)) - if len(node.id): - try: - ids.append(self.get_id(node.id)) - except: - pass + else: + ids = [self.get_id(node.id)] if len(ids): self.pool.get(node.model).unlink(self.cr, self.uid, ids) self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids, direct=True) From 3f8b66799f6cf768320045aca6131b7d8eb6dc5c Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 2 Apr 2010 10:15:46 +0200 Subject: [PATCH 33/99] [REF] Cleanup ir_model_data._unlink bzr revid: jth@openerp.com-20100402081546-deiqbx40q2hkucck --- bin/addons/base/ir/ir_model.py | 8 ++++---- bin/tools/convert.py | 2 +- bin/tools/yaml_import.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index db03cef116f..a4afff1973d 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -548,10 +548,10 @@ class ir_model_data(osv.osv): table.replace('.', '_'))] = (table, inherit_id) return res_id - def _unlink(self, cr, uid, model, ids, direct=False): - for id in ids: - self.unlink_mark[(model, id)]=False - cr.execute('delete from ir_model_data where res_id=%s and model=%s', (id, model)) + def _unlink(self, cr, uid, model, res_ids): + for res_id in res_ids: + self.unlink_mark[(model, res_id)] = False + cr.execute('delete from ir_model_data where res_id=%s and model=%s', (res_id, model)) return True def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False): diff --git a/bin/tools/convert.py b/bin/tools/convert.py index 1d4b1fd9d0b..36f2740dbc9 100644 --- a/bin/tools/convert.py +++ b/bin/tools/convert.py @@ -251,7 +251,7 @@ form: module.record_id""" % (xml_id,) pass if ids: self.pool.get(d_model).unlink(cr, self.uid, ids) - self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids, direct=True) + self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids) def _tag_report(self, cr, rec, data_node=None): res = {} diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index 0a5d8d7e09b..cd1a1a34fd6 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -541,7 +541,7 @@ class YamlInterpreter(object): ids = [self.get_id(node.id)] if len(ids): self.pool.get(node.model).unlink(self.cr, self.uid, ids) - self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids, direct=True) + self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids) def process_url(self, node): self.validate_xml_id(node.id) From 101e933d28150aaf0940810d90e6dcde2d07d3a3 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Fri, 2 Apr 2010 14:32:27 +0530 Subject: [PATCH 34/99] [IMP] workflow :- add domain in workflow field. bzr revid: ysa@tinyerp.co.in-20100402090227-6yqr461xps4jz97b --- bin/addons/base/ir/workflow/workflow.py | 1 + bin/addons/base/ir/workflow/workflow_view.xml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py index 2a551e9549f..f32f0d842f7 100644 --- a/bin/addons/base/ir/workflow/workflow.py +++ b/bin/addons/base/ir/workflow/workflow.py @@ -151,6 +151,7 @@ class wkf_transition(osv.osv): 'condition': fields.char('Condition', required=True, size=128), 'act_from': fields.many2one('workflow.activity', 'Source Activity', required=True, select=True, ondelete='cascade'), 'act_to': fields.many2one('workflow.activity', 'Destination Activity', required=True, select=True, ondelete='cascade'), + 'workflow_id': fields.related('act_from', 'wkf_id', type='many2one', relation='workflow', string='Workflow'), } _defaults = { 'condition': lambda *a: 'True', diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index 04457f21338..c72d2f55ea3 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -35,8 +35,8 @@ - - + + From 4e056d7540fb0f2aec47617304ca77bd20c534e5 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Fri, 2 Apr 2010 17:58:57 +0530 Subject: [PATCH 35/99] [IMP] diagram :- improve code. bzr revid: ysa@tinyerp.co.in-20100402122857-dpbc2b8nhgrzvtyq --- bin/addons/base/ir/workflow/workflow.py | 1 - bin/addons/base/ir/workflow/workflow_view.xml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py index f32f0d842f7..2a551e9549f 100644 --- a/bin/addons/base/ir/workflow/workflow.py +++ b/bin/addons/base/ir/workflow/workflow.py @@ -151,7 +151,6 @@ class wkf_transition(osv.osv): 'condition': fields.char('Condition', required=True, size=128), 'act_from': fields.many2one('workflow.activity', 'Source Activity', required=True, select=True, ondelete='cascade'), 'act_to': fields.many2one('workflow.activity', 'Destination Activity', required=True, select=True, ondelete='cascade'), - 'workflow_id': fields.related('act_from', 'wkf_id', type='many2one', relation='workflow', string='Workflow'), } _defaults = { 'condition': lambda *a: 'True', diff --git a/bin/addons/base/ir/workflow/workflow_view.xml b/bin/addons/base/ir/workflow/workflow_view.xml index c72d2f55ea3..04457f21338 100644 --- a/bin/addons/base/ir/workflow/workflow_view.xml +++ b/bin/addons/base/ir/workflow/workflow_view.xml @@ -35,8 +35,8 @@ - - + + From 35bab1e92d629371a0f67754d07b37c4c33f6a68 Mon Sep 17 00:00:00 2001 From: "RVO(OpenERP)" <> Date: Fri, 2 Apr 2010 19:24:12 +0530 Subject: [PATCH 36/99] Added docstring in public methods of orm bzr revid: hda@tinyerp.com-20100402135412-sndekigkmxf5twb2 --- bin/osv/orm.py | 299 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 285 insertions(+), 14 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 32efe32558b..af3ca7ec6dc 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -501,6 +501,16 @@ class orm_template(object): self._table = self._name.replace('.', '_') def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}): + """ + Fetch records as objects allowing to use dot notation to browse fields and relations + + :param cr: database cursor + :param user: current user id + :param select: id or a list of ids + :param context: context arguments, like lang, time zone + :rtype: object or a list of objects requested + + """ if not context: context = {} self._list_class = list_class or browse_record_list @@ -616,6 +626,19 @@ class orm_template(object): return [data] + lines def export_data(self, cr, uid, ids, fields_to_export, context=None): + """ + Export fields for selected objects + + :param cr: database cursor + :param uid: current user id + :param ids: a list of ids + :param fields_to_export: a list of fields + :param context: context arguments, like lang, time zone, may contain import_comp(default: False) to make exported data compatible with import_data() + :rtype: a dictionary with a *datas* matrix + + This method is used when exporting data via client menu + + """ if not context: context = {} imp_comp = context.get('import_comp',False) @@ -645,6 +668,24 @@ class orm_template(object): return {'datas':datas} def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None): + """ + Import given data in given module + + :param cr: database cursor + :param uid: current user id + :param ids: a list of ids + :param fields: a list of fields + :param data: data to import + :param mode: 'init' or 'update' for record creation + :param current_module: module name + :param noupdate: flag for record creation + :param context: context arguments, like lang, time zone, + :param filename: optional file to store partial import state for recovery + :rtype: a tuple + + This method is used when importing data via client menu + + """ if not context: context = {} fields = map(lambda x: x.split('/'), fields) @@ -984,16 +1025,13 @@ class orm_template(object): self._invalids.clear() def default_get(self, cr, uid, fields_list, context=None): - """ Set default values for the object's fields. + """ + Set default values for the object's fields. - Returns a dict of {field_name:default_value} + :param fields_list: fields for which the object doesn't have any value yet, and default values need to be provided. + If fields outside this list are returned, the user-provided values will be overwritten. + :rtype: a dict of {field_name:default_value} - Arguments: - `fields_list`: the fields for which the object doesn't have - any value yet, and default values need to be - provided. If fields outside this list are - returned, the user-provided values will be - overwritten. """ return {} @@ -1081,6 +1119,7 @@ class orm_template(object): for r in res.keys(): if r not in fields: del res[r] + print 'Result from fields get', res return res # @@ -1316,6 +1355,23 @@ class orm_template(object): # if view_id, view_type is not required # def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): + """ + Get the detailed composition of the requested view like fields, model, view architecture + + :param cr: database cursor + :param user: current user id + :param view_id: id of the view or None + :param view_type: type of the view to return if view_id is None ('form', tree', ...) + :param context: context arguments, like lang, time zone + :param toolbar: true to include concextual actions + :param submenu: example (portal_project module) + :return: a dictionary describing the composition of the requested view (including inherited views and extensions) + :raise AttributeError: + * if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace' + * if some tag other than 'position' is found in parent view + :raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure + + """ if not context: context = {} @@ -1878,6 +1934,28 @@ class orm(orm_template): _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count', 'exists'] def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None): + """ + Get the list of records in list view sorted by the given fields + + :param cr: database cursor + :param uid: current user id + :param domain: a list specifying search criteria [['field_name', 'operator', 'value'], ...] + :param fields: a list of fields present in the list view specified on the object + :param groupby: a list of fields on which to sort the records + :type fields_list: a list (example ['field_name_1', ...]) + :param offset: optional number of records to skip + :param limit: optional max number of records to return + :param context: context arguments, like lang, time zone + :return: a list of dictionaries(one dictionary for each record) containing: + + * the values of fields sorted out by the fields in groupby arguement + * __domain: a list of tuples specifying the search criteria + * __context: a dictionary with argument like 'groupby' + :rtype: [{'field_name_1': value, ...] + :raise AccessError: * if user has no read rights on the requested object + * if user tries to bypass access rules for read on the requested object + + """ groupby_list = groupby groupby = groupby[0] context = context or {} @@ -1953,10 +2031,13 @@ class orm(orm_template): return data def _inherits_join_calc(self, field, tables, where_clause): - """ Adds missing table select and join clause(s) for reaching + """ + Adds missing table select and join clause(s) for reaching the field coming from an '_inherits' parent table. - @param tables: list of table._table names enclosed in double quotes as returned + + :param tables: a list of table._table names enclosed in double quotes as returned by _where_calc() + """ current_table = self while field in current_table._inherit_fields and not field in current_table._columns: @@ -2411,6 +2492,17 @@ class orm(orm_template): self._columns[f].restart() def default_get(self, cr, uid, fields_list, context=None): + """ + Get default field values of the given field list of the model + + :param cr: database cursor + :param uid: current user id + :param fields_list: a list of fields to get the default value + :type fields_list: a list (example ['field_name_1', ...]) + :param context: context arguments, like lang, time zone + :return: a dictionary of the default values for fields (set on the object class, by the user preferences, or via the context) + + """ if not context: context = {} value = {} @@ -2502,12 +2594,38 @@ class orm(orm_template): self._inherits_reload_src() def fields_get(self, cr, user, fields=None, context=None): + """ + Get the description of list of fields + + :param cr: database cursor + :param user: current user id + :param fields: a list of fields + :param context: context arguments, like lang, time zone + :return: a dictionary of field dictionaries, each one describing a field of the business object + :raise AccessError: * if user has no create/write rights on the requested object + + """ ira = self.pool.get('ir.model.access') read_access = ira.check(cr, user, self._name, 'write', raise_exception=False, context=context) or \ ira.check(cr, user, self._name, 'create', raise_exception=False, context=context) return super(orm, self).fields_get(cr, user, fields, context, read_access) def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + """ + Read records with given ids with the given fields + + :param cr: database cursor + :param user: current user id + :param ids: id or a list of the ids of the records to read + :param fields: optional list of field names to return (default: all fields would be returned) + :type fields: a list (example ['field_name_1', ...]) + :param context(optional, highly recommended): context arguments, like lang, time zone + :return: a list of dictionaries((a dictionary per record asked)) with requested field values + :rtype: [{‘name_of_the_field’: value, ...}, ...] + :raise AccessError: * if user has no read rights on the requested object + * if user tries to bypass access rules for read on the requested object + + """ if not context: context = {} self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context) @@ -2693,6 +2811,24 @@ class orm(orm_template): return res def perm_read(self, cr, user, ids, context=None, details=True): + """ + Read the permission for record of the given ids + + :param cr: database cursor + :param user: current user id + :param ids: id or list of ids + :param context: context arguments, like lang, time zone + :param details: if True, *_uid fields are replaced with the name of the user + :return: a list of ownership dictionaries for each requested record + :rtype: a list of dictionaries with the following keys: + + * id: object id + * create_uid: user who created the record + * create_date: date when the record was created + * write_uid: last user who changed the record + * write_date: date of the last change to the record + + """ if not context: context = {} if not ids: @@ -2753,6 +2889,20 @@ class orm(orm_template): % (operation, self._name)) def unlink(self, cr, uid, ids, context=None): + """ + Delete records with given ids + + :param cr: database cursor + :param uid: current user id + :param ids: id or a list of ids + :param context(optional, highly recommended): context arguments, like lang, time zone + :return: True + :raise AccessError: * if user has no unlink rights on the requested object + * if user tries to bypass access rules for unlink on the requested object + :raise UserError: if the record is default property for other records + + """ + if not ids: return True if isinstance(ids, (int, long)): @@ -2799,6 +2949,30 @@ class orm(orm_template): # TODO: Validate # def write(self, cr, user, ids, vals, context=None): + """ + Update records with given ids with the given field values + + :param cr: database cursor + :param user: current user id + :type user: integer (example 1) + :param ids: id or a list of ids + :param vals: a dictionary of field values to update + :type vals: a dictionary (example {'field_name':'value', ...}) + :param context(optional, highly recommended): context arguments, like lang, time zone + :return: True + :raise AccessError: * if user has no write rights on the requested object + * if user tries to bypass access rules for write on the requested object + :raise ValidateError: if user tries to enter invalid value for a field that is not in selection + :raise UserError: if recurssion is found + + vals format for relational field type. + + + many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])]) + + one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})]) + + many2one field : ID of related record + + reference field : model name, id (example: 'product.product, 5') + + """ readonly = None for field in vals.copy(): fobj = None @@ -2985,10 +3159,28 @@ class orm(orm_template): # TODO: Should set perm to user.xxx # def create(self, cr, user, vals, context=None): - """ create(cr, user, vals, context) -> int - cr = database cursor - user = user id - vals = dictionary of the form {'field_name':field_value, ...} + """ + Create new record with specified value + + :param cr: database cursor + :param user: current user id + :type user: integer (example 1) + :param vals: a dictionary for new record {'field_name':field_value, ...} + :type vals: a dictionary (example {'field_name':field_value, ...}) + :param context(optional, highly recommended): context arguements, like lang, time zone + :type context: a dictionary (example {'lang':'en_us', ...}) + :return: id of new record created + :raise AccessError: * if user has no create rights on the requested object + * if user tries to bypass access rules for create on the requested object + :raise ValidateError: if user tries to enter invalid value for a field that is not in selection + + vals format for relational field type. + + + many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])]) + + one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})]) + + many2one field : ID of related record + + reference field : model name, id (example: 'product.product, 5') + """ if not context: context = {} @@ -3333,6 +3525,27 @@ class orm(orm_template): def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): + """ + Search for record/s with or without domain + + :param cr: database cursor + :param user: current user id + :param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...] + :param offset: optional number of records to skip + :param limit: optional max number of records to return + :param order: optional columns to sort by (default: self._order ) + :param context(optional, highly recommended): context arguments, like lang, time zone + :param count: if True, returns only the number of records matching the criteria, not their ids + :return: id or a list of ids of records matching the criteria + :rtype: integer or a list of integers + :raise AccessError: * if user tries to bypass access rules for read on the requested object. + + Operators: + * =, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right + Prefix operators: + * '&' (default), '|', '!' + + """ if not context: context = {} # compute the where, order by, limit and offset clauses @@ -3382,6 +3595,16 @@ class orm(orm_template): return self._columns[field].search(cr, self, args, field, value, offset, limit, uid) def name_get(self, cr, user, ids, context=None): + """ + + :param cr: database cursor + :param user: current user id + :type user: integer (example 1) + :param ids: a list of ids + :param context: context arguments, like lang, time zone + :return: tuples with the text representation of requested objects for to-many relationships + + """ if not context: context = {} if not ids: @@ -3392,6 +3615,20 @@ class orm(orm_template): [self._rec_name], context, load='_classic_write')] def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): + """ + + :param cr: database cursor + :param user: current user id + :param name: object name to search + :param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...] + :param operator: operator for search criterion + :param context: context arguments, like lang, time zone + :param limit: optional max number of records to return + :return: a list of object names matching the search criteria, used to provide completition for to-many relationships + + This method is equivalent of search() on name + name_get() + + """ if not args: args = [] if not context: @@ -3404,6 +3641,17 @@ class orm(orm_template): return res def copy_data(self, cr, uid, id, default=None, context=None): + """ + Copy given record's data with all its fields values + + :param cr: database cursor + :param user: current user id + :param ids: id of the record to copy + :param default: a dictionary of field values to update before saving the duplicate object + :param context: context arguments, like lang, time zone + :return: a dictionary containing all the field values + """ + if not context: context = {} if not default: @@ -3475,6 +3723,18 @@ class orm(orm_template): return data, trans_data def copy(self, cr, uid, id, default=None, context=None): + """ + Duplicate record with given id updating it with default values + + :param cr: database cursor + :param uid: current user id + :param id: id of the record to copy + :param default: a dictionary of field values to update before saving the duplicate object + :type default: a dictionary (example {'field_name':field_value, ...}) + :param context: context arguments, like lang, time zone + :return: True + + """ trans_obj = self.pool.get('ir.translation') data, trans_data = self.copy_data(cr, uid, id, default, context) new_id = self.create(cr, uid, data, context) @@ -3489,6 +3749,16 @@ class orm(orm_template): return bool(cr.fetchone()[0]) def check_recursion(self, cr, uid, ids, parent=None): + """ + Check recursion in records + + :param cr: database cursor + :param uid: current user id + :param ids: a list of ids of records + :param parent: parent field name + :return: True or False based on recursion detection + """ + if not parent: parent = self._parent_name ids_parent = ids[:] @@ -3507,3 +3777,4 @@ class orm(orm_template): return True # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + From 790f32d9d0327945f9f1741b58bfb6979923539e Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Fri, 2 Apr 2010 16:03:23 +0200 Subject: [PATCH 37/99] [REF] function, ref, eval nodes. [IMP] YAML multi constructor for ref tag. bzr revid: jth@openerp.com-20100402140323-qmki5mvns0vxwmdm --- bin/tools/yaml_import.py | 96 +++++++++++++++++++++++++--------------- bin/tools/yaml_tag.py | 16 ++++--- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/bin/tools/yaml_import.py b/bin/tools/yaml_import.py index cd1a1a34fd6..e77999a49f1 100644 --- a/bin/tools/yaml_import.py +++ b/bin/tools/yaml_import.py @@ -286,26 +286,39 @@ class YamlInterpreter(object): record_dict[field_name] = field_value return record_dict + def process_ref(self, node, column=None): + if node.search: + if node.model: + model_name = node.model + elif column: + model_name = column._obj + else: + raise YamlImportException('You need to give a model for the search, or a column to infer it.') + model = self.get_model(model_name) + q = eval(node.search, self.eval_context) + ids = model.search(self.cr, self.uid, q) + if node.use: + instances = model.browse(self.cr, self.uid, ids) + value = [inst[node.use] for inst in instances] + else: + value = ids + elif node.id: + value = self.get_id(node.id) + else: + value = None + return value + + def process_eval(self, node): + return eval(node.expression, self.eval_context) + def _eval_field(self, model, field_name, expression): column = model._columns[field_name] if is_ref(expression): - if expression.model: - other_model_name = expression.model - else: - other_model_name = column._obj - other_model = self.get_model(other_model_name) - if expression.search: - q = eval(expression.search, self.eval_context) - ids = other_model.search(self.cr, self.uid, q) - if expression.use: - instances = other_model.browse(self.cr, self.uid, ids) - elements = [inst[expression.use] for inst in instances] - else: - elements = ids - if column._type in ("many2many", "one2many"): - value = [(6, 0, elements)] - else: # many2one - value = self._get_first_result(elements) + elements = self.process_ref(expression) + if column._type in ("many2many", "one2many"): + value = [(6, 0, elements)] + else: # many2one + value = self._get_first_result(elements) elif column._type == "many2one": value = self.get_id(expression) elif column._type == "one2many": @@ -316,7 +329,7 @@ class YamlInterpreter(object): value = [(6, 0, ids)] else: # scalar field if is_eval(expression): - value = eval(expression.expression, self.eval_context) + value = self.process_eval(expression) else: value = expression # raise YamlImportException('Unsupported column "%s" or value %s:%s' % (field_name, type(expression), expression)) @@ -372,6 +385,32 @@ class YamlInterpreter(object): uid = self.uid wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr) + + def _eval_params(self, model, params): + args = [] + for i, param in enumerate(params): + if isinstance(param, types.ListType): + value = self._eval_params(model, param) + elif is_ref(param): + value = self.process_ref(param) + elif is_eval(param): + value = self.process_eval(param) + elif isinstance(param, types.DictionaryType): # supports XML syntax + param_model = self.get_model(param.get('model', model)) + if 'search' in param: + q = eval(param['search'], self.eval_context) + ids = param_model.search(cr, uid, q) + value = self._get_first_result(ids) + elif 'eval' in param: + local_context = {'obj': lambda x: param_model.browse(self.cr, self.uid, x, context)} + local_context.update(self.id_map) + value = eval(param['eval'], self.eval_context, local_context) + else: + raise YamlImportException('You must provide either a !ref or at least a "eval" or a "search" to function parameter #%d.' % i) + else: + value = param # scalar value + args.append(value) + return args def process_function(self, node): function, params = node.items()[0] @@ -379,25 +418,10 @@ class YamlInterpreter(object): return model = self.get_model(function.model) context = self.get_context(function, self.eval_context) - args = [] if function.eval: - args = eval(function.eval, self.eval_context) - for i, param in enumerate(params): - if 'model' in param: - param_model = self.get_model(param['model']) - else: - param_model = model - if 'search' in param: - q = eval(param['search'], self.eval_context) - ids = param_model.search(cr, uid, q) - value = self._get_first_result(ids) - elif 'eval' in param: - local_context = {'obj': lambda x: value_model.browse(self.cr, self.uid, x, context)} - local_context.update(self.id_map) - value = eval(param['eval'], self.eval_context, local_context) - else: - raise YamlImportException('You must provide at least a "eval" or a "search" to function parameter #%d.' % i) - args.append(value) + args = self.process_eval(function.eval) + else: + args = self._eval_params(function.model, params) method = function.name getattr(model, method)(self.cr, self.uid, *args) diff --git a/bin/tools/yaml_tag.py b/bin/tools/yaml_tag.py index 83f6dde9c8d..1d36dc17a5b 100644 --- a/bin/tools/yaml_tag.py +++ b/bin/tools/yaml_tag.py @@ -87,10 +87,9 @@ class Eval(YamlTag): super(Eval, self).__init__() class Ref(YamlTag): - # 'model' parameter is optional since it defaults to the column type - def __init__(self, search): - self.search = search - super(Ref, self).__init__() + def __init__(self, *args, **kwargs): + print "kwargs %s" % kwargs + super(Ref, self).__init__(*args, **kwargs) class IrSet(YamlTag): def __init__(self): @@ -144,8 +143,11 @@ def eval_constructor(loader, node): expression = loader.construct_scalar(node) return Eval(expression) -def ref_constructor(loader, node): - kwargs = loader.construct_mapping(node) +def ref_constructor(loader, tag_suffix, node): + if tag_suffix == "id": + kwargs = {"id": loader.construct_scalar(node)} + else: + kwargs = loader.construct_mapping(node) return Ref(**kwargs) def ir_set_constructor(loader, node): @@ -167,5 +169,5 @@ yaml.add_constructor(u"!context", context_constructor) yaml.add_constructor(u"!delete", delete_constructor) yaml.add_constructor(u"!url", url_constructor) yaml.add_constructor(u"!eval", eval_constructor) -yaml.add_constructor(u"!ref", ref_constructor) +yaml.add_multi_constructor(u"!ref", ref_constructor) yaml.add_constructor(u"!ir_set", ir_set_constructor) From ed2c762bc7b209679fd1c9a3691965127df194b8 Mon Sep 17 00:00:00 2001 From: Launchpad Code Hosting Date: Mon, 5 Apr 2010 04:53:17 +0100 Subject: [PATCH 38/99] Launchpad automatic translations update. bzr revid: codehost@crowberry-20100403035418-kws0c2b4zehstcsw bzr revid: codehost@crowberry-20100404035222-xo32id7uhd75od1g bzr revid: codehost@crowberry-20100405035317-jonllw2dd4inplg8 --- bin/addons/base/i18n/he.po | 8 ++++---- bin/addons/base/i18n/hr.po | 4 ++-- bin/addons/base/i18n/ja.po | 4 ++-- bin/addons/base/i18n/sv.po | 4 ++-- bin/addons/base/i18n/uk.po | 4 ++-- bin/addons/base/i18n/zh_CN.po | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bin/addons/base/i18n/he.po b/bin/addons/base/i18n/he.po index 1f8196732f8..b068412415d 100644 --- a/bin/addons/base/i18n/he.po +++ b/bin/addons/base/i18n/he.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2009-12-18 17:51+0000\n" +"PO-Revision-Date: 2010-04-04 19:08+0000\n" "Last-Translator: Ddorda \n" "Language-Team: Hebrew \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-01-14 04:46+0000\n" +"X-Launchpad-Export-Date: 2010-04-05 03:52+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base @@ -3006,7 +3006,7 @@ msgstr "מפריד עשרות" #: view:res.request:0 #: field:res.request,history:0 msgid "History" -msgstr "הסטוריה" +msgstr "היסטוריה" #. module: base #: field:ir.attachment,create_uid:0 @@ -4776,7 +4776,7 @@ msgstr "8. %I:%M:%S %p ==> 06:25:20 PM" #: view:ir.translation:0 #: model:ir.ui.menu,name:base.menu_translation msgid "Translations" -msgstr "תרגום" +msgstr "תרגומים" #. module: base #: field:ir.sequence,padding:0 diff --git a/bin/addons/base/i18n/hr.po b/bin/addons/base/i18n/hr.po index 55f4faa1f59..7d42d7b1df1 100644 --- a/bin/addons/base/i18n/hr.po +++ b/bin/addons/base/i18n/hr.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-01 04:15+0000\n" +"PO-Revision-Date: 2010-04-04 04:38+0000\n" "Last-Translator: goranc \n" "Language-Team: openerp-translators\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" +"X-Launchpad-Export-Date: 2010-04-05 03:52+0000\n" "X-Generator: Launchpad (build Unknown)\n" "Language: hr\n" diff --git a/bin/addons/base/i18n/ja.po b/bin/addons/base/i18n/ja.po index b3b586b82a7..554dd6419e7 100644 --- a/bin/addons/base/i18n/ja.po +++ b/bin/addons/base/i18n/ja.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-01 04:13+0000\n" +"PO-Revision-Date: 2010-04-04 04:23+0000\n" "Last-Translator: Harry (Open ERP) \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" +"X-Launchpad-Export-Date: 2010-04-05 03:52+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/sv.po b/bin/addons/base/i18n/sv.po index 4021ba05f39..40d0f5d2420 100644 --- a/bin/addons/base/i18n/sv.po +++ b/bin/addons/base/i18n/sv.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-01 04:09+0000\n" +"PO-Revision-Date: 2010-04-04 04:19+0000\n" "Last-Translator: Anders Wallenquist \n" "Language-Team: <>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" +"X-Launchpad-Export-Date: 2010-04-05 03:53+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/uk.po b/bin/addons/base/i18n/uk.po index bb304a6d3a1..95eb2534544 100644 --- a/bin/addons/base/i18n/uk.po +++ b/bin/addons/base/i18n/uk.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-01 04:14+0000\n" +"PO-Revision-Date: 2010-04-04 04:27+0000\n" "Last-Translator: Fabien (Open ERP) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" +"X-Launchpad-Export-Date: 2010-04-05 03:53+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/zh_CN.po b/bin/addons/base/i18n/zh_CN.po index 18feb650de8..67b91d630c9 100644 --- a/bin/addons/base/i18n/zh_CN.po +++ b/bin/addons/base/i18n/zh_CN.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-01 04:12+0000\n" +"PO-Revision-Date: 2010-04-04 04:43+0000\n" "Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" +"X-Launchpad-Export-Date: 2010-04-05 03:53+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base From d984b6d8d0e4fa589c3d3f176e470b69a228bb13 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Mon, 5 Apr 2010 11:31:06 +0530 Subject: [PATCH 39/99] Removed print statement bzr revid: rvo@tinyerp.co.in-20100405060106-pz1vz05wue71p0kc --- bin/osv/orm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index af3ca7ec6dc..ebc24530cd9 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -1119,7 +1119,6 @@ class orm_template(object): for r in res.keys(): if r not in fields: del res[r] - print 'Result from fields get', res return res # @@ -2902,7 +2901,7 @@ class orm(orm_template): :raise UserError: if the record is default property for other records """ - + if not ids: return True if isinstance(ids, (int, long)): From 06560539e9d7719aa07ff074de72cf3db96e52cc Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Mon, 5 Apr 2010 10:17:47 +0200 Subject: [PATCH 40/99] [IMP] Removed print. bzr revid: jth@openerp.com-20100405081747-spggk7u9xe7t8bur --- bin/tools/yaml_tag.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/tools/yaml_tag.py b/bin/tools/yaml_tag.py index 1d36dc17a5b..97b29db1ac0 100644 --- a/bin/tools/yaml_tag.py +++ b/bin/tools/yaml_tag.py @@ -88,7 +88,6 @@ class Eval(YamlTag): class Ref(YamlTag): def __init__(self, *args, **kwargs): - print "kwargs %s" % kwargs super(Ref, self).__init__(*args, **kwargs) class IrSet(YamlTag): From 877fde1280102e1154402b2e00c8347276fba33f Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Mon, 5 Apr 2010 15:44:13 +0530 Subject: [PATCH 41/99] [FIX] Unlink : Corrected code for proper query lp bug: https://launchpad.net/bugs/553258 fixed bzr revid: jvo@tinyerp.com-20100405101413-bvcus7jamjcmseex --- bin/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 92fb7f9400b..4f7e5fefdae 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -2785,7 +2785,7 @@ class orm(orm_template): self.check_access_rule(cr, uid, ids, 'unlink', context=context) for sub_ids in cr.split_for_in_conditions(ids): cr.execute('delete from ' + self._table + ' ' \ - 'where id in %s', sub_ids) + 'where id in %s', (sub_ids,)) for order, object, store_ids, fields in result_store: if object != self._name: From 1b3c687e4bd1d823c389b0302ecbd1bd8dba81de Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Mon, 5 Apr 2010 23:14:19 +0200 Subject: [PATCH 42/99] [REF] Extract some functions before adding new things. bzr revid: jth@openerp.com-20100405211419-yokve9t42fna43n3 --- bin/addons/__init__.py | 70 +++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index e07fdd07bbc..e3550cd7018 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -591,6 +591,43 @@ class MigrationManager(object): def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): + + def process_sql_file(cr, file): + queries = fp.read().split(';') + for query in queries: + new_query = ' '.join(query.split()) + if new_query: + cr.execute(new_query) + + def load_init_update_xml(cr, m, idref, mode, kind): + for filename in package.data.get('%s_xml' % kind, []): + logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename)) + _, ext = os.path.splitext(filename) + fp = tools.file_open(opj(m, filename)) + if ext == '.csv': + noupdate = (kind == 'init') + tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate) + elif ext == '.sql': + process_sql_file(cr, fp) + elif ext == '.yml': + tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs) + else: + tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs) + fp.close() + + def load_demo_xml(cr, m, idref, mode): + for xml in package.data.get('demo_xml', []): + name, ext = os.path.splitext(xml) + logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml)) + fp = tools.file_open(opj(m, xml)) + if ext == '.csv': + tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True) + elif ext == '.yml': + tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs) + else: + tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs) + fp.close() + # **kwargs is passed directly to convert_xml_import if not status: status = {} @@ -647,39 +684,10 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): 'license': package.data.get('license', 'GPL-2'), 'certificate': package.data.get('certificate') or None, }) - for filename in package.data.get('%s_xml' % kind, []): - logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename)) - name, ext = os.path.splitext(filename) - fp = tools.file_open(opj(m, filename)) - if ext == '.csv': - noupdate=False - if kind == 'init': - noupdate=True - tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate) - elif ext == '.sql': - queries = fp.read().split(';') - for query in queries: - new_query = ' '.join(query.split()) - if new_query: - cr.execute(new_query) - elif ext == '.yml': - tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs) - else: - tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs) - fp.close() + load_init_update_xml(cr, m, idref, mode, kind) if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'): status['progress'] = (float(statusi)+0.75) / len(graph) - for xml in package.data.get('demo_xml', []): - name, ext = os.path.splitext(xml) - logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml)) - fp = tools.file_open(opj(m, xml)) - if ext == '.csv': - tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True) - elif ext == '.yml': - tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs) - else: - tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs) - fp.close() + load_demo_xml(cr, m, idref, mode) cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid)) package_todo.append(package.name) From 7fcd252f16c391fefdba22e6ee526eac0d8030da Mon Sep 17 00:00:00 2001 From: Launchpad Code Hosting Date: Tue, 6 Apr 2010 05:01:20 +0100 Subject: [PATCH 43/99] Launchpad automatic translations update. bzr revid: codehost@crowberry-20100406040120-ce71kl30h2qvczxq --- bin/addons/base/i18n/he.po | 4 ++-- bin/addons/base/i18n/hr.po | 4 ++-- bin/addons/base/i18n/ja.po | 4 ++-- bin/addons/base/i18n/sv.po | 4 ++-- bin/addons/base/i18n/uk.po | 4 ++-- bin/addons/base/i18n/zh_CN.po | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/addons/base/i18n/he.po b/bin/addons/base/i18n/he.po index b068412415d..1e3765b48e3 100644 --- a/bin/addons/base/i18n/he.po +++ b/bin/addons/base/i18n/he.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-04 19:08+0000\n" +"PO-Revision-Date: 2010-04-05 04:29+0000\n" "Last-Translator: Ddorda \n" "Language-Team: Hebrew \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-05 03:52+0000\n" +"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/hr.po b/bin/addons/base/i18n/hr.po index 7d42d7b1df1..51a7f1d772a 100644 --- a/bin/addons/base/i18n/hr.po +++ b/bin/addons/base/i18n/hr.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-04 04:38+0000\n" +"PO-Revision-Date: 2010-04-05 04:33+0000\n" "Last-Translator: goranc \n" "Language-Team: openerp-translators\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-05 03:52+0000\n" +"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n" "X-Generator: Launchpad (build Unknown)\n" "Language: hr\n" diff --git a/bin/addons/base/i18n/ja.po b/bin/addons/base/i18n/ja.po index 554dd6419e7..f55114223a5 100644 --- a/bin/addons/base/i18n/ja.po +++ b/bin/addons/base/i18n/ja.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-04 04:23+0000\n" +"PO-Revision-Date: 2010-04-05 04:34+0000\n" "Last-Translator: Harry (Open ERP) \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-05 03:52+0000\n" +"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/sv.po b/bin/addons/base/i18n/sv.po index 40d0f5d2420..04fc6b3d022 100644 --- a/bin/addons/base/i18n/sv.po +++ b/bin/addons/base/i18n/sv.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-04 04:19+0000\n" +"PO-Revision-Date: 2010-04-05 04:37+0000\n" "Last-Translator: Anders Wallenquist \n" "Language-Team: <>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-05 03:53+0000\n" +"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/uk.po b/bin/addons/base/i18n/uk.po index 95eb2534544..2557ca954df 100644 --- a/bin/addons/base/i18n/uk.po +++ b/bin/addons/base/i18n/uk.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-04 04:27+0000\n" +"PO-Revision-Date: 2010-04-05 04:31+0000\n" "Last-Translator: Fabien (Open ERP) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-05 03:53+0000\n" +"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/zh_CN.po b/bin/addons/base/i18n/zh_CN.po index 67b91d630c9..3010205f573 100644 --- a/bin/addons/base/i18n/zh_CN.po +++ b/bin/addons/base/i18n/zh_CN.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-04-04 04:43+0000\n" +"PO-Revision-Date: 2010-04-05 04:30+0000\n" "Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-04-05 03:53+0000\n" +"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base From 13a43dfdc36b751cb9ed6c55169e76b48094b829 Mon Sep 17 00:00:00 2001 From: "DSH(OpenERP)" <> Date: Tue, 6 Apr 2010 12:03:36 +0530 Subject: [PATCH 44/99] Support tiny report tag in add_header, prevent server crash on wrong report type selection lp bug: https://launchpad.net/bugs/510834 fixed bzr revid: hda@tinyerp.com-20100406063336-sh9u8tyfqdybtrp7 --- bin/report/report_sxw.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index 98d960db1a4..a85bac84f5c 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -297,11 +297,7 @@ class rml_parse(object): rml_head = self.rml_header2 else: rml_head = self.rml_header - if self.logo and (rml_head.find('company.logo')<0 or rml_head.find('',''' [[company.logo]] ''') - if not self.logo and rml_head.find('company.logo')>=0: - rml_head = rml_head.replace('','') + head_dom = etree.XML(rml_head) for tag in head_dom: found = rml_dom.find('.//'+tag.tag) @@ -443,9 +439,10 @@ class report_sxw(report_rml, preprocess.report): rml_parser = self.parser(cr, uid, self.name2, context=context) objs = self.getObjects(cr, uid, ids, context) rml_parser.set_context(objs, data, ids, report_xml.report_type) - processed_rml = self.preprocess_rml(etree.XML(rml),report_xml.report_type) + processed_rml = etree.XML(rml) if report_xml.header: rml_parser._add_header(processed_rml) + processed_rml = self.preprocess_rml(processed_rml,report_xml.report_type) if rml_parser.logo: logo = base64.decodestring(rml_parser.logo) create_doc = self.generators[report_xml.report_type] @@ -462,13 +459,18 @@ class report_sxw(report_rml, preprocess.report): sxw_z = zipfile.ZipFile(sxw_io, mode='r') rml = sxw_z.read('content.xml') meta = sxw_z.read('meta.xml') + mime_type = sxw_z.read('mimetype') + if mime_type == 'application/vnd.sun.xml.writer': + mime_type = 'sxw' + else : + mime_type = 'odt' sxw_z.close() rml_parser = self.parser(cr, uid, self.name2, context=context) rml_parser.parents = sxw_parents rml_parser.tag = sxw_tag objs = self.getObjects(cr, uid, ids, context) - rml_parser.set_context(objs, data, ids,report_xml.report_type) + rml_parser.set_context(objs, data, ids, mime_type) rml_dom_meta = node = etree.XML(meta) elements = node.findall(rml_parser.localcontext['name_space']["meta"]+"user-defined") @@ -489,7 +491,7 @@ class report_sxw(report_rml, preprocess.report): for n in rml_dom.iterdescendants(): if n.tag == key1: elements.append(n) - if report_type == 'odt': + if mime_type == 'odt': for pe in elements: e = pe.findall(key2) for de in e: @@ -520,8 +522,8 @@ class report_sxw(report_rml, preprocess.report): if de.getparent(): pp.remove(de) - rml_dom = self.preprocess_rml(rml_dom,report_type) - create_doc = self.generators[report_type] + rml_dom = self.preprocess_rml(rml_dom, mime_type) + create_doc = self.generators[mime_type] odt = etree.tostring(create_doc(rml_dom, rml_parser.localcontext), encoding='utf-8', xml_declaration=True) sxw_z = zipfile.ZipFile(sxw_io, mode='a') @@ -547,7 +549,7 @@ class report_sxw(report_rml, preprocess.report): sxw_z.close() final_op = sxw_io.getvalue() sxw_io.close() - return (final_op, report_type) + return (final_op, mime_type) def create_single_html2html(self, cr, uid, ids, data, report_xml, context=None): if not context: From 1982e4bf3c559de1da266618e9b5de75eca9f14e Mon Sep 17 00:00:00 2001 From: Mantavya Gajjar Date: Tue, 6 Apr 2010 12:32:31 +0530 Subject: [PATCH 45/99] [REVIEW]: code review bzr revid: mga@tinyerp.com-20100406070231-zurxtsmvqk0szjv7 --- addons/survey/survey.py | 101 +++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/addons/survey/survey.py b/addons/survey/survey.py index f7bb9329436..a1db5bdcc86 100644 --- a/addons/survey/survey.py +++ b/addons/survey/survey.py @@ -97,7 +97,6 @@ class survey(osv.osv): survey() - class survey_history(osv.osv): _name = 'survey.history' _description = 'Survey History' @@ -110,7 +109,6 @@ class survey_history(osv.osv): _defaults = { 'date': lambda * a: datetime.datetime.now() } - survey_history() class survey_page(osv.osv): @@ -144,14 +142,14 @@ class survey_page(osv.osv): surv_name_wiz = self.pool.get('survey.name.wiz') surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'transfer':True, 'page_no' : context.get('page_number',0) }) return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'survey.question.wiz', - 'type': 'ir.actions.act_window', - 'target': 'new', - 'search_view_id': search_id[0], - 'context': context - } + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'survey.question.wiz', + 'type': 'ir.actions.act_window', + 'target': 'new', + 'search_view_id': search_id[0], + 'context': context + } def copy(self, cr, uid, id, default=None, context={}): raise osv.except_osv(_('Warning !'),_('You cannot duplicate the resource!')) @@ -196,28 +194,28 @@ class survey_question(osv.osv): 'descriptive_text': fields.text('Descriptive Text', size=255), 'column_heading_ids': fields.one2many('survey.question.column.heading', 'question_id',' Column heading'), 'type': fields.selection([('multiple_choice_only_one_ans','Multiple Choice (Only One Answer)'), - ('multiple_choice_multiple_ans','Multiple Choice (Multiple Answer)'), - ('matrix_of_choices_only_one_ans','Matrix of Choices (Only One Answers Per Row)'), - ('matrix_of_choices_only_multi_ans','Matrix of Choices (Multiple Answers Per Row)'), - ('matrix_of_drop_down_menus','Matrix of Drop-down Menus'), - ('rating_scale','Rating Scale'),('single_textbox','Single Textbox'), - ('multiple_textboxes','Multiple Textboxes'), - ('multiple_textboxes_diff_type','Multiple Textboxes With Different Type'), - ('comment','Comment/Essay Box'), - ('numerical_textboxes','Numerical Textboxes'),('date','Date'), - ('date_and_time','Date and Time'),('descriptive_text','Descriptive Text'), - ('table','Table'), - ], 'Question Type', required=1,), + ('multiple_choice_multiple_ans','Multiple Choice (Multiple Answer)'), + ('matrix_of_choices_only_one_ans','Matrix of Choices (Only One Answers Per Row)'), + ('matrix_of_choices_only_multi_ans','Matrix of Choices (Multiple Answers Per Row)'), + ('matrix_of_drop_down_menus','Matrix of Drop-down Menus'), + ('rating_scale','Rating Scale'),('single_textbox','Single Textbox'), + ('multiple_textboxes','Multiple Textboxes'), + ('multiple_textboxes_diff_type','Multiple Textboxes With Different Type'), + ('comment','Comment/Essay Box'), + ('numerical_textboxes','Numerical Textboxes'),('date','Date'), + ('date_and_time','Date and Time'),('descriptive_text','Descriptive Text'), + ('table','Table'), + ], 'Question Type', required=1,), 'is_comment_require': fields.boolean('Add Comment Field (optional)'), 'comment_label': fields.char('Field Label', size = 255), 'comment_field_type': fields.selection([('char', 'Single Line Of Text'), ('text', 'Paragraph of Text')], 'Comment Field Type'), 'comment_valid_type': fields.selection([('do_not_validate', '''Don't Validate Comment Text.'''), - ('must_be_specific_length', 'Must Be Specific Length'), - ('must_be_whole_number', 'Must Be A Whole Number'), - ('must_be_decimal_number', 'Must Be A Decimal Number'), - ('must_be_date', 'Must Be A Date'), - ('must_be_email_address', 'Must Be An Email Address'), - ], 'Text Validation'), + ('must_be_specific_length', 'Must Be Specific Length'), + ('must_be_whole_number', 'Must Be A Whole Number'), + ('must_be_decimal_number', 'Must Be A Decimal Number'), + ('must_be_date', 'Must Be A Date'), + ('must_be_email_address', 'Must Be An Email Address'), + ], 'Text Validation'), 'comment_minimum_no': fields.integer('Minimum number'), 'comment_maximum_no': fields.integer('Maximum number'), 'comment_minimum_float': fields.float('Minimum decimal number'), @@ -229,12 +227,12 @@ class survey_question(osv.osv): 'make_comment_field_err_msg': fields.text('Error message'), 'is_validation_require': fields.boolean('Validate Text (optional)'), 'validation_type': fields.selection([('do_not_validate', '''Don't Validate Comment Text.'''),\ - ('must_be_specific_length', 'Must Be Specific Length'),\ - ('must_be_whole_number', 'Must Be A Whole Number'),\ - ('must_be_decimal_number', 'Must Be A Decimal Number'),\ - ('must_be_date', 'Must Be A Date'),\ - ('must_be_email_address', 'Must Be An Email Address')\ - ], 'Text Validation'), + ('must_be_specific_length', 'Must Be Specific Length'),\ + ('must_be_whole_number', 'Must Be A Whole Number'),\ + ('must_be_decimal_number', 'Must Be A Decimal Number'),\ + ('must_be_date', 'Must Be A Date'),\ + ('must_be_email_address', 'Must Be An Email Address')\ + ], 'Text Validation'), 'validation_minimum_no': fields.integer('Minimum number'), 'validation_maximum_no': fields.integer('Maximum number'), 'validation_minimum_float': fields.float('Minimum decimal number'), @@ -437,14 +435,14 @@ class survey_question(osv.osv): surv_name_wiz = self.pool.get('survey.name.wiz') surv_name_wiz.write(cr, uid, [context.get('sur_name_id',False)], {'transfer':True, 'page_no' : context.get('page_number',False) }) return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'survey.question.wiz', - 'type': 'ir.actions.act_window', - 'target': 'new', - 'search_view_id': search_id[0], - 'context': context - } + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'survey.question.wiz', + 'type': 'ir.actions.act_window', + 'target': 'new', + 'search_view_id': search_id[0], + 'context': context + } def default_get(self, cr, uid, fields, context={}): data = super(survey_question, self).default_get(cr, uid, fields, context) @@ -453,7 +451,7 @@ class survey_question(osv.osv): data['sequence'] = context['line_order'][-1][2]['sequence'] + 1 if context.has_key('page_id'): - data['page_id']= context.get('page_id',False) + data['page_id']= context.get('page_id', False) return data survey_question() @@ -465,11 +463,11 @@ class survey_question_column_heading(osv.osv): _rec_name = 'title' def _get_in_visible_rating_weight(self,cr, uid, context={}): - if context.get('in_visible_rating_weight',False): + if context.get('in_visible_rating_weight', False): return context['in_visible_rating_weight'] return False def _get_in_visible_menu_choice(self,cr, uid, context={}): - if context.get('in_visible_menu_choice',False): + if context.get('in_visible_menu_choice', False): return context['in_visible_menu_choice'] return False @@ -485,7 +483,6 @@ class survey_question_column_heading(osv.osv): 'in_visible_rating_weight': _get_in_visible_rating_weight, 'in_visible_menu_choice': _get_in_visible_menu_choice, } - survey_question_column_heading() class survey_answer(osv.osv): @@ -496,7 +493,7 @@ class survey_answer(osv.osv): def _calc_response_avg(self, cr, uid, ids, field_name, arg, context): val = {} - for rec in self.browse(cr, uid, ids): + for rec in self.browse(cr, uid, ids, context): cr.execute("select count(question_id) ,(select count(answer_id) \ from survey_response_answer sra, survey_response_line sa \ where sra.response_id = sa.id and sra.answer_id = %d \ @@ -515,9 +512,7 @@ class survey_answer(osv.osv): return val def _get_in_visible_answer_type(self,cr, uid, context={}): - if context.get('in_visible_answer_type',False): - return context.get('in_visible_answer_type',False) - return False + return context.get('in_visible_answer_type', False) _columns = { 'question_id': fields.many2one('survey.question', 'Question', ondelete='cascade'), @@ -526,8 +521,8 @@ class survey_answer(osv.osv): 'response': fields.function(_calc_response_avg, method=True, string="#Answer", multi='sums'), 'average': fields.function(_calc_response_avg, method=True, string="#Avg", multi='sums'), 'type': fields.selection([('char','Character'),('date','Date'),('datetime','Date & Time'),\ - ('integer','Integer'),('float','Float'),('selection','Selection'),\ - ('email','Email')], "Type of Answer",required=1), + ('integer','Integer'),('float','Float'),('selection','Selection'),\ + ('email','Email')], "Type of Answer",required=1), 'menu_choice': fields.text('Menu Choices'), 'in_visible_answer_type': fields.boolean('Is Answer Type Invisible??') } @@ -539,7 +534,7 @@ class survey_answer(osv.osv): def default_get(self, cr, uid, fields, context={}): data = super(survey_answer, self).default_get(cr, uid, fields, context) - if context.get('line_order',False): + if context.get('line_order', False): if len(context['line_order'][-1]) > 2 and type(context['line_order'][-1][2]) == type({}) and context['line_order'][-1][2].has_key('sequence'): data['sequence'] = context['line_order'][-1][2]['sequence'] + 1 return data From e92975499ffbda6e9baed7f7cf08538611e6e239 Mon Sep 17 00:00:00 2001 From: "nel@tinyerp.com" <> Date: Tue, 6 Apr 2010 09:17:43 +0200 Subject: [PATCH 46/99] [FIX] context in read_group bzr revid: nel@tinyerp.com-20100406071743-qe507a4o2iwhgpd7 --- bin/osv/orm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 4f7e5fefdae..37432a9ff90 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -1936,7 +1936,8 @@ class orm(orm_template): for d in data: d['__domain'] = [(groupby,'=',alldata[d['id']][groupby] or False)] + domain - d['__context'] = {'group_by':groupby_list[1:]} + if not isinstance(groupby_list,(str, unicode)): + d['__context'] = {'group_by':groupby_list[1:]} if fget.has_key(groupby): if d[groupby] and fget[groupby]['type'] in ('date','datetime'): dt = datetime.datetime.strptime(alldata[d['id']][groupby][:7],'%Y-%m') From 59e15ebce0e9c5f70378adef965a046c48703189 Mon Sep 17 00:00:00 2001 From: Mantavya Gajjar Date: Tue, 6 Apr 2010 14:31:56 +0530 Subject: [PATCH 47/99] [REVIEW]: code review bzr revid: mga@tinyerp.com-20100406090156-3wesrbw3ue8g739f --- addons/base_calendar/base_calendar.py | 339 +++++++++++++------------- 1 file changed, 169 insertions(+), 170 deletions(-) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index f7608219b9d..6a29b023301 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -31,9 +31,10 @@ import time import tools months = { - 1: "January", 2: "February", 3: "March", 4: "April", \ - 5: "May", 6: "June", 7: "July", 8: "August", 9: "September", \ - 10: "October", 11: "November", 12: "December"} + 1: "January", 2: "February", 3: "March", 4: "April", \ + 5: "May", 6: "June", 7: "July", 8: "August", 9: "September", \ + 10: "October", 11: "November", 12: "December" +} def get_recurrent_dates(rrulestring, exdate, startdate=None): """ @@ -94,7 +95,6 @@ def real_id2base_calendar_id(real_id, recurrent_date): return '%d-%s' % (real_id, recurrent_date) return real_id - def _links_get(self, cr, uid, context={}): """ Get request link. @@ -385,9 +385,11 @@ request was delegated to"), response_re = re.compile("Are you coming\?.*\n*.*(YES|NO|MAYBE).*", re.UNICODE) def msg_new(self, cr, uid, msg): - """ @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, """ + """ + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + """ return False def msg_act_get(self, msg): @@ -446,25 +448,25 @@ request was delegated to"), (att2.partner_id and att2.partner_id.name) or \ att2.email) + ' - Status: ' + att2.state.title()) body_vals = {'name': res_obj.name, - 'start_date': res_obj.date, - 'end_date': res_obj.date_deadline or False, - 'description': res_obj.description or '-', - 'location': res_obj.location or '-', - 'attendees': '
'.join(att_infos), - 'user': res_obj.user_id and res_obj.user_id.name or 'OpenERP User', - 'sign': sign, - 'company': company + 'start_date': res_obj.date, + 'end_date': res_obj.date_deadline or False, + 'description': res_obj.description or '-', + 'location': res_obj.location or '-', + 'attendees': '
'.join(att_infos), + 'user': res_obj.user_id and res_obj.user_id.name or 'OpenERP User', + 'sign': sign, + 'company': company } body = html_invitation % body_vals if mail_to and email_from: tools.email_send( - email_from, - mail_to, - sub, - body, - subtype='html', - reply_to=email_from - ) + email_from, + mail_to, + sub, + body, + subtype='html', + reply_to=email_from + ) return True def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv): """ @@ -507,7 +509,6 @@ request was delegated to"), for vals in self.browse(cr, uid, ids, context=context): user = vals.user_id if user: - mod_obj = self.pool.get(vals.ref._name) if vals.ref: if vals.ref.user_id.id != user.id: @@ -562,9 +563,7 @@ class res_alarm(osv.osv): are both optional, but if one occurs, so MUST the other"""), 'repeat': fields.integer('Repeat'), 'active': fields.boolean('Active', help="If the active field is set to \ - true, it will allow you to hide the event alarm information without removing it."), - - + true, it will allow you to hide the event alarm information without removing it.") } _defaults = { 'trigger_interval': lambda *x: 'minutes', @@ -590,10 +589,10 @@ are both optional, but if one occurs, so MUST the other"""), model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0] model_obj = self.pool.get(model) - for data in model_obj.browse(cr, uid, ids): + for data in model_obj.browse(cr, uid, ids, context): basic_alarm = data.alarm_id - if not context.get('alarm_id'): + if not context.get('alarm_id', False): self.do_alarm_unlink(cr, uid, [data.id], model) return True self.do_alarm_unlink(cr, uid, [data.id], model) @@ -636,7 +635,7 @@ are both optional, but if one occurs, so MUST the other"""), ir_obj = self.pool.get('ir.model') model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0] model_obj = self.pool.get(model) - for datas in model_obj.browse(cr, uid, ids): + for datas in model_obj.browse(cr, uid, ids, context): alarm_ids = alarm_obj.search(cr, uid, [('model_id', '=', model_id), ('res_id', '=', datas.id)]) if alarm_ids: alarm_obj.unlink(cr, uid, alarm_ids) @@ -654,35 +653,35 @@ class calendar_alarm(osv.osv): __attribute__ = {} _columns = { - 'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'), - 'name': fields.char('Summary', size=124, help="""Contains the text to be \ - used as the message subject for email \ - or contains the text to be used for display"""), - 'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \ - ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \ - required=True, help="Defines the action to be invoked when an alarm is triggered"), - 'description': fields.text('Description', help='Provides a more complete \ - description of the calendar component, than that \ - provided by the "SUMMARY" property'), - 'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \ - 'alarm_id', 'attendee_id', 'Attendees', readonly=True), - 'attach': fields.binary('Attachment', help="""* Points to a sound resource,\ - which is rendered when the alarm is triggered for audio, - * File which is intended to be sent as message attachments for email, - * Points to a procedure resource, which is invoked when\ - the alarm is triggered for procedure."""), - 'res_id': fields.integer('Resource ID'), - 'model_id': fields.many2one('ir.model', 'Model'), - 'user_id': fields.many2one('res.users', 'Owner'), - 'event_date': fields.datetime('Event Date'), - 'event_end_date': fields.datetime('Event End Date'), - 'trigger_date': fields.datetime('Trigger Date', readonly="True"), - 'state':fields.selection([ - ('draft', 'Draft'), - ('run', 'Run'), - ('stop', 'Stop'), - ('done', 'Done'), - ], 'State', select=True, readonly=True), + 'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'), + 'name': fields.char('Summary', size=124, help="""Contains the text to be \ + used as the message subject for email \ + or contains the text to be used for display"""), + 'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \ + ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \ + required=True, help="Defines the action to be invoked when an alarm is triggered"), + 'description': fields.text('Description', help='Provides a more complete \ + description of the calendar component, than that \ + provided by the "SUMMARY" property'), + 'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \ + 'alarm_id', 'attendee_id', 'Attendees', readonly=True), + 'attach': fields.binary('Attachment', help="""* Points to a sound resource,\ + which is rendered when the alarm is triggered for audio, + * File which is intended to be sent as message attachments for email, + * Points to a procedure resource, which is invoked when\ + the alarm is triggered for procedure."""), + 'res_id': fields.integer('Resource ID'), + 'model_id': fields.many2one('ir.model', 'Model'), + 'user_id': fields.many2one('res.users', 'Owner'), + 'event_date': fields.datetime('Event Date'), + 'event_end_date': fields.datetime('Event End Date'), + 'trigger_date': fields.datetime('Trigger Date', readonly="True"), + 'state':fields.selection([ + ('draft', 'Draft'), + ('run', 'Run'), + ('stop', 'Stop'), + ('done', 'Done'), + ], 'State', select=True, readonly=True), } _defaults = { @@ -717,15 +716,14 @@ class calendar_alarm(osv.osv): def do_run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, \ context=None): """ - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of calendar alarm’s IDs. - @param use_new_cursor: False or the dbname - @param context: A standard dictionary for contextual values + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of calendar alarm’s IDs. + @param use_new_cursor: False or the dbname + @param context: A standard dictionary for contextual values """ - if not context: context = {} current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S') @@ -793,7 +791,6 @@ class calendar_event(osv.osv): def _tz_get(self, cr, uid, context={}): return [(x.lower(), x) for x in pytz.all_timezones] - def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, context={}): """ @param self: The object pointer @@ -892,10 +889,10 @@ class calendar_event(osv.osv): whr = " where id=%(id)s" qry = qry + whr val.update({ - 'table': self._table, - 'rule_type': rrule_type, - 'id': id, - }) + 'table': self._table, + 'rule_type': rrule_type, + 'id': id, + }) cr.execute(qry % val) return True @@ -909,9 +906,7 @@ class calendar_event(osv.osv): @return: dictionary of rrule value. """ result = {} - for event in ids: - - datas = self.read(cr, uid, event) + for datas in self.read(cr, uid, ids): if datas.get('rrule_type'): if datas.get('rrule_type') == 'none': result[event] = False @@ -935,11 +930,10 @@ class calendar_event(osv.osv): 'duration': fields.float('Duration'), 'description': fields.text('Your action'), 'class': fields.selection([('public', 'Public'), ('private', 'Private'), \ - ('confidential', 'Confidential')], 'Mark as'), + ('confidential', 'Confidential')], 'Mark as'), 'location': fields.char('Location', size=264, help="Location of Event"), 'show_as': fields.selection([('free', 'Free'), \ - ('busy', 'Busy')], - 'Show as'), + ('busy', 'Busy')], 'Show as'), 'base_calendar_url': fields.char('Caldav URL', size=264), 'exdate': fields.text('Exception Date/Times', help="This property \ defines the list of date/time exceptions for arecurring calendar component."), @@ -948,8 +942,8 @@ class calendar_event(osv.osv): 'rrule': fields.function(_get_rulestring, type='char', size=124, method=True,\ string='Recurrent Rule', store=True, fnct_inv=_set_rrulestring), 'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \ - ('weekly', 'Weekly'), ('monthly', 'Monthly'), \ - ('yearly', 'Yearly'), ('custom', 'Custom')], 'Recurrency'), + ('weekly', 'Weekly'), ('monthly', 'Monthly'), \ + ('yearly', 'Yearly'), ('custom', 'Custom')], 'Recurrency'), 'alarm_id': fields.many2one('res.alarm', 'Alarm'), 'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'), 'recurrent_uid': fields.integer('Recurrent ID'), @@ -958,13 +952,13 @@ class calendar_event(osv.osv): string='Timezone', store=True), 'user_id': fields.many2one('res.users', 'Responsible'), 'freq': fields.selection([('None', 'No Repeat'), \ - ('secondly', 'Secondly'), \ - ('minutely', 'Minutely'), \ - ('hourly', 'Hourly'), \ - ('daily', 'Daily'), \ - ('weekly', 'Weekly'), \ - ('monthly', 'Monthly'), \ - ('yearly', 'Yearly')], 'Frequency'), + ('secondly', 'Secondly'), \ + ('minutely', 'Minutely'), \ + ('hourly', 'Hourly'), \ + ('daily', 'Daily'), \ + ('weekly', 'Weekly'), \ + ('monthly', 'Monthly'), \ + ('yearly', 'Yearly')], 'Frequency'), 'interval': fields.integer('Interval'), 'count': fields.integer('Count'), 'mo': fields.boolean('Mon'), @@ -975,19 +969,18 @@ class calendar_event(osv.osv): 'sa': fields.boolean('Sat'), 'su': fields.boolean('Sun'), 'select1': fields.selection([('date', 'Date of month'), \ - ('day', 'Day of month')], 'Option'), + ('day', 'Day of month')], 'Option'), 'day': fields.integer('Date of month'), 'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \ - ('WE', 'Wednesday'), ('TH', 'Thursday'), \ - ('FR', 'Friday'), ('SA', 'Saturday'), \ - ('SU', 'Sunday')], 'Weekday'), + ('WE', 'Wednesday'), ('TH', 'Thursday'), \ + ('FR', 'Friday'), ('SA', 'Saturday'), \ + ('SU', 'Sunday')], 'Weekday'), 'byday': fields.selection([('1', 'First'), ('2', 'Second'), \ - ('3', 'Third'), ('4', 'Fourth'), \ - ('5', 'Fifth'), ('-1', 'Last')], 'By day'), + ('3', 'Third'), ('4', 'Fourth'), \ + ('5', 'Fifth'), ('-1', 'Last')], 'By day'), 'month_list': fields.selection(months.items(), 'Month'), 'end_date': fields.date('Repeat Until') } - _defaults = { 'class': lambda *a: 'public', 'show_as': lambda *a: 'busy', @@ -1011,11 +1004,11 @@ class calendar_event(osv.osv): event_id = base_calendar_id2real_id(event_id) datas = self.read(cr, uid, event_id, context=context) defaults.update({ - 'recurrent_uid': base_calendar_id2real_id(datas['id']), - 'recurrent_id': defaults.get('date') or real_date, - 'rrule_type': 'none', - 'rrule': '' - }) + 'recurrent_uid': base_calendar_id2real_id(datas['id']), + 'recurrent_id': defaults.get('date') or real_date, + 'rrule_type': 'none', + 'rrule': '' + }) exdate = datas['exdate'] and datas['exdate'].split(',') or [] if real_date and defaults.get('date'): exdate.append(real_date) @@ -1031,7 +1024,7 @@ class calendar_event(osv.osv): @param event_ids: List of crm meeting’s IDs. @return: True """ - #start Loop + for event_id in event_ids: event_id = base_calendar_id2real_id(event_id) @@ -1046,7 +1039,7 @@ class calendar_event(osv.osv): qry += ", location = '%(location)s'" qry += "WHERE id = %s" % (event_id) cr.execute(qry %(defaults)) - #End Loop + return True def get_recurrent_ids(self, cr, uid, select, base_start_date, base_until_date, limit=100): @@ -1143,7 +1136,7 @@ class calendar_event(osv.osv): weekstring = '' monthstring = '' yearstring = '' -# logic for computing rrule string + freq = datas.get('freq') if freq == 'None': return '' @@ -1183,7 +1176,6 @@ class calendar_event(osv.osv): rrule_string = 'FREQ=' + freq.upper() + weekstring + ';INTERVAL=' + \ str(datas.get('interval')) + enddate + monthstring + yearstring -# End logic return rrule_string @@ -1376,13 +1368,14 @@ class calendar_todo(osv.osv): _description = "Calendar Task" def _get_date(self, cr, uid, ids, name, arg, context): - """ Get Date - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of calendar todo's IDs. - @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. - @param context: A standard dictionary for contextual values + """ + Get Date + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of calendar todo's IDs. + @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. + @param context: A standard dictionary for contextual values """ res = {} @@ -1391,14 +1384,15 @@ class calendar_todo(osv.osv): return res def _set_date(self, cr, uid, id, name, value, arg, context): - """ Set Date - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param id: calendar's ID. - @param value: Get Value - @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. - @param context: A standard dictionary for contextual values + """ + Set Date + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param id: calendar's ID. + @param value: Get Value + @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. + @param context: A standard dictionary for contextual values """ event = self.browse(cr, uid, id, context=context) @@ -1422,11 +1416,12 @@ class ir_attachment(osv.osv): _inherit = 'ir.attachment' def search_count(self, cr, user, args, context=None): - """ @param self: The object pointer - @param cr: the current row, from the database cursor, - @param user: the current user’s ID for security checks, - @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. - @param context: A standard dictionary for contextual values + """ + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param user: the current user’s ID for security checks, + @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. + @param context: A standard dictionary for contextual values """ args1 = [] @@ -1436,14 +1431,14 @@ class ir_attachment(osv.osv): def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): - - """ @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. - @param offset: The Number of Results to pass, - @param limit: The Number of Results to Return, - @param context: A standard dictionary for contextual values + """ + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...]. + @param offset: The Number of Results to pass, + @param limit: The Number of Results to Return, + @param context: A standard dictionary for contextual values """ new_args = args @@ -1460,12 +1455,13 @@ class ir_values(osv.osv): def set(self, cr, uid, key, key2, name, models, value, replace=True, \ isobject=False, meta=False, preserve_user=False, company=False): - - """ set IR Values - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param model: Get The Model """ + """ + Set IR Values + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param model: Get The Model + """ new_model = [] for data in models: @@ -1478,12 +1474,13 @@ class ir_values(osv.osv): def get(self, cr, uid, key, key2, models, meta=False, context={}, \ res_id_req=False, without_user=True, key2_req=True): - - """ Get IR Values - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param model: Get The Model """ + """ + Get IR Values + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param model: Get The Model + """ new_model = [] for data in models: @@ -1502,14 +1499,14 @@ class ir_model(osv.osv): def read(self, cr, uid, ids, fields=None, context={}, load='_classic_read'): - - """ Read IR Model - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of IR Model’s IDs. - @param context: A standard dictionary for contextual values """ - + """ + Read IR Model + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of IR Model’s IDs. + @param context: A standard dictionary for contextual values + """ data = super(ir_model, self).read(cr, uid, ids, fields=fields, \ context=context, load=load) @@ -1523,13 +1520,13 @@ ir_model() class virtual_report_spool(web_services.report_spool): def exp_report(self, db, uid, object, ids, datas=None, context=None): - - """ Export Report - @param self: The object pointer - @param db: get the current database, - @param uid: the current user’s ID for security checks, - @param context: A standard dictionary for contextual values """ - + """ + Export Report + @param self: The object pointer + @param db: get the current database, + @param uid: the current user’s ID for security checks, + @param context: A standard dictionary for contextual values + """ if object == 'printscreen.list': return super(virtual_report_spool, self).exp_report(db, uid, \ @@ -1547,13 +1544,14 @@ class res_users(osv.osv): _inherit = 'res.users' def _get_user_avail(self, cr, uid, ids, context=None): - - """ Get USer Availability - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of res user’s IDs. - @param context: A standard dictionary for contextual values """ + """ + Get USer Availability + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of res user’s IDs. + @param context: A standard dictionary for contextual values + """ current_datetime = datetime.now().strftime('%Y-%m-%d %H:%M:%S') res = {} @@ -1577,13 +1575,14 @@ class res_users(osv.osv): return res def _get_user_avail_fun(self, cr, uid, ids, name, args, context=None): - - """ Get USer Availability Function - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of res user’s IDs. - @param context: A standard dictionary for contextual values """ + """ + Get USer Availability Function + @param self: The object pointer + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of res user’s IDs. + @param context: A standard dictionary for contextual values + """ return self._get_user_avail(cr, uid, ids, context=context) From b7133378a2281aca219357c8da27a5455accb309 Mon Sep 17 00:00:00 2001 From: "DSH(OpenERP)" <> Date: Tue, 6 Apr 2010 15:03:56 +0530 Subject: [PATCH 48/99] Added removeParentNode in company header logo bzr revid: hda@tinyerp.com-20100406093356-lz9cdk9ldswks2m3 --- bin/addons/base/res/res_company.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/addons/base/res/res_company.py b/bin/addons/base/res/res_company.py index dccf08ed2ed..ea64aa5eb16 100644 --- a/bin/addons/base/res/res_company.py +++ b/bin/addons/base/res/res_company.py @@ -209,7 +209,7 @@ class res_company(osv.osv): - [[company.logo]] + [[ company.logo or removeParentNode('image') ]] From f29491458bee47fa1c2e1770f6ac0d291936c3d8 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Tue, 6 Apr 2010 12:24:45 +0200 Subject: [PATCH 49/99] [ADD] Tests declared in 'test' section of __terp__ are rolled back at the end of the module processing. [FIX] Removed an unnecessary commit in ir_sequence.get_id (replaced by SELECT ... FOR UPDATE NOWAIT): commit was actually breaking the test rollback behaviour. bzr revid: jth@openerp.com-20100406102445-le227exvrphahbx5 --- bin/addons/__init__.py | 35 +++++++++++++++++++++++++++++++ bin/addons/base/ir/ir_sequence.py | 21 ++++++++----------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index e3550cd7018..2b0f90bc5cf 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -589,6 +589,7 @@ class MigrationManager(object): if mod: del mod +log = logging.getLogger('init') def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): @@ -628,6 +629,35 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs) fp.close() + def load_data(cr, module_name, id_map, mode): + _load_data(cr, module_name, id_map, mode, 'data') + + def load_demo(cr, module_name, id_map, mode): + _load_data(cr, module_name, id_map, mode, 'demo') + + def load_test(cr, module_name, id_map, mode): + cr.commit() + try: + _load_data(cr, module_name, id_map, mode, 'test') + finally: + cr.rollback() + + def _load_data(cr, module_name, id_map, mode, kind): + noupdate = (kind == 'demo') + for filename in package.data.get(kind, []): + _, ext = os.path.splitext(filename) + log.info("module %s: loading %s", module_name, filename) + pathname = os.path.join(module_name, filename) + file = tools.file_open(pathname) + # TODO manage .csv file with noupdate == (kind == 'init') + if ext == '.sql': + process_sql_file(cr, fp) + elif ext == '.yml': + tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate) + else: + tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate) + file.close() + # **kwargs is passed directly to convert_xml_import if not status: status = {} @@ -685,10 +715,15 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs): 'certificate': package.data.get('certificate') or None, }) load_init_update_xml(cr, m, idref, mode, kind) + load_data(cr, m, idref, mode) if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'): status['progress'] = (float(statusi)+0.75) / len(graph) load_demo_xml(cr, m, idref, mode) + load_demo(cr, m, idref, mode) cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid)) + + load_test(cr, m, idref, mode) + package_todo.append(package.name) migrations.migrate_module(package, 'post') diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py index 8b0e7795a7c..fcc197116a1 100644 --- a/bin/addons/base/ir/ir_sequence.py +++ b/bin/addons/base/ir/ir_sequence.py @@ -73,18 +73,15 @@ class ir_sequence(osv.osv): } def get_id(self, cr, uid, sequence_id, test='id', context=None): - try: - assert test in ('code','id') - cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE', (sequence_id, True)) - res = cr.dictfetchone() - if res: - cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True)) - if res['number_next']: - return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) - else: - return self._process(res['prefix']) + self._process(res['suffix']) - finally: - cr.commit() + assert test in ('code','id') + cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE NOWAIT', (sequence_id, True)) + res = cr.dictfetchone() + if res: + cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True)) + if res['number_next']: + return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) + else: + return self._process(res['prefix']) + self._process(res['suffix']) return False def get(self, cr, uid, code): From 91a88a3f8dbd4d2438ddd273d3683fe7a16d03ba Mon Sep 17 00:00:00 2001 From: "HDA (OpenERP)" Date: Tue, 6 Apr 2010 15:56:21 +0530 Subject: [PATCH 50/99] Improved docstring of orm bzr revid: hda@tinyerp.com-20100406102621-0n65uvesduhpv987 --- bin/osv/orm.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 589681ebfaf..29eab99a7d1 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -2493,12 +2493,12 @@ class orm(orm_template): def default_get(self, cr, uid, fields_list, context=None): """ - Get default field values of the given field list of the model + To Get default field values of given fields list of the model :param cr: database cursor :param uid: current user id :param fields_list: a list of fields to get the default value - :type fields_list: a list (example ['field_name_1', ...]) + :type fields_list: a list (example ['field1','field2',]) :param context: context arguments, like lang, time zone :return: a dictionary of the default values for fields (set on the object class, by the user preferences, or via the context) @@ -2872,9 +2872,9 @@ class orm(orm_template): def check_access_rule(self, cr, uid, ids, operation, context=None): """Verifies that the operation given by ``operation`` is allowed for the user according to ir.rules. - @param ``operation``: one of ``'read'``, ``'write'``, ``'unlink'`` - @raise ``except_orm``: if current ir.rules do not permit this operation. - @return: ``None`` if the operation is allowed + :param operation: one of ``'read'``, ``'write'``, ``'unlink'`` + :raise except_orm: * if current ir.rules do not permit this operation. + :return: None if the operation is allowed """ where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context) if where_clause: @@ -2955,9 +2955,9 @@ class orm(orm_template): :param cr: database cursor :param user: current user id :type user: integer (example 1) - :param ids: id or a list of ids - :param vals: a dictionary of field values to update - :type vals: a dictionary (example {'field_name':'value', ...}) + :param ids: id or list of ids + :param vals: dictionary of field values to update + :type vals: dictionary (example {'field_name':'value', ...}) :param context(optional, highly recommended): context arguments, like lang, time zone :return: True :raise AccessError: * if user has no write rights on the requested object @@ -3165,10 +3165,10 @@ class orm(orm_template): :param cr: database cursor :param user: current user id :type user: integer (example 1) - :param vals: a dictionary for new record {'field_name':field_value, ...} - :type vals: a dictionary (example {'field_name':field_value, ...}) + :param vals: dictionary for new record {'field_name':field_value, ...} + :type vals: dictionary (example {'field_name':field_value, ...}) :param context(optional, highly recommended): context arguements, like lang, time zone - :type context: a dictionary (example {'lang':'en_us', ...}) + :type context: dictionary (example {'lang':'en_us', ...}) :return: id of new record created :raise AccessError: * if user has no create rights on the requested object * if user tries to bypass access rules for create on the requested object @@ -3532,10 +3532,10 @@ class orm(orm_template): :param cr: database cursor :param user: current user id - :param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...] - :param offset: optional number of records to skip + :param args: list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...] + :param offset: optional number from search starts :param limit: optional max number of records to return - :param order: optional columns to sort by (default: self._order ) + :param order: optional columns to sort by (default: self._order=id ) :param context(optional, highly recommended): context arguments, like lang, time zone :param count: if True, returns only the number of records matching the criteria, not their ids :return: id or a list of ids of records matching the criteria From 8326caf5de58ff62c0fd18239e0a7ec7ccceb9b3 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Tue, 6 Apr 2010 16:39:53 +0530 Subject: [PATCH 51/99] [IMP] service: pass db parameter in checkRequest for http bzr revid: hmo@tinyerp.com-20100406110953-5x0h9b3jlytnkty6 --- bin/service/http_server.py | 14 +++++++------- bin/service/websrv_lib.py | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bin/service/http_server.py b/bin/service/http_server.py index fe3d98a0004..47ae6c35ae8 100644 --- a/bin/service/http_server.py +++ b/bin/service/http_server.py @@ -251,12 +251,11 @@ class OerpAuthProxy(AuthProxy): self.auth_tries = 0 self.last_auth = None - def checkRequest(self,handler,path = '/'): - if self.auth_creds: - return True + def checkRequest(self,handler,path, db=False): auth_str = handler.headers.get('Authorization',False) try: - db = handler.get_db_from_path(path) + if not db: + db = handler.get_db_from_path(path) print "Got db:",db except: if path.startswith('/'): @@ -267,8 +266,9 @@ class OerpAuthProxy(AuthProxy): else: #FIXME! self.provider.log("Wrong path: %s, failing auth" %path) - raise AuthRejectedExc("Authorization failed. Wrong sub-path.") - + raise AuthRejectedExc("Authorization failed. Wrong sub-path.") + if self.auth_creds.get(db): + return True if auth_str and auth_str.startswith('Basic '): auth_str=auth_str[len('Basic '):] (user,passwd) = base64.decodestring(auth_str).split(':') @@ -276,7 +276,7 @@ class OerpAuthProxy(AuthProxy): acd = self.provider.authenticate(db,user,passwd,handler.client_address) if acd != False: self.auth_creds[db] = acd - self.last_auth=db + self.last_auth = db return True if self.auth_tries > 5: self.provider.log("Failing authorization after 5 requests w/o password") diff --git a/bin/service/websrv_lib.py b/bin/service/websrv_lib.py index 354311eaa64..464c41ca356 100644 --- a/bin/service/websrv_lib.py +++ b/bin/service/websrv_lib.py @@ -234,6 +234,9 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler): if fore.close_connection: # print "Closing connection because of handler" self.close_connection = fore.close_connection + if hasattr(fore, '_flush'): + fore._flush() + def parse_rawline(self): """Parse a request (internal). From 927227cef9f80780a265e9407d46487012892a86 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Tue, 6 Apr 2010 18:00:28 +0530 Subject: [PATCH 52/99] [IMP]:improved orm's public methods docstrings bzr revid: rvo@tinyerp.co.in-20100406123028-1mroxby4v4ijbxtr --- bin/osv/orm.py | 97 +++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 29eab99a7d1..59f64d704d6 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -139,7 +139,7 @@ class browse_record(object): def __init__(self, cr, uid, id, table, cache, context=None, list_class = None, fields_process={}): ''' table : the object (inherited from orm) - context : a dictionary with an optional context + context : dictionary with an optional context ''' if not context: context = {} @@ -506,9 +506,9 @@ class orm_template(object): :param cr: database cursor :param user: current user id - :param select: id or a list of ids + :param select: id or list of ids :param context: context arguments, like lang, time zone - :rtype: object or a list of objects requested + :rtype: object or list of objects requested """ if not context: @@ -631,10 +631,10 @@ class orm_template(object): :param cr: database cursor :param uid: current user id - :param ids: a list of ids - :param fields_to_export: a list of fields + :param ids: list of ids + :param fields_to_export: list of fields :param context: context arguments, like lang, time zone, may contain import_comp(default: False) to make exported data compatible with import_data() - :rtype: a dictionary with a *datas* matrix + :rtype: dictionary with a *datas* matrix This method is used when exporting data via client menu @@ -673,15 +673,15 @@ class orm_template(object): :param cr: database cursor :param uid: current user id - :param ids: a list of ids - :param fields: a list of fields + :param ids: list of ids + :param fields: list of fields :param data: data to import :param mode: 'init' or 'update' for record creation :param current_module: module name :param noupdate: flag for record creation :param context: context arguments, like lang, time zone, :param filename: optional file to store partial import state for recovery - :rtype: a tuple + :rtype: tuple This method is used when importing data via client menu @@ -1362,9 +1362,9 @@ class orm_template(object): :param view_id: id of the view or None :param view_type: type of the view to return if view_id is None ('form', tree', ...) :param context: context arguments, like lang, time zone - :param toolbar: true to include concextual actions + :param toolbar: true to include contextual actions :param submenu: example (portal_project module) - :return: a dictionary describing the composition of the requested view (including inherited views and extensions) + :return: dictionary describing the composition of the requested view (including inherited views and extensions) :raise AttributeError: * if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace' * if some tag other than 'position' is found in parent view @@ -1934,22 +1934,22 @@ class orm(orm_template): def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None): """ - Get the list of records in list view sorted by the given fields + Get the list of records in list view grouped by the given ``groupby`` fields :param cr: database cursor :param uid: current user id - :param domain: a list specifying search criteria [['field_name', 'operator', 'value'], ...] - :param fields: a list of fields present in the list view specified on the object - :param groupby: a list of fields on which to sort the records - :type fields_list: a list (example ['field_name_1', ...]) + :param domain: list specifying search criteria [['field_name', 'operator', 'value'], ...] + :param fields: list of fields present in the list view specified on the object + :param groupby: list of fields on which to groupby the records + :type fields_list: list (example ['field_name_1', ...]) :param offset: optional number of records to skip :param limit: optional max number of records to return :param context: context arguments, like lang, time zone - :return: a list of dictionaries(one dictionary for each record) containing: + :return: list of dictionaries(one dictionary for each record) containing: - * the values of fields sorted out by the fields in groupby arguement - * __domain: a list of tuples specifying the search criteria - * __context: a dictionary with argument like 'groupby' + * the values of fields grouped by the fields in ``groupby`` argument + * __domain: list of tuples specifying the search criteria + * __context: dictionary with argument like ``groupby`` :rtype: [{'field_name_1': value, ...] :raise AccessError: * if user has no read rights on the requested object * if user tries to bypass access rules for read on the requested object @@ -2035,7 +2035,7 @@ class orm(orm_template): Adds missing table select and join clause(s) for reaching the field coming from an '_inherits' parent table. - :param tables: a list of table._table names enclosed in double quotes as returned + :param tables: list of table._table names enclosed in double quotes as returned by _where_calc() """ @@ -2497,10 +2497,10 @@ class orm(orm_template): :param cr: database cursor :param uid: current user id - :param fields_list: a list of fields to get the default value - :type fields_list: a list (example ['field1','field2',]) + :param fields_list: list of fields to get the default value + :type fields_list: list (example ['field1', 'field2',]) :param context: context arguments, like lang, time zone - :return: a dictionary of the default values for fields (set on the object class, by the user preferences, or via the context) + :return: dictionary of the default values for fields (set on the object class, by the user preferences, or via the context) """ if not context: @@ -2599,9 +2599,9 @@ class orm(orm_template): :param cr: database cursor :param user: current user id - :param fields: a list of fields + :param fields: list of fields :param context: context arguments, like lang, time zone - :return: a dictionary of field dictionaries, each one describing a field of the business object + :return: dictionary of field dictionaries, each one describing a field of the business object :raise AccessError: * if user has no create/write rights on the requested object """ @@ -2616,11 +2616,11 @@ class orm(orm_template): :param cr: database cursor :param user: current user id - :param ids: id or a list of the ids of the records to read + :param ids: id or list of the ids of the records to read :param fields: optional list of field names to return (default: all fields would be returned) - :type fields: a list (example ['field_name_1', ...]) + :type fields: list (example ['field_name_1', ...]) :param context(optional, highly recommended): context arguments, like lang, time zone - :return: a list of dictionaries((a dictionary per record asked)) with requested field values + :return: list of dictionaries((dictionary per record asked)) with requested field values :rtype: [{‘name_of_the_field’: value, ...}, ...] :raise AccessError: * if user has no read rights on the requested object * if user tries to bypass access rules for read on the requested object @@ -2819,8 +2819,8 @@ class orm(orm_template): :param ids: id or list of ids :param context: context arguments, like lang, time zone :param details: if True, *_uid fields are replaced with the name of the user - :return: a list of ownership dictionaries for each requested record - :rtype: a list of dictionaries with the following keys: + :return: list of ownership dictionaries for each requested record + :rtype: list of dictionaries with the following keys: * id: object id * create_uid: user who created the record @@ -2872,7 +2872,8 @@ class orm(orm_template): def check_access_rule(self, cr, uid, ids, operation, context=None): """Verifies that the operation given by ``operation`` is allowed for the user according to ir.rules. - :param operation: one of ``'read'``, ``'write'``, ``'unlink'`` + + :param operation: one of ``write``, ``unlink`` :raise except_orm: * if current ir.rules do not permit this operation. :return: None if the operation is allowed """ @@ -2894,7 +2895,7 @@ class orm(orm_template): :param cr: database cursor :param uid: current user id - :param ids: id or a list of ids + :param ids: id or list of ids :param context(optional, highly recommended): context arguments, like lang, time zone :return: True :raise AccessError: * if user has no unlink rights on the requested object @@ -2957,7 +2958,7 @@ class orm(orm_template): :type user: integer (example 1) :param ids: id or list of ids :param vals: dictionary of field values to update - :type vals: dictionary (example {'field_name':'value', ...}) + :type vals: dictionary (example {'field_name': 'value', ...}) :param context(optional, highly recommended): context arguments, like lang, time zone :return: True :raise AccessError: * if user has no write rights on the requested object @@ -3165,10 +3166,10 @@ class orm(orm_template): :param cr: database cursor :param user: current user id :type user: integer (example 1) - :param vals: dictionary for new record {'field_name':field_value, ...} - :type vals: dictionary (example {'field_name':field_value, ...}) - :param context(optional, highly recommended): context arguements, like lang, time zone - :type context: dictionary (example {'lang':'en_us', ...}) + :param vals: dictionary for new record {'field_name': field_value, ...} + :type vals: dictionary (example {'field_name': field_value, ...}) + :param context(optional, highly recommended): context arguments, like lang, time zone + :type context: dictionary (example {'lang': 'en_us', ...}) :return: id of new record created :raise AccessError: * if user has no create rights on the requested object * if user tries to bypass access rules for create on the requested object @@ -3538,8 +3539,8 @@ class orm(orm_template): :param order: optional columns to sort by (default: self._order=id ) :param context(optional, highly recommended): context arguments, like lang, time zone :param count: if True, returns only the number of records matching the criteria, not their ids - :return: id or a list of ids of records matching the criteria - :rtype: integer or a list of integers + :return: id or list of ids of records matching the criteria + :rtype: integer or list of integers :raise AccessError: * if user tries to bypass access rules for read on the requested object. Operators: @@ -3602,7 +3603,7 @@ class orm(orm_template): :param cr: database cursor :param user: current user id :type user: integer (example 1) - :param ids: a list of ids + :param ids: list of ids :param context: context arguments, like lang, time zone :return: tuples with the text representation of requested objects for to-many relationships @@ -3622,11 +3623,11 @@ class orm(orm_template): :param cr: database cursor :param user: current user id :param name: object name to search - :param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...] + :param args: list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...] :param operator: operator for search criterion :param context: context arguments, like lang, time zone :param limit: optional max number of records to return - :return: a list of object names matching the search criteria, used to provide completition for to-many relationships + :return: list of object names matching the search criteria, used to provide completion for to-many relationships This method is equivalent of search() on name + name_get() @@ -3649,9 +3650,9 @@ class orm(orm_template): :param cr: database cursor :param user: current user id :param ids: id of the record to copy - :param default: a dictionary of field values to update before saving the duplicate object + :param default: dictionary of field values to update before saving the duplicate object :param context: context arguments, like lang, time zone - :return: a dictionary containing all the field values + :return: dictionary containing all the field values """ if not context: @@ -3731,8 +3732,8 @@ class orm(orm_template): :param cr: database cursor :param uid: current user id :param id: id of the record to copy - :param default: a dictionary of field values to update before saving the duplicate object - :type default: a dictionary (example {'field_name':field_value, ...}) + :param default: dictionary of field values to update before saving the duplicate object + :type default: dictionary (example {'field_name': field_value, ...}) :param context: context arguments, like lang, time zone :return: True @@ -3756,7 +3757,7 @@ class orm(orm_template): :param cr: database cursor :param uid: current user id - :param ids: a list of ids of records + :param ids: list of ids of records :param parent: parent field name :return: True or False based on recursion detection """ From 24d873fdbecf7444b861d59e8a7a4bcea1fc0f92 Mon Sep 17 00:00:00 2001 From: Julien Thewys Date: Tue, 6 Apr 2010 14:33:25 +0200 Subject: [PATCH 53/99] [MERGE] Fixed previous merges. bzr revid: jth@openerp.com-20100406123325-219p7klnjaen82h2 --- bin/addons/base/i18n/bg.po | 16 ---------------- bin/addons/base/i18n/hr.po | 32 -------------------------------- bin/addons/base/i18n/it.po | 9 --------- bin/addons/base/i18n/ja.po | 32 -------------------------------- bin/addons/base/i18n/sv.po | 32 -------------------------------- bin/addons/base/i18n/uk.po | 32 -------------------------------- bin/addons/base/i18n/zh_CN.po | 32 -------------------------------- 7 files changed, 185 deletions(-) diff --git a/bin/addons/base/i18n/bg.po b/bin/addons/base/i18n/bg.po index 7061cf9d069..297b2d31301 100644 --- a/bin/addons/base/i18n/bg.po +++ b/bin/addons/base/i18n/bg.po @@ -7,29 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 04:19+0000\n" -======= -"PO-Revision-Date: 2010-03-31 08:05+0000\n" ->>>>>>> MERGE-SOURCE -======= "PO-Revision-Date: 2010-04-01 04:11+0000\n" ->>>>>>> MERGE-SOURCE "Last-Translator: Boris \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= -"X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE -======= "X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/hr.po b/bin/addons/base/i18n/hr.po index 1f286953955..51a7f1d772a 100644 --- a/bin/addons/base/i18n/hr.po +++ b/bin/addons/base/i18n/hr.po @@ -7,45 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 04:36+0000\n" -======= -"PO-Revision-Date: 2010-03-31 06:01+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-01 04:15+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-03 04:32+0000\n" ->>>>>>> MERGE-SOURCE -======= "PO-Revision-Date: 2010-04-05 04:33+0000\n" ->>>>>>> MERGE-SOURCE "Last-Translator: goranc \n" "Language-Team: openerp-translators\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= -"X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-04 03:52+0000\n" ->>>>>>> MERGE-SOURCE -======= "X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" "Language: hr\n" diff --git a/bin/addons/base/i18n/it.po b/bin/addons/base/i18n/it.po index 8a9a054c1dd..f98e352340b 100644 --- a/bin/addons/base/i18n/it.po +++ b/bin/addons/base/i18n/it.po @@ -7,22 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 09:53+0000\n" -"Last-Translator: Carlo Vettore \n" -======= "PO-Revision-Date: 2010-03-31 05:14+0000\n" "Last-Translator: OpenERP Administrators \n" ->>>>>>> MERGE-SOURCE "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= "X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/ja.po b/bin/addons/base/i18n/ja.po index ad273b86073..f55114223a5 100644 --- a/bin/addons/base/i18n/ja.po +++ b/bin/addons/base/i18n/ja.po @@ -8,45 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 04:24+0000\n" -======= -"PO-Revision-Date: 2010-03-31 05:47+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-01 04:13+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-03 04:34+0000\n" ->>>>>>> MERGE-SOURCE -======= "PO-Revision-Date: 2010-04-05 04:34+0000\n" ->>>>>>> MERGE-SOURCE "Last-Translator: Harry (Open ERP) \n" "Language-Team: Japanese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= -"X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-04 03:51+0000\n" ->>>>>>> MERGE-SOURCE -======= "X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/sv.po b/bin/addons/base/i18n/sv.po index 58aab09492b..04fc6b3d022 100644 --- a/bin/addons/base/i18n/sv.po +++ b/bin/addons/base/i18n/sv.po @@ -7,45 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 04:18+0000\n" -======= -"PO-Revision-Date: 2010-03-31 04:49+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-01 04:09+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-03 04:28+0000\n" ->>>>>>> MERGE-SOURCE -======= "PO-Revision-Date: 2010-04-05 04:37+0000\n" ->>>>>>> MERGE-SOURCE "Last-Translator: Anders Wallenquist \n" "Language-Team: <>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= -"X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-04 03:52+0000\n" ->>>>>>> MERGE-SOURCE -======= "X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/uk.po b/bin/addons/base/i18n/uk.po index a1612c6d22d..2557ca954df 100644 --- a/bin/addons/base/i18n/uk.po +++ b/bin/addons/base/i18n/uk.po @@ -7,45 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 04:37+0000\n" -======= -"PO-Revision-Date: 2010-03-31 05:27+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-01 04:14+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-03 04:31+0000\n" ->>>>>>> MERGE-SOURCE -======= "PO-Revision-Date: 2010-04-05 04:31+0000\n" ->>>>>>> MERGE-SOURCE "Last-Translator: Fabien (Open ERP) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= -"X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-04 03:52+0000\n" ->>>>>>> MERGE-SOURCE -======= "X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/zh_CN.po b/bin/addons/base/i18n/zh_CN.po index 9f124f629d9..3010205f573 100644 --- a/bin/addons/base/i18n/zh_CN.po +++ b/bin/addons/base/i18n/zh_CN.po @@ -7,45 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"PO-Revision-Date: 2010-03-30 04:19+0000\n" -======= -"PO-Revision-Date: 2010-03-31 05:03+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-01 04:12+0000\n" ->>>>>>> MERGE-SOURCE -======= -"PO-Revision-Date: 2010-04-03 04:30+0000\n" ->>>>>>> MERGE-SOURCE -======= "PO-Revision-Date: 2010-04-05 04:30+0000\n" ->>>>>>> MERGE-SOURCE "Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -<<<<<<< TREE -"X-Launchpad-Export-Date: 2010-03-31 03:54+0000\n" -======= -"X-Launchpad-Export-Date: 2010-04-01 03:40+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-02 03:41+0000\n" ->>>>>>> MERGE-SOURCE -======= -"X-Launchpad-Export-Date: 2010-04-04 03:52+0000\n" ->>>>>>> MERGE-SOURCE -======= "X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n" ->>>>>>> MERGE-SOURCE "X-Generator: Launchpad (build Unknown)\n" #. module: base From 4fcdc4055eff4420943e91476c0874f7381ff0c4 Mon Sep 17 00:00:00 2001 From: "qdp-launchpad@tinyerp.com" <> Date: Tue, 6 Apr 2010 17:20:04 +0200 Subject: [PATCH 54/99] [FIX] l10n_be: periodical vat declaration, compliancy with version 5.1.0 of Intervat bzr revid: qdp-launchpad@tinyerp.com-20100406152004-pad9ocx398bpiowa --- addons/l10n_be/company.py | 8 +++-- .../wizard/l10n_be_account_vat_declaration.py | 30 +++++++++---------- .../l10n_be_account_vat_declaration_view.xml | 5 +++- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/addons/l10n_be/company.py b/addons/l10n_be/company.py index 4395f13380e..1a29940ada6 100644 --- a/addons/l10n_be/company.py +++ b/addons/l10n_be/company.py @@ -25,7 +25,7 @@ class res_company(osv.osv): _description = 'res.company' def _get_default_ad(self, addresses): - city = post_code = address = "" + city = post_code = address = country_code = "" for ads in addresses: if ads.type == 'default': city = ads.city or "" @@ -34,7 +34,9 @@ class res_company(osv.osv): address = ads.street or "" if ads.street2: address += " " + ads.street2 - return city, post_code, address + if ads.country_id: + country_code = ads.country_id and ads.country_id.code or "" + return city, post_code, address, country_code res_company() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py index 9dd5ea5b269..b1674293291 100644 --- a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py +++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py @@ -32,6 +32,9 @@ class l10n_be_vat_declaration(osv.osv_memory): 'period_id': fields.many2one('account.period','Period', required=True), 'msg': fields.text('File created', size=64, readonly=True), 'file_save': fields.binary('Save File'), + 'ask_resitution': {'type': 'boolean', 'string': 'Ask Restitution',}, + 'ask_payment': {'type': 'boolean', 'string': 'Ask Payment',}, + 'client_nihil': {'type': 'boolean', 'string': 'Last Declaration of Entreprise', 'help': 'Thick this case only if it concerns only the last statement on the civil or cessation of activity'}, } _defaults = { @@ -46,7 +49,7 @@ class l10n_be_vat_declaration(osv.osv_memory): obj_comp = self.pool.get('res.company') obj_data = self.pool.get('ir.model.data') - list_of_tags=['00','01','02','03','45','46','47','48','49','54','55','56','57','59','61','62','63','64','71','81','82','83','84','85','86','87','91'] + list_of_tags=['00','01','02','03','44','45','46','47','48','49','54','55','56','57','59','61','62','63','64','71','81','82','83','84','85','86','87','88','91'] obj_company = obj_user.browse(cr, uid, uid, context=context).company_id user_cmpny = obj_company.name vat_no=obj_company.partner_id.vat @@ -59,26 +62,19 @@ class l10n_be_vat_declaration(osv.osv_memory): ctx['period_id'] = data['period_id'] #added context here tax_info = obj_tax_code.read(cr, uid, tax_code_ids, ['code','sum_period'], context=ctx) - address = post_code = city = '' - if not obj_company.partner_id.address: - address = post_code = city = '' - - city, post_code, address = obj_comp._get_default_ad(obj_company.partner_id.address) + address = post_code = city = country_code = '' + city, post_code, address, country_code = obj_comp._get_default_ad(obj_company.partner_id.address) year_id = obj_fyear.find(cr, uid) account_period = obj_acc_period.browse(cr, uid, data['period_id'], context=context) - current_year = account_period.fiscalyear_id.name period_code = account_period.code - send_ref = user_cmpny - if period_code: - send_ref = send_ref + period_code - + send_ref = str(obj_company.partner_id.id) + str(account_period.date_start[5:7]) + str(account_period.date_stop[:4]) data_of_file='\n' data_of_file +='\n\t\n\t\t'+str(vat_no)+'\n\t\t'+str(obj_company.name)+'\n\t\t
'+address+'
' - data_of_file +='\n\t\t'+post_code+'\n\t\t'+city+'\n\t\t'+send_ref+'\n\t
' - data_of_file +='\n\t\n\t\t1\n\t\t'+str(vat_no)+'\n\t\t\n\t\t\t' - + data_of_file +='\n\t\t'+post_code+'\n\t\t'+city+'\n\t\t'+country_code+'\n\t\t'+send_ref+'\n\t' + data_of_file +='\n\t\n\t\t1\n\t\t'+str(vat_no[2:])+'\n\t\t\n\t\t\t' + starting_month = account_period.date_start[5:7] ending_month = account_period.date_stop[5:7] if starting_month != ending_month: @@ -89,14 +85,16 @@ class l10n_be_vat_declaration(osv.osv_memory): else: data_of_file += '' + starting_month + '\n\t\t\t' data_of_file += '' + str(account_period.date_stop[:4]) + '\n\t\t\n\t\t' + data_of_file += '\n\t\t'+ (data['form']['client_nihil'] and 'YES' or 'NO') +'' data_of_file +='\n\t\t\n\t\t\t' for item in tax_info: if item['code']: + if item['code'] == '71-72': item['code'] = '71' if item['code'] in list_of_tags: - data_of_file +='\n\t\t\t\t' + str(int(item['sum_period']*100)) + '' + data_of_file +='\n\t\t\t\t' + str(abs(int(item['sum_period']*100))) + '' data_of_file +='\n\t\t\t\n\t\t\n\t\n
' data['file_save'] = base64.encodestring(data_of_file) @@ -105,4 +103,4 @@ class l10n_be_vat_declaration(osv.osv_memory): l10n_be_vat_declaration() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration_view.xml b/addons/l10n_be/wizard/l10n_be_account_vat_declaration_view.xml index 56f8b6477f9..3f33448f8d3 100644 --- a/addons/l10n_be/wizard/l10n_be_account_vat_declaration_view.xml +++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration_view.xml @@ -15,6 +15,9 @@
+ + +