From 6acc5a157dc287adde7d31978e2ecc163a3ef9ef Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Tue, 15 Nov 2011 17:38:43 +0100 Subject: [PATCH 01/59] [IMP] Moved the tests directory inside the openerp module, added --run-tests command-line argument. bzr revid: vmt@openerp.com-20111115163843-djq6hybp24lk9f5e --- openerp-server | 14 +++++++++++ openerp/addons/base/test/test_ir_cron.py | 12 +++++----- openerp/tests/__init__.py | 17 +++++++++++++ .../tests}/addons/test_exceptions/__init__.py | 0 .../addons/test_exceptions/__openerp__.py | 0 .../tests}/addons/test_exceptions/models.py | 0 .../tests}/addons/test_exceptions/view.xml | 0 {tests => openerp/tests}/common.py | 24 ++++++++++--------- {tests => openerp/tests}/test_ir_sequence.py | 4 +++- {tests => openerp/tests}/test_orm.py | 8 +++---- {tests => openerp/tests}/test_xmlrpc.py | 5 +++- openerp/tools/config.py | 5 ++++ tests/__init__.py | 2 -- 13 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 openerp/tests/__init__.py rename {tests => openerp/tests}/addons/test_exceptions/__init__.py (100%) rename {tests => openerp/tests}/addons/test_exceptions/__openerp__.py (100%) rename {tests => openerp/tests}/addons/test_exceptions/models.py (100%) rename {tests => openerp/tests}/addons/test_exceptions/view.xml (100%) rename {tests => openerp/tests}/common.py (84%) rename {tests => openerp/tests}/test_ir_sequence.py (99%) rename {tests => openerp/tests}/test_orm.py (97%) rename {tests => openerp/tests}/test_xmlrpc.py (96%) delete mode 100644 tests/__init__.py diff --git a/openerp-server b/openerp-server index 71a52d5f06b..c108d9f9372 100755 --- a/openerp-server +++ b/openerp-server @@ -221,6 +221,20 @@ if __name__ == "__main__": setup_signal_handlers() + if config["run_tests_no_db"]: + import unittest2 + import openerp.tests + # This test suite creates a database. + unittest2.TextTestRunner(verbosity=2).run(openerp.tests.make_suite_no_db()) + sys.exit(0) + + if config["run_tests"]: + import unittest2 + import openerp.tests + # This test suite assumes a database. + unittest2.TextTestRunner(verbosity=2).run(openerp.tests.make_suite()) + sys.exit(0) + if config["test_file"]: run_test_file(config['db_name'], config['test_file']) sys.exit(0) diff --git a/openerp/addons/base/test/test_ir_cron.py b/openerp/addons/base/test/test_ir_cron.py index 6ef79ac3a78..2154c014f8c 100644 --- a/openerp/addons/base/test/test_ir_cron.py +++ b/openerp/addons/base/test/test_ir_cron.py @@ -40,7 +40,7 @@ JOB = { 'model': u'ir.cron' } -class test_ir_cron(openerp.osv.osv.osv): +class x_test_ir_cron(openerp.osv.osv.osv): """ Add a few handy methods to test cron jobs scheduling. """ _inherit = "ir.cron" @@ -57,7 +57,7 @@ class test_ir_cron(openerp.osv.osv.osv): time.sleep(80) print ">>> out _80_seconds" - def test_0(self, cr, uid): + def x_test_0(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S') @@ -66,17 +66,17 @@ class test_ir_cron(openerp.osv.osv.osv): self.create(cr, uid, dict(JOB, name='test_0 _20_seconds B', function='_20_seconds', nextcall=t2)) self.create(cr, uid, dict(JOB, name='test_0 _20_seconds C', function='_20_seconds', nextcall=t3)) - def test_1(self, cr, uid): + def x_test_1(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') self.create(cr, uid, dict(JOB, name='test_1 _20_seconds * 3', function='_20_seconds', nextcall=t1, numbercall=3)) - def test_2(self, cr, uid): + def x_test_2(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') self.create(cr, uid, dict(JOB, name='test_2 _80_seconds * 2', function='_80_seconds', nextcall=t1, numbercall=2)) - def test_3(self, cr, uid): + def x_test_3(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S') @@ -86,7 +86,7 @@ class test_ir_cron(openerp.osv.osv.osv): self.create(cr, uid, dict(JOB, name='test_3 _20_seconds C', function='_20_seconds', nextcall=t3)) # This test assumes 4 cron threads. - def test_00(self, cr, uid): + def x_test_00(self, cr, uid): self.test_00_set = set() now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py new file mode 100644 index 00000000000..306edcd68ce --- /dev/null +++ b/openerp/tests/__init__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +import unittest2 + +import test_orm +import test_ir_sequence +import test_xmlrpc + +def make_suite(): + suite = unittest2.TestSuite() + suite.addTests(unittest2.TestLoader().loadTestsFromModule(test_ir_sequence)) + suite.addTests(unittest2.TestLoader().loadTestsFromModule(test_orm)) + return suite + +def make_suite_no_db(): + suite = unittest2.TestSuite() + suite.addTests(unittest2.TestLoader().loadTestsFromModule(test_xmlrpc)) + return suite diff --git a/tests/addons/test_exceptions/__init__.py b/openerp/tests/addons/test_exceptions/__init__.py similarity index 100% rename from tests/addons/test_exceptions/__init__.py rename to openerp/tests/addons/test_exceptions/__init__.py diff --git a/tests/addons/test_exceptions/__openerp__.py b/openerp/tests/addons/test_exceptions/__openerp__.py similarity index 100% rename from tests/addons/test_exceptions/__openerp__.py rename to openerp/tests/addons/test_exceptions/__openerp__.py diff --git a/tests/addons/test_exceptions/models.py b/openerp/tests/addons/test_exceptions/models.py similarity index 100% rename from tests/addons/test_exceptions/models.py rename to openerp/tests/addons/test_exceptions/models.py diff --git a/tests/addons/test_exceptions/view.xml b/openerp/tests/addons/test_exceptions/view.xml similarity index 100% rename from tests/addons/test_exceptions/view.xml rename to openerp/tests/addons/test_exceptions/view.xml diff --git a/tests/common.py b/openerp/tests/common.py similarity index 84% rename from tests/common.py rename to openerp/tests/common.py index 116898dc7ef..618eafb21f7 100644 --- a/tests/common.py +++ b/openerp/tests/common.py @@ -6,9 +6,10 @@ import xmlrpclib import openerp -ADDONS_PATH = os.environ['OPENERP_ADDONS_PATH'] -PORT = int(os.environ['OPENERP_PORT']) -DB = os.environ['OPENERP_DATABASE'] +# The openerp library is supposed already configured. +ADDONS_PATH = openerp.tools.config['addons_path'] +PORT = openerp.tools.config['xmlrpc_port'] +DB = openerp.tools.config['db_name'] HOST = '127.0.0.1' @@ -25,15 +26,19 @@ db_proxy_61 = None model_proxy_61 = None model_uri_61 = None -def setUpModule(): +def start_openerp(): """ - Start the OpenERP server similary to the openerp-server script and - setup some xmlrpclib proxies. + Start the OpenERP server similary to the openerp-server script. """ - openerp.tools.config['addons_path'] = ADDONS_PATH - openerp.tools.config['xmlrpc_port'] = PORT openerp.service.start_services() + # Ugly way to ensure the server is listening. + time.sleep(2) + +def create_xmlrpc_proxies(): + """ + setup some xmlrpclib proxies. + """ global common_proxy_60 global db_proxy_60 global object_proxy_60 @@ -55,9 +60,6 @@ def setUpModule(): db_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'db') model_proxy_61 = xmlrpclib.ServerProxy(model_uri_61 + 'model/' + DB) - # Ugly way to ensure the server is listening. - time.sleep(2) - def tearDownModule(): """ Shutdown the OpenERP server similarly to a single ctrl-c. """ openerp.service.stop_services() diff --git a/tests/test_ir_sequence.py b/openerp/tests/test_ir_sequence.py similarity index 99% rename from tests/test_ir_sequence.py rename to openerp/tests/test_ir_sequence.py index 8a4731d524f..3bf84cf3404 100644 --- a/tests/test_ir_sequence.py +++ b/openerp/tests/test_ir_sequence.py @@ -19,7 +19,9 @@ import common DB = common.DB ADMIN_USER_ID = common.ADMIN_USER_ID -setUpModule = common.setUpModule +def setUpModule(): + common.create_xmlrpc_proxies() + tearDownModule = common.tearDownModule def registry(model): diff --git a/tests/test_orm.py b/openerp/tests/test_orm.py similarity index 97% rename from tests/test_orm.py rename to openerp/tests/test_orm.py index 61b574df5eb..79ac421b7ac 100644 --- a/tests/test_orm.py +++ b/openerp/tests/test_orm.py @@ -1,9 +1,10 @@ import os import unittest2 + import openerp UID = 1 -DB = os.environ['OPENERP_DATABASE'] +DB = openerp.tools.config['db_name'] CREATE = lambda values: (0, False, values) UPDATE = lambda id, values: (1, id, values) @@ -13,14 +14,13 @@ LINK_TO = lambda id: (4, id, False) DELETE_ALL = lambda: (5, False, False) REPLACE_WITH = lambda ids: (6, False, ids) -def setUpModule(): - openerp.tools.config['addons_path'] = os.environ['OPENERP_ADDONS_PATH'] - class TestO2MSerialization(unittest2.TestCase): + def setUp(self): self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor() self.partner = openerp.modules.registry.RegistryManager.get(DB)['res.partner'] self.address = openerp.modules.registry.RegistryManager.get(DB)['res.partner.address'] + def tearDown(self): self.cr.rollback() self.cr.close() diff --git a/tests/test_xmlrpc.py b/openerp/tests/test_xmlrpc.py similarity index 96% rename from tests/test_xmlrpc.py rename to openerp/tests/test_xmlrpc.py index 4ad530cbeda..a08987cdd85 100644 --- a/tests/test_xmlrpc.py +++ b/openerp/tests/test_xmlrpc.py @@ -19,7 +19,10 @@ ADMIN_USER = common.ADMIN_USER ADMIN_USER_ID = common.ADMIN_USER_ID ADMIN_PASSWORD = common.ADMIN_PASSWORD -setUpModule = common.setUpModule +def setUpModule(): + common.start_openerp() + common.create_xmlrpc_proxies() + tearDownModule = common.tearDownModule class test_xmlrpc(unittest2.TestCase): diff --git a/openerp/tools/config.py b/openerp/tools/config.py index f1c3931e81f..2b27844ceed 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -266,6 +266,10 @@ class configmanager(object): type="int") group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true", help="Use the unaccent function provided by the database when available.") + group.add_option("--run-tests", dest="run_tests", my_default=False, action="store_true", + help="Run a test suite.") + group.add_option("--run-tests-no-db", dest="run_tests_no_db", my_default=False, action="store_true", + help="Run a test suite (which does'nt assume an existing database).") parser.add_option_group(group) @@ -370,6 +374,7 @@ class configmanager(object): 'list_db', 'xmlrpcs', 'test_file', 'test_disable', 'test_commit', 'test_report_directory', 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent', + 'run_tests', 'run_tests_no_db', ] for arg in keys: diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 396284efaf7..00000000000 --- a/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -import test_xmlrpc From 9e225c7c1e599624d419cd87f1705ad9a8dcb162 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Wed, 16 Nov 2011 13:11:42 +0100 Subject: [PATCH 02/59] [IMP] check the -d option is also provided when using --run-tests. bzr revid: vmt@openerp.com-20111116121142-bxahffcr7diseoiy --- openerp/tools/config.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 2b27844ceed..7d496d524b1 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -267,9 +267,9 @@ class configmanager(object): group.add_option("--unaccent", dest="unaccent", my_default=False, action="store_true", help="Use the unaccent function provided by the database when available.") group.add_option("--run-tests", dest="run_tests", my_default=False, action="store_true", - help="Run a test suite.") + help="Run a test suite. This creates a database (possibly created with --run-tests-no-db).") group.add_option("--run-tests-no-db", dest="run_tests_no_db", my_default=False, action="store_true", - help="Run a test suite (which does'nt assume an existing database).") + help="Run a test suite (which doesn't assume an existing database).") parser.add_option_group(group) @@ -327,6 +327,10 @@ class configmanager(object): "The config file '%s' selected with -c/--config doesn't exist, "\ "use -s/--save if you want to generate it"%(opt.config)) + die((opt.run_tests or opt.run_tests_no_db) and not opt.db_name, + "The --run-tests and --run-tests-no-db need a database name "\ + "(using the --database or -d options).") + # place/search the config file on Win32 near the server installation # (../etc from the server) # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write, From 330316672ff5490602a1eb4e7743514355c306e4 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 17 Nov 2011 11:06:08 +0100 Subject: [PATCH 03/59] [IMP] Clearer use of update_module arg. - update_module was defined as config[init] or config[update] - this means it is a mutable dictionary - the code testing update_module is actually mutating config[init] and config[update]... - now update_module is really a True or False value. bzr revid: vmt@openerp.com-20111117100608-0n8o99slgk42kiiv --- openerp-server | 6 ++++-- openerp/modules/loading.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/openerp-server b/openerp-server index c108d9f9372..e47a8ea29cf 100755 --- a/openerp-server +++ b/openerp-server @@ -89,7 +89,8 @@ def setup_pid_file(): def preload_registry(dbname): """ Preload a registry, and start the cron.""" try: - db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False) + update_module = True if config['init'] or config['update'] else False + db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=update_module, pooljobs=False) # jobs will start to be processed later, when openerp.cron.start_master_thread() is called by openerp.service.start_services() registry.schedule_cron_jobs() @@ -99,7 +100,8 @@ def preload_registry(dbname): def run_test_file(dbname, test_file): """ Preload a registry, possibly run a test file, and start the cron.""" try: - db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False) + update_module = True if config['init'] or config['update'] else False + db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=update_module, pooljobs=False) cr = db.cursor() logger = logging.getLogger('server') logger.info('loading test file %s', test_file) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index 00948bc690e..6a2e653fd57 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -276,6 +276,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False): tools.config['update']['all'] = 1 if not tools.config['without_demo']: tools.config["demo"]['all'] = 1 + update_module = True # This is a brand new pool, just created in pooler.get_db_and_pool() pool = pooler.get_pool(cr.dbname) @@ -293,7 +294,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False): # processed_modules: for cleanup step after install # loaded_modules: to avoid double loading - loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report) + loaded_modules, processed_modules = load_module_graph(cr, graph, status, report=report) if tools.config['load_language']: for lang in tools.config['load_language'].split(','): From 99f99b6d8317ddeb53339b24934d722c97d1dc61 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 17 Nov 2011 15:46:28 +0100 Subject: [PATCH 04/59] [REF] openerp.modules.loading: preparing to support the -i=all option: getting almost rid of the mess with tools.config[init|update]. bzr revid: vmt@openerp.com-20111117144628-pjpac87b5whg5cl0 --- openerp/modules/graph.py | 10 ------- openerp/modules/loading.py | 53 ++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/openerp/modules/graph.py b/openerp/modules/graph.py index 7b302e99f4c..62b706ad846 100644 --- a/openerp/modules/graph.py +++ b/openerp/modules/graph.py @@ -122,9 +122,6 @@ class Graph(dict): current.remove(package) node = self.add_node(package, info) node.data = info - for kind in ('init', 'demo', 'update'): - if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force: - setattr(node, kind, True) else: later.add(package) packages.append((package, info)) @@ -186,18 +183,11 @@ class Node(Singleton): node.depth = self.depth + 1 if node not in self.children: self.children.append(node) - for attr in ('init', 'update', 'demo'): - if hasattr(self, attr): - setattr(node, attr, True) self.children.sort(lambda x, y: cmp(x.name, y.name)) return node def __setattr__(self, name, value): super(Singleton, self).__setattr__(name, value) - if name in ('init', 'update', 'demo'): - tools.config[name][self.name] = 1 - for child in self.children: - setattr(child, name, value) if name == 'depth': for child in self.children: setattr(child, name, value + 1) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index 6a2e653fd57..551947cf4a4 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -164,7 +164,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= register_module_classes(package.name) models = pool.load(cr, package) loaded_modules.append(package.name) - if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): + if package.state in ('to install', 'to upgrade'): init_module_models(cr, package.name, models) status['progress'] = float(index) / len(graph) @@ -178,18 +178,19 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= idref = {} - mode = 'update' - if hasattr(package, 'init') or package.state == 'to install': + if package.state == 'to install': mode = 'init' + else: + mode = 'update' - if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): + if package.state in ('to install', 'to upgrade'): if package.state=='to upgrade': # upgrading the module information modobj.write(cr, 1, [module_id], modobj.get_values_from_terp(package.data)) load_init_xml(module_name, idref, mode) load_update_xml(module_name, idref, mode) load_data(module_name, idref, mode) - if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'): + if package.dbdemo and package.state != 'installed': status['progress'] = (index + 0.75) / len(graph) load_demo_xml(module_name, idref, mode) load_demo(module_name, idref, mode) @@ -212,9 +213,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= modobj.update_translations(cr, 1, [module_id], None) package.state = 'installed' - for kind in ('init', 'demo', 'update'): - if hasattr(package, kind): - delattr(package, kind) cr.commit() @@ -272,10 +270,6 @@ def load_modules(db, force_demo=False, status=None, update_module=False): if not openerp.modules.db.is_initialized(cr): logger.notifyChannel("init", netsvc.LOG_INFO, "init db") openerp.modules.db.initialize(cr) - tools.config["init"]["all"] = 1 - tools.config['update']['all'] = 1 - if not tools.config['without_demo']: - tools.config["demo"]['all'] = 1 update_module = True # This is a brand new pool, just created in pooler.get_db_and_pool() @@ -294,6 +288,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False): # processed_modules: for cleanup step after install # loaded_modules: to avoid double loading + # After load_module_graph(), 'base' has been installed or updated and its state is 'installed'. loaded_modules, processed_modules = load_module_graph(cr, graph, status, report=report) if tools.config['load_language']: @@ -301,28 +296,33 @@ def load_modules(db, force_demo=False, status=None, update_module=False): tools.load_language(cr, lang) # STEP 2: Mark other modules to be loaded/updated + # This is a one-shot use of tools.config[init|update] from the command line + # arguments. It is directly cleared to not interfer with later create/update + # issued via RPC. if update_module: modobj = pool.get('ir.module.module') - if ('base' in tools.config['init']) or ('base' in tools.config['update']): + if ('base' in tools.config['init']) or ('base' in tools.config['update']) \ + or ('all' in tools.config['init']) or ('all' in tools.config['update']): logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list') modobj.update_list(cr, 1) + if 'all' in tools.config['init']: + pass + _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) - mods = [k for k in tools.config['init'] if tools.config['init'][k]] - if mods: - ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) - if ids: - modobj.button_install(cr, 1, ids) + mods = [k for k in tools.config['init'] if tools.config['init'][k] and k not in ('base', 'all')] + ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)]) + if ids: + modobj.button_install(cr, 1, ids) # goes from 'uninstalled' to 'to install' - mods = [k for k in tools.config['update'] if tools.config['update'][k]] - if mods: - ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) - if ids: - modobj.button_upgrade(cr, 1, ids) - - cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base')) + mods = [k for k in tools.config['update'] if tools.config['update'][k] and k not in ('base', 'all')] + ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)]) + if ids: + modobj.button_upgrade(cr, 1, ids) # goes from 'installed' to 'to upgrade' + for kind in ('init', 'demo', 'update'): + tools.config[kind] = {} # STEP 3: Load marked modules (skipping base which was done in STEP 1) # IMPORTANT: this is done in two parts, first loading all installed or @@ -372,9 +372,6 @@ def load_modules(db, force_demo=False, status=None, update_module=False): if report.get_report(): logger.notifyChannel('init', netsvc.LOG_INFO, report) - for kind in ('init', 'demo', 'update'): - tools.config[kind] = {} - cr.commit() if update_module: # Remove records referenced from ir_model_data for modules to be From b6b25ea70639bfe2eb5c1249e7b1d40216ab545b Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 17 Nov 2011 16:07:20 +0100 Subject: [PATCH 05/59] [IMP] openerp.modules.loading: -i all seems to work as documented in --help. bzr revid: vmt@openerp.com-20111117150720-uaer043c2k55937n --- openerp/modules/loading.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index 551947cf4a4..d0b18632e31 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -307,7 +307,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False): modobj.update_list(cr, 1) if 'all' in tools.config['init']: - pass + ids = modobj.search(cr, 1, []) + tools.config['init'] = dict.fromkeys([m['name'] for m in modobj.read(cr, 1, ids, ['name'])], 1) _check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys())) From e9c405c244249e536f0a97c5b6b9d9b4d7215e06 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 17 Nov 2011 17:28:24 +0100 Subject: [PATCH 06/59] [FIX] openerp.modules.loading: previous change disabled demo data altogether, this should be now fixed by making sure that --withou-demo flag (or its absence) is taken care of, and setting the base module demo field. Normally the demo state of a module should have a ripple effect on all modules depending on it. This might prove not enough in this case and require some more testing. bzr revid: vmt@openerp.com-20111117162824-yqswv6yk7bmiyj4s --- openerp-server | 6 ++++-- openerp/modules/graph.py | 14 +++++++++----- openerp/modules/loading.py | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/openerp-server b/openerp-server index e47a8ea29cf..c985579ef15 100755 --- a/openerp-server +++ b/openerp-server @@ -90,7 +90,8 @@ def preload_registry(dbname): """ Preload a registry, and start the cron.""" try: update_module = True if config['init'] or config['update'] else False - db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=update_module, pooljobs=False) + db, registry = openerp.pooler.get_db_and_pool( + dbname, update_module=update_module, pooljobs=False, force_demo=not config['without_demo']) # jobs will start to be processed later, when openerp.cron.start_master_thread() is called by openerp.service.start_services() registry.schedule_cron_jobs() @@ -101,7 +102,8 @@ def run_test_file(dbname, test_file): """ Preload a registry, possibly run a test file, and start the cron.""" try: update_module = True if config['init'] or config['update'] else False - db, registry = openerp.pooler.get_db_and_pool(dbname, update_module=update_module, pooljobs=False) + db, registry = openerp.pooler.get_db_and_pool( + dbname, update_module=update_module, pooljobs=False, force_demo=not config['without_demo']) cr = db.cursor() logger = logging.getLogger('server') logger.info('loading test file %s', test_file) diff --git a/openerp/modules/graph.py b/openerp/modules/graph.py index 62b706ad846..5c6e5899ce4 100644 --- a/openerp/modules/graph.py +++ b/openerp/modules/graph.py @@ -88,15 +88,19 @@ class Graph(dict): for k, v in additional_data[package.name].items(): setattr(package, k, v) - def add_module(self, cr, module, force=None): - self.add_modules(cr, [module], force) + def add_module(self, cr, module, force_demo=False): + self.add_modules(cr, [module], force_demo) - def add_modules(self, cr, module_list, force=None): - if force is None: - force = [] + def add_modules(self, cr, module_list, force_demo=False): packages = [] len_graph = len(self) for module in module_list: + if force_demo: + cr.execute(""" + UPDATE ir_module_module + SET demo='t' + WHERE name = %s""", + (module,)) # This will raise an exception if no/unreadable descriptor file. # NOTE The call to load_information_from_description_file is already # done by db.initialize, so it is possible to not do it again here. diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index d0b18632e31..066b78f5e3f 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -281,7 +281,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False): # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) graph = openerp.modules.graph.Graph() - graph.add_module(cr, 'base', force) + graph.add_module(cr, 'base', force_demo) if not graph: logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)') raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)')) From 9212a7f81a4cc1fe74e81cdd4d58523119460a03 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 1 Dec 2011 13:18:56 +0100 Subject: [PATCH 07/59] [FIX] test_ir_cron: the names of the class and methods were changed by mistake. bzr revid: vmt@openerp.com-20111201121856-knw7ewbpj083zf12 --- openerp/addons/base/test/test_ir_cron.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/test/test_ir_cron.py b/openerp/addons/base/test/test_ir_cron.py index 2154c014f8c..6ef79ac3a78 100644 --- a/openerp/addons/base/test/test_ir_cron.py +++ b/openerp/addons/base/test/test_ir_cron.py @@ -40,7 +40,7 @@ JOB = { 'model': u'ir.cron' } -class x_test_ir_cron(openerp.osv.osv.osv): +class test_ir_cron(openerp.osv.osv.osv): """ Add a few handy methods to test cron jobs scheduling. """ _inherit = "ir.cron" @@ -57,7 +57,7 @@ class x_test_ir_cron(openerp.osv.osv.osv): time.sleep(80) print ">>> out _80_seconds" - def x_test_0(self, cr, uid): + def test_0(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S') @@ -66,17 +66,17 @@ class x_test_ir_cron(openerp.osv.osv.osv): self.create(cr, uid, dict(JOB, name='test_0 _20_seconds B', function='_20_seconds', nextcall=t2)) self.create(cr, uid, dict(JOB, name='test_0 _20_seconds C', function='_20_seconds', nextcall=t3)) - def x_test_1(self, cr, uid): + def test_1(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') self.create(cr, uid, dict(JOB, name='test_1 _20_seconds * 3', function='_20_seconds', nextcall=t1, numbercall=3)) - def x_test_2(self, cr, uid): + def test_2(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') self.create(cr, uid, dict(JOB, name='test_2 _80_seconds * 2', function='_80_seconds', nextcall=t1, numbercall=2)) - def x_test_3(self, cr, uid): + def test_3(self, cr, uid): now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S') @@ -86,7 +86,7 @@ class x_test_ir_cron(openerp.osv.osv.osv): self.create(cr, uid, dict(JOB, name='test_3 _20_seconds C', function='_20_seconds', nextcall=t3)) # This test assumes 4 cron threads. - def x_test_00(self, cr, uid): + def test_00(self, cr, uid): self.test_00_set = set() now = datetime.now() t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S') From 620453c4a04822af52f25449433dcfd1a0c848e5 Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Wed, 5 Sep 2012 15:58:21 +0530 Subject: [PATCH 08/59] [FIX] Workflow function activities explode if they contain empty lines. bzr revid: tpa@tinyerp.com-20120905102821-8q6d42d5p0xudxmv --- openerp/workflow/wkf_expr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/workflow/wkf_expr.py b/openerp/workflow/wkf_expr.py index 608af34d118..bf9e99f00a4 100644 --- a/openerp/workflow/wkf_expr.py +++ b/openerp/workflow/wkf_expr.py @@ -44,7 +44,7 @@ class Env(dict): def _eval_expr(cr, ident, workitem, action): ret=False assert action, 'You used a NULL action in a workflow, use dummy node instead.' - for line in action.split('\n'): + for line in filter(lambda action: action != "", action.split('\n')): line = line.strip() uid=ident[0] model=ident[1] From 1b54fb39f23f8a6ba0d6e458723b12eb65bbaf94 Mon Sep 17 00:00:00 2001 From: "RGA(OpenERP)" <> Date: Fri, 14 Sep 2012 19:16:04 +0530 Subject: [PATCH 09/59] [FIX] Compute domain fail to eval when domain has array comparision bzr revid: rgaopenerp-20120914134604-agowp1k0lxgutk7s --- addons/web/static/src/js/view_form.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 7d328f012fe..43e5313eb7b 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1527,7 +1527,8 @@ instance.web.form.compute_domain = function(expr, fields) { switch (op.toLowerCase()) { case '=': case '==': - stack.push(field_value == val); + // ([] == []) ==> false + stack.push(_.isEqual(field_value, val)); break; case '!=': case '<>': @@ -1559,6 +1560,7 @@ instance.web.form.compute_domain = function(expr, fields) { op, JSON.stringify(expr)); } } + return _.all(stack, _.identity); }; From 790b07a83a3e88888853e7b42276b3e47a38ad4b Mon Sep 17 00:00:00 2001 From: "Turkesh Patel (Open ERP)" Date: Tue, 23 Oct 2012 14:40:01 +0530 Subject: [PATCH 10/59] [IMP] improved code bzr revid: tpa@tinyerp.com-20121023091001-a5z5utrnxv3tcbsk --- openerp/workflow/wkf_expr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/workflow/wkf_expr.py b/openerp/workflow/wkf_expr.py index bf9e99f00a4..16b186c904d 100644 --- a/openerp/workflow/wkf_expr.py +++ b/openerp/workflow/wkf_expr.py @@ -44,7 +44,7 @@ class Env(dict): def _eval_expr(cr, ident, workitem, action): ret=False assert action, 'You used a NULL action in a workflow, use dummy node instead.' - for line in filter(lambda action: action != "", action.split('\n')): + for line in filter(None, action.split('\n')): line = line.strip() uid=ident[0] model=ident[1] From 72f59ff45e937fa24895069c0b68c6ad9cfbe228 Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 25 Oct 2012 15:01:11 +0200 Subject: [PATCH 11/59] [FIX] modules: avoid to have the docutils' warnings rendered inline. The html rendering of the __openerp__.py description is done using docutils. This patch removes the warnings that docutils inserts in the resulting document and log them. Docutils still generates spurious output on stderr though. (An alternative is to use the `report_level` option but in that case it seems we can never witness the warnings being created.) bzr revid: vmt@openerp.com-20121025130111-7l157hexh6ew9w34 --- openerp/addons/base/module/module.py | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/module/module.py b/openerp/addons/base/module/module.py index 3484b40cee9..42b57b7c661 100644 --- a/openerp/addons/base/module/module.py +++ b/openerp/addons/base/module/module.py @@ -20,7 +20,10 @@ ############################################################################## import base64 +from docutils import io, nodes from docutils.core import publish_string +from docutils.transforms import Transform, writer_aux +from docutils.writers.html4css1 import Writer import imp import logging import re @@ -80,6 +83,32 @@ class module_category(osv.osv): 'visible' : 1, } +class MyFilterMessages(Transform): + """ + Custom docutils transform to remove `system message` for a document and + generate warnings. + + (The standard filter removes them based on some `report_level` passed in + the `settings_override` dictionary, but if we use it, we can't see them + and generate warnings.) + """ + + default_priority = 870 + + def apply(self): + for node in self.document.traverse(nodes.system_message): + _logger.warning("docutils' system message present: %s", str(node)) + node.parent.remove(node) + +class MyWriter(Writer): + """ + Custom docutils html4ccs1 writer that doesn't add the warnings to the + output document. + """ + + def get_transforms(self): + return [MyFilterMessages, writer_aux.Admonitions] + class module(osv.osv): _name = "ir.module.module" _rec_name = "shortdesc" @@ -100,7 +129,7 @@ class module(osv.osv): res = dict.fromkeys(ids, '') for module in self.browse(cr, uid, ids, context=context): overrides = dict(embed_stylesheet=False, doctitle_xform=False, output_encoding='unicode') - output = publish_string(source=module.description, writer_name='html', settings_overrides=overrides) + output = publish_string(source=module.description, settings_overrides=overrides, writer=MyWriter()) res[module.id] = output return res From e242773cac11363c1671c8a65de42a95d5cedc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Thu, 25 Oct 2012 15:38:51 +0200 Subject: [PATCH 12/59] [IMP] For legal reason (forbiden to modify journal entries which belongs to a closed fy or period) : Forbid to modify the code of an account if journal entries have been already posted on this account. This cannot be simply 'configurable' since it can lead to a lack of confidence in OpenERP and this is what we want to change. bzr revid: joel.grandguillaume@camptocamp.com-20121025133851-3mnvsy9ltavys94k --- addons/account/account.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index c9dd1950347..ebc5aa8af93 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -648,8 +648,19 @@ class account_account(osv.osv): raise osv.except_osv(_('Warning!'), _("You cannot change the type of account from '%s' to '%s' type as it contains journal items!") % (old_type,new_type,)) return True - def write(self, cr, uid, ids, vals, context=None): + # For legal reason (forbiden to modify journal entries which belongs to a closed fy or period), Forbid to modify + # the code of an account if journal entries have been already posted on this account. This cannot be simply + # 'configurable' since it can lead to a lack of confidence in OpenERP and this is what we want to change. + def _check_allow_code_change(self, cr, uid, ids, context=None): + line_obj = self.pool.get('account.move.line') + for account in self.browse(cr, uid, ids, context=context): + account_ids = self.search(cr, uid, [('id', 'child_of', [account.id])]) + if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): + raise osv.except_osv(_('Warning !'), _("You cannot change the code of account which contains journal items!")) + return True + + def write(self, cr, uid, ids, vals, context=None): if context is None: context = {} if not ids: @@ -669,6 +680,8 @@ class account_account(osv.osv): self._check_moves(cr, uid, ids, "write", context=context) if 'type' in vals.keys(): self._check_allow_type_change(cr, uid, ids, vals['type'], context=context) + if 'code' in vals.keys(): + self._check_allow_code_change(cr, uid, ids, context=context) return super(account_account, self).write(cr, uid, ids, vals, context=context) def unlink(self, cr, uid, ids, context=None): From aef8541bc8e422dce8d718986f00f109c30ebc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Thu, 25 Oct 2012 15:47:02 +0200 Subject: [PATCH 13/59] [IMP] Forbid to change type of account for 'consolidation' and 'view' if there is entries on it or his children. bzr revid: joel.grandguillaume@camptocamp.com-20121025134702-7px784iu3226qc0l --- addons/account/account.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index ebc5aa8af93..33150274bdf 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -633,8 +633,7 @@ class account_account(osv.osv): return True def _check_allow_type_change(self, cr, uid, ids, new_type, context=None): - group1 = ['payable', 'receivable', 'other'] - group2 = ['consolidation','view'] + group_dest = ['consolidation','view'] line_obj = self.pool.get('account.move.line') for account in self.browse(cr, uid, ids, context=context): old_type = account.type @@ -642,12 +641,12 @@ class account_account(osv.osv): if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): #Check for 'Closed' type if old_type == 'closed' and new_type !='closed': - raise osv.except_osv(_('Warning!'), _("You cannot change the type of account from 'Closed' to any other type which contains journal items!")) - #Check for change From group1 to group2 and vice versa - if (old_type in group1 and new_type in group2) or (old_type in group2 and new_type in group1): - raise osv.except_osv(_('Warning!'), _("You cannot change the type of account from '%s' to '%s' type as it contains journal items!") % (old_type,new_type,)) - return True + raise osv.except_osv(_('Warning !'), _("You cannot change the type of account from 'Closed' to any other type which contains journal items!")) + # Forbid to change an account type for group_dest if move are on it (or his children) + if (new_type in group_dest): + raise osv.except_osv(_('Warning !'), _("You cannot change the type of account for '%s' type as it contains journal items!") % (new_type,)) + return True # For legal reason (forbiden to modify journal entries which belongs to a closed fy or period), Forbid to modify # the code of an account if journal entries have been already posted on this account. This cannot be simply From 8132259454bf6b19b9f740510a21cf087a112dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 10:49:58 +0200 Subject: [PATCH 14/59] [IMP] Add a constraints on reconcile object to forbid the reconciliation between different partner [IMP] Add a constraint on account move: you cannot pickup a date that is not in the fiscal year of the concerned period bzr revid: joel.grandguillaume@camptocamp.com-20121026084958-nlcoqlav00l42gwr --- addons/account/account.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/addons/account/account.py b/addons/account/account.py index 33150274bdf..f3ba38fee8d 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1313,6 +1313,14 @@ class account_move(osv.osv): 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, } + def _check_fiscal_year(self, cursor, user, ids): + for move in self.browse(cursor, user, ids): + date_start = move.period_id.fiscalyear_id.date_start + date_stop = move.period_id.fiscalyear_id.date_stop + if move.date < date_start or move.date > date_stop: + return False + return True + def _check_centralisation(self, cursor, user, ids, context=None): for move in self.browse(cursor, user, ids, context=context): if move.journal_id.centralisation: @@ -1328,6 +1336,9 @@ class account_move(osv.osv): (_check_centralisation, 'You cannot create more than one move per period on a centralized journal.', ['journal_id']), + (_check_fiscal_year, + 'You cannot create entries with date not in the fiscal year of the chosen period', + ['line_id','']), ] def post(self, cr, uid, ids, context=None): @@ -1686,7 +1697,25 @@ class account_move_reconcile(osv.osv): _defaults = { 'name': lambda self,cr,uid,ctx=None: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile', context=ctx) or '/', } + + # Look in the line_id and line_partial_ids to ensure the partner is the same or empty + # on all lines + def _check_same_partner(self, cr, uid, ids, context=None): + for reconcile in self.browse(cr, uid, ids, context=context): + if reconcile.line_id: + first_partner = reconcile.line_id[0].partner_id.id + move_lines = reconcile.line_id + elif reconcile.line_partial_ids: + first_partner = reconcile.line_partial_ids[0].partner_id.id + move_lines = reconcile.line_partial_ids + if any([line.partner_id.id != first_partner for line in move_lines]): + return False + return True + _constraints = [ + (_check_same_partner, 'You can only reconcile moves where the partner (we mean ID) is the same or empty on all entries.', ['line_id']), + ] + def reconcile_partial_check(self, cr, uid, ids, type='auto', context=None): total = 0.0 for rec in self.browse(cr, uid, ids, context=context): From eaf53213da2ba6bb0e620d8eadc58e33016e2aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 11:12:42 +0200 Subject: [PATCH 15/59] [IMP] Forbid the user to delete any move linked to an invoice. Cancelling invoice still work obviously bzr revid: joel.grandguillaume@camptocamp.com-20121026091242-9fu2a4do0ra50fqu --- addons/account/account.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/account/account.py b/addons/account/account.py index f3ba38fee8d..330e3f2d742 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1485,6 +1485,11 @@ class account_move(osv.osv): raise osv.except_osv(_('User Error!'), _('You cannot delete a posted journal entry "%s".') % \ move['name']) + for line in move.line_id: + if line.invoice: + raise osv.except_osv(_('User Error!'), + _("Move cannot be deleted if linked to an invoice: %s : Move ID:%s.") % \ + (line.invoice.number,move.name)) line_ids = map(lambda x: x.id, move.line_id) context['journal_id'] = move.journal_id.id context['period_id'] = move.period_id.id From 87be45dcd659384b6f63c7dc2e15a2efc5730d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 12:51:32 +0200 Subject: [PATCH 16/59] [IMP] For manual entries when multicurrency : a - Validation on the use of the 'Currency' and 'Currency Amount' fields as it is possible to enter one without the other b - Validation to prevent a Credit amount with a positive 'Currency Amount', or a Debit with a negative 'Currency Amount' bzr revid: joel.grandguillaume@camptocamp.com-20121026105132-6x0zh3gc823h3b68 --- addons/account/account_move_line.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 67a55d8f451..ffb9bb3ef0c 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -613,12 +613,27 @@ class account_move_line(osv.osv): return False return True + def _check_currency_and_amount(self, cr, uid, ids, context=None): + for l in self.browse(cr, uid, ids, context=context): + if (l.currency_id and not l.amount_currency) or (not l.currency_id and l.amount_currency): + return False + return True + + def _check_currency_amount(self, cr, uid, ids, context=None): + for l in self.browse(cr, uid, ids, context=context): + if l.amount_currency: + if (l.amount_currency > 0.0 and l.credit > 0.0) or (l.amount_currency < 0.0 and l.debit > 0.0): + return False + return True + _constraints = [ (_check_no_view, 'You cannot create journal items on an account of type view.', ['account_id']), (_check_no_closed, 'You cannot create journal items on closed account.', ['account_id']), (_check_company_id, 'Account and Period must belong to the same company.', ['company_id']), (_check_date, 'The date of your Journal Entry is not in the defined period! You should change the date or remove this constraint from the journal.', ['date']), (_check_currency, 'The selected account of your Journal Entry forces to provide a secondary currency. You should remove the secondary currency on the account or select a multi-currency view on the journal.', ['currency_id']), + (_check_currency_and_amount, "You cannot create journal items with a secondary currency without recording both 'currency' and 'amount currency' field.", ['currency_id','amount_currency']), + (_check_currency_amount, 'The amount expressed in the secondary currency must be positif when journal item are debit and negatif when journal item are credit.', ['amount_currency']), ] #TODO: ONCHANGE_ACCOUNT_ID: set account_tax_id From 72805c8ad407f7d1904233dd03fe8bcf943f4a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 13:06:31 +0200 Subject: [PATCH 17/59] [IMP] Add a check on entries that user cannot provide a secondary currency if the same than the company one. bzr revid: joel.grandguillaume@camptocamp.com-20121026110631-vz7vx4ae0axabh14 --- addons/account/account_move_line.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index ffb9bb3ef0c..147fe7f76a4 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -626,6 +626,12 @@ class account_move_line(osv.osv): return False return True + def _check_currency_company(self, cr, uid, ids, context=None): + for l in self.browse(cr, uid, ids, context=context): + if l.currency_id.id == l.company_id.currency_id.id: + return False + return True + _constraints = [ (_check_no_view, 'You cannot create journal items on an account of type view.', ['account_id']), (_check_no_closed, 'You cannot create journal items on closed account.', ['account_id']), @@ -634,6 +640,7 @@ class account_move_line(osv.osv): (_check_currency, 'The selected account of your Journal Entry forces to provide a secondary currency. You should remove the secondary currency on the account or select a multi-currency view on the journal.', ['currency_id']), (_check_currency_and_amount, "You cannot create journal items with a secondary currency without recording both 'currency' and 'amount currency' field.", ['currency_id','amount_currency']), (_check_currency_amount, 'The amount expressed in the secondary currency must be positif when journal item are debit and negatif when journal item are credit.', ['amount_currency']), + (_check_currency_company, "You can't provide a secondary currency if the same than the company one." , ['currency_id']), ] #TODO: ONCHANGE_ACCOUNT_ID: set account_tax_id From 9527a10ba50520bade31e8be081aa200bf0dcced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 13:16:41 +0200 Subject: [PATCH 18/59] [IMP] Forbid to change the journal of a bank statement if you already have a line in it. This is done in the voucher, cause this is the case that break : when voucher is created and you change the journal, it'll result in having entries generated on various journal which is not consistent. bzr revid: joel.grandguillaume@camptocamp.com-20121026111641-a0lg5h1mcyh1tit7 --- addons/account_voucher/account_voucher.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index f25318727df..3e9fe15d3d1 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1532,7 +1532,17 @@ class account_bank_statement(osv.osv): }) return move_line_obj.write(cr, uid, [x.id for x in v.move_ids], {'statement_id': st_line.statement_id.id}, context=context) - return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line.id, company_currency_id, next_number, context=context) + + def write(self, cr, uid, ids, vals, context=None): + # We should not be able to change the journal if we already have + # bank statement line generated in case we use the voucher. This is because + # the import feature will write the current used journal. That lead to crap + # cause we should not be able to do that... A bank statement is by definition linked + # to one journal ! + for bk_st in self.browse(cr, uid, ids, context=context): + if vals.get('journal_id', False) and bk_st.line_ids: + raise osv.except_osv(_('Unable to change journal !'), _('You can not change the journal if you already generated lines !')) + return super(account_bank_statement, self).write(cr, uid, ids, vals, context=context) account_bank_statement() From 6ef3db0a864612cc54be4a4dec2c9247ef5d7157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 13:35:02 +0200 Subject: [PATCH 19/59] [FIX] Do not provide a secondary currency when posting entries from bank statment if the same than the company to respect the new constraints. bzr revid: joel.grandguillaume@camptocamp.com-20121026113502-jsxdf5swqfizfcen --- addons/account/account_bank_statement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 48082451f35..68d1f3279f3 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -311,7 +311,7 @@ class account_bank_statement(osv.osv): 'statement_id': st_line.statement_id.id, 'journal_id': st_line.statement_id.journal_id.id, 'period_id': st_line.statement_id.period_id.id, - 'currency_id': cur_id, + 'currency_id': amount_currency and cur_id, 'amount_currency': amount_currency, 'analytic_account_id': analytic_id, } From 19e1d840c4264f96e99e424260f44a701a7564b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 14:29:21 +0200 Subject: [PATCH 20/59] [IMP] Add contraint for Payment Order : If a invoice is imported in a payment order, forbid to reset invoice to cancel or draft bzr revid: joel.grandguillaume@camptocamp.com-20121026122921-fdlrothxta992cd3 --- addons/account_payment/account_invoice.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/addons/account_payment/account_invoice.py b/addons/account_payment/account_invoice.py index 7138540fa3b..60085a890c7 100644 --- a/addons/account_payment/account_invoice.py +++ b/addons/account_payment/account_invoice.py @@ -20,12 +20,29 @@ ############################################################################## from datetime import datetime - +from tools.translate import _ from osv import fields, osv class Invoice(osv.osv): _inherit = 'account.invoice' + # Forbid to cancel an invoice if the related move lines have already been + # used in a payment order. The risk is that importing the payment line + # in the bank statement will result in a crash cause no more move will + # be found in the payment line + def action_cancel(self, cr, uid, ids, *args): + payment_line_obj = self.pool.get('payment.line') + invoices = self.browse(cr, uid, ids) + for inv in invoices: + inv_mv_lines = map(lambda x: x.id, inv.move_id.line_id) + pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id','in',inv_mv_lines)]) + if pl_line_ids: + pay_line = payment_line_obj.browse(cr,uid,pl_line_ids) + payment_order_name = ','.join(map(lambda x: x.order_id.reference, pay_line)) + raise osv.except_osv(_('Error!'), _("You cannot cancel an invoice which has already been imported in a payment order. Remove it from the following payment order : %s."%(payment_order_name))) + result = super(Invoice, self).action_cancel(cr, uid, ids, *args) + return result + def _amount_to_pay(self, cursor, user, ids, name, args, context=None): '''Return the amount still to pay regarding all the payment orders''' if not ids: From 227738dedb586128eac1753795bc85d806ee127d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Fri, 26 Oct 2012 15:54:15 +0200 Subject: [PATCH 21/59] [IMP] Forbid to remove the reconcile on opening entries, we introduce a new boolean field to identify the reconciliation made by the closing process from others. bzr revid: joel.grandguillaume@camptocamp.com-20121026135415-k1q0yq5zxngmfm5k --- addons/account/account.py | 31 +++++++++++++------ addons/account/account_move_line.py | 4 ++- .../wizard/account_fiscalyear_close.py | 4 +-- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index 330e3f2d742..2e60c47d758 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1698,23 +1698,36 @@ class account_move_reconcile(osv.osv): 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry Lines'), 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'), 'create_date': fields.date('Creation date', readonly=True), + 'opening_reconcile': fields.boolean('Opening entries reconciliation', help="Is this reconciliation produced by the opening of a new fiscal year ?."), } _defaults = { 'name': lambda self,cr,uid,ctx=None: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile', context=ctx) or '/', + 'opening_reconcile': 0, } + # You cannot unlink a reconciliation if it is a opening_reconcile one, + # you should use the generate opening entries wizard for that + def unlink(self, cr, uid, ids, context=None): + for move_rec in self.browse(cr, uid, ids): + if move_rec.opening_reconcile: + raise osv.except_osv(_('Error!'), _('You cannot unreconcile journal items if they has been generated by the \ + opening/closing fiscal year process.')) + return super(account_move_reconcile, self).unlink(cr, uid, ids, context=context) + # Look in the line_id and line_partial_ids to ensure the partner is the same or empty - # on all lines + # on all lines. We allow that only for opening/closing period def _check_same_partner(self, cr, uid, ids, context=None): for reconcile in self.browse(cr, uid, ids, context=context): - if reconcile.line_id: - first_partner = reconcile.line_id[0].partner_id.id - move_lines = reconcile.line_id - elif reconcile.line_partial_ids: - first_partner = reconcile.line_partial_ids[0].partner_id.id - move_lines = reconcile.line_partial_ids - if any([line.partner_id.id != first_partner for line in move_lines]): - return False + move_lines = [] + if not reconcile.opening_reconcile: + if reconcile.line_id: + first_partner = reconcile.line_id[0].partner_id.id + move_lines = reconcile.line_id + elif reconcile.line_partial_ids: + first_partner = reconcile.line_partial_ids[0].partner_id.id + move_lines = reconcile.line_partial_ids + if any([line.partner_id.id != first_partner for line in move_lines]): + return False return True _constraints = [ diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 147fe7f76a4..ed6b9caf9a8 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -1126,7 +1126,7 @@ class account_move_line(osv.osv): 'has been confirmed.') % res[2]) return res - def _remove_move_reconcile(self, cr, uid, move_ids=None, context=None): + def _remove_move_reconcile(self, cr, uid, move_ids=None, opening_reconcile=False, context=None): # Function remove move rencocile ids related with moves obj_move_line = self.pool.get('account.move.line') obj_move_rec = self.pool.get('account.move.reconcile') @@ -1141,6 +1141,8 @@ class account_move_line(osv.osv): unlink_ids += rec_ids unlink_ids += part_rec_ids if unlink_ids: + if opening_reconcile: + obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconcile':False}) obj_move_rec.unlink(cr, uid, unlink_ids) return True diff --git a/addons/account/wizard/account_fiscalyear_close.py b/addons/account/wizard/account_fiscalyear_close.py index 196c14d3792..60a2e486948 100644 --- a/addons/account/wizard/account_fiscalyear_close.py +++ b/addons/account/wizard/account_fiscalyear_close.py @@ -60,7 +60,7 @@ class account_fiscalyear_close(osv.osv_memory): cr.execute('select distinct(company_id) from account_move_line where id in %s',(tuple(ids),)) if len(cr.fetchall()) > 1: raise osv.except_osv(_('Warning!'), _('The entries to reconcile should belong to the same company.')) - r_id = self.pool.get('account.move.reconcile').create(cr, uid, {'type': 'auto'}) + r_id = self.pool.get('account.move.reconcile').create(cr, uid, {'type': 'auto', 'opening_reconcile':True}) cr.execute('update account_move_line set reconcile_id = %s where id in %s',(r_id, tuple(ids),)) return r_id @@ -107,7 +107,7 @@ class account_fiscalyear_close(osv.osv_memory): ('journal_id', '=', new_journal.id), ('period_id', '=', period.id)]) if move_ids: move_line_ids = obj_acc_move_line.search(cr, uid, [('move_id', 'in', move_ids)]) - obj_acc_move_line._remove_move_reconcile(cr, uid, move_line_ids, context=context) + obj_acc_move_line._remove_move_reconcile(cr, uid, move_line_ids, opening_reconcile=True, context=context) obj_acc_move_line.unlink(cr, uid, move_line_ids, context=context) obj_acc_move.unlink(cr, uid, move_ids, context=context) From 516e8697dd50123917f6b3d650628c15d2525c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Wed, 31 Oct 2012 13:50:03 +0100 Subject: [PATCH 22/59] [FIX] Re-add the call to super in create_move_from_st_line in the voucher module. I removed it by mistake... Thanks to Stefan bzr revid: joel.grandguillaume@camptocamp.com-20121031125003-0oe3d4ncvmblmc8p --- addons/account_voucher/account_voucher.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 6ad3212892c..cca9daeaa8f 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1532,6 +1532,7 @@ class account_bank_statement(osv.osv): }) return move_line_obj.write(cr, uid, [x.id for x in v.move_ids], {'statement_id': st_line.statement_id.id}, context=context) + return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line.id, company_currency_id, next_number, context=context) def write(self, cr, uid, ids, vals, context=None): # We should not be able to change the journal if we already have From 49dd95bf51ac8c6434bd42ea8620723b90cfe3c9 Mon Sep 17 00:00:00 2001 From: "ajay javiya (OpenERP)" Date: Fri, 2 Nov 2012 16:50:02 +0530 Subject: [PATCH 23/59] [FIX]: Password field should not allows to setdefault bzr revid: aja@tinyerp.com-20121102112002-awvp6juu2mvdndz2 --- addons/web/static/src/js/view_form.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index e0727b99259..4e6a8bcf437 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -990,7 +990,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM || field.get("readonly") || field.field.type === 'one2many' || field.field.type === 'many2many' - || field.field.type === 'binary') { + || field.field.type === 'binary' + || field.password) { return false; } From 84050666abeff213fdc7fa10a6b4021107162f2a Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 22 Nov 2012 15:28:19 +0100 Subject: [PATCH 24/59] [REF] some code/error message refactoring [REM] account: removed _check_fiscal_year constraint on account.move.line (was made to ensure a journal item's date is included inbetween the date start and stop of the fiscalyear it belongs) over which we have a doubt [FIX] account: fixed _check_currency_and_amount constraint on account.move.line (having 0 as amount_currency is a legal value since debit and credit can both be 0 (used in case of complex taxes)) [FIX] account_voucher: changed the behavior in order to restrict the update of the journal in a bank statement only when there is a voucher already created/generated for that, because previous check was too restrictive. bzr revid: qdp-launchpad@openerp.com-20121122142819-wvqak470vn3ete27 --- addons/account/account.py | 40 +++++++------------ addons/account/account_move_line.py | 10 ++--- .../wizard/account_fiscalyear_close.py | 4 +- addons/account_payment/account_invoice.py | 14 +++---- addons/account_voucher/account_voucher.py | 12 +++--- 5 files changed, 32 insertions(+), 48 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index da161c48fcd..15881100441 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -641,7 +641,7 @@ class account_account(osv.osv): return True def _check_allow_type_change(self, cr, uid, ids, new_type, context=None): - group_dest = ['consolidation','view'] + restricted_groups = ['consolidation','view'] line_obj = self.pool.get('account.move.line') for account in self.browse(cr, uid, ids, context=context): old_type = account.type @@ -649,10 +649,10 @@ class account_account(osv.osv): if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): #Check for 'Closed' type if old_type == 'closed' and new_type !='closed': - raise osv.except_osv(_('Warning !'), _("You cannot change the type of account from 'Closed' to any other type which contains journal items!")) - # Forbid to change an account type for group_dest if move are on it (or his children) - if (new_type in group_dest): - raise osv.except_osv(_('Warning !'), _("You cannot change the type of account for '%s' type as it contains journal items!") % (new_type,)) + raise osv.except_osv(_('Warning !'), _("You cannot change the type of account from 'Closed' to any other type as it contains journal items!")) + # Forbid to change an account type for restricted_groups as it contains journal items (or if one of its children does) + if (new_type in restricted_groups): + raise osv.except_osv(_('Warning !'), _("You cannot change the type of account to '%s' type as it contains journal items!") % (new_type,)) return True @@ -662,8 +662,8 @@ class account_account(osv.osv): def _check_allow_code_change(self, cr, uid, ids, context=None): line_obj = self.pool.get('account.move.line') for account in self.browse(cr, uid, ids, context=context): - account_ids = self.search(cr, uid, [('id', 'child_of', [account.id])]) - if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): + account_ids = self.search(cr, uid, [('id', 'child_of', [account.id])], context=context) + if line_obj.search(cr, uid, [('account_id', 'in', account_ids)], context=context): raise osv.except_osv(_('Warning !'), _("You cannot change the code of account which contains journal items!")) return True @@ -1321,14 +1321,6 @@ class account_move(osv.osv): 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, } - def _check_fiscal_year(self, cursor, user, ids): - for move in self.browse(cursor, user, ids): - date_start = move.period_id.fiscalyear_id.date_start - date_stop = move.period_id.fiscalyear_id.date_stop - if move.date < date_start or move.date > date_stop: - return False - return True - def _check_centralisation(self, cursor, user, ids, context=None): for move in self.browse(cursor, user, ids, context=context): if move.journal_id.centralisation: @@ -1344,9 +1336,6 @@ class account_move(osv.osv): (_check_centralisation, 'You cannot create more than one move per period on a centralized journal.', ['journal_id']), - (_check_fiscal_year, - 'You cannot create entries with date not in the fiscal year of the chosen period', - ['line_id','']), ] def post(self, cr, uid, ids, context=None): @@ -1496,7 +1485,7 @@ class account_move(osv.osv): for line in move.line_id: if line.invoice: raise osv.except_osv(_('User Error!'), - _("Move cannot be deleted if linked to an invoice: %s : Move ID:%s.") % \ + _("Move cannot be deleted if linked to an invoice. (Invoice: %s - Move ID:%s)") % \ (line.invoice.number,move.name)) line_ids = map(lambda x: x.id, move.line_id) context['journal_id'] = move.journal_id.id @@ -1706,18 +1695,17 @@ class account_move_reconcile(osv.osv): 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry Lines'), 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'), 'create_date': fields.date('Creation date', readonly=True), - 'opening_reconcile': fields.boolean('Opening entries reconciliation', help="Is this reconciliation produced by the opening of a new fiscal year ?."), + 'opening_reconciliation': fields.boolean('Opening Entries Reconciliation', help="Is this reconciliation produced by the opening of a new fiscal year ?."), } _defaults = { 'name': lambda self,cr,uid,ctx=None: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile', context=ctx) or '/', - 'opening_reconcile': 0, } - # You cannot unlink a reconciliation if it is a opening_reconcile one, + # You cannot unlink a reconciliation if it is a opening_reconciliation one, # you should use the generate opening entries wizard for that def unlink(self, cr, uid, ids, context=None): - for move_rec in self.browse(cr, uid, ids): - if move_rec.opening_reconcile: + for move_rec in self.browse(cr, uid, ids, context=context): + if move_rec.opening_reconciliation: raise osv.except_osv(_('Error!'), _('You cannot unreconcile journal items if they has been generated by the \ opening/closing fiscal year process.')) return super(account_move_reconcile, self).unlink(cr, uid, ids, context=context) @@ -1727,7 +1715,7 @@ class account_move_reconcile(osv.osv): def _check_same_partner(self, cr, uid, ids, context=None): for reconcile in self.browse(cr, uid, ids, context=context): move_lines = [] - if not reconcile.opening_reconcile: + if not reconcile.opening_reconciliation: if reconcile.line_id: first_partner = reconcile.line_id[0].partner_id.id move_lines = reconcile.line_id @@ -1739,7 +1727,7 @@ class account_move_reconcile(osv.osv): return True _constraints = [ - (_check_same_partner, 'You can only reconcile moves where the partner (we mean ID) is the same or empty on all entries.', ['line_id']), + (_check_same_partner, 'You can only reconcile journal items with the same partner.', ['line_id']), ] def reconcile_partial_check(self, cr, uid, ids, type='auto', context=None): diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index b5e49951dfa..935ae70a059 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -615,7 +615,7 @@ class account_move_line(osv.osv): def _check_currency_and_amount(self, cr, uid, ids, context=None): for l in self.browse(cr, uid, ids, context=context): - if (l.currency_id and not l.amount_currency) or (not l.currency_id and l.amount_currency): + if (l.amount_currency and not l.currency_id): return False return True @@ -640,7 +640,7 @@ class account_move_line(osv.osv): (_check_currency, 'The selected account of your Journal Entry forces to provide a secondary currency. You should remove the secondary currency on the account or select a multi-currency view on the journal.', ['currency_id']), (_check_currency_and_amount, "You cannot create journal items with a secondary currency without recording both 'currency' and 'amount currency' field.", ['currency_id','amount_currency']), (_check_currency_amount, 'The amount expressed in the secondary currency must be positif when journal item are debit and negatif when journal item are credit.', ['amount_currency']), - (_check_currency_company, "You can't provide a secondary currency if the same than the company one." , ['currency_id']), + (_check_currency_company, "You cannot provide a secondary currency if it is the same than the company one." , ['currency_id']), ] #TODO: ONCHANGE_ACCOUNT_ID: set account_tax_id @@ -1126,7 +1126,7 @@ class account_move_line(osv.osv): 'has been confirmed.') % res[2]) return res - def _remove_move_reconcile(self, cr, uid, move_ids=None, opening_reconcile=False, context=None): + def _remove_move_reconcile(self, cr, uid, move_ids=None, opening_reconciliation=False, context=None): # Function remove move rencocile ids related with moves obj_move_line = self.pool.get('account.move.line') obj_move_rec = self.pool.get('account.move.reconcile') @@ -1141,8 +1141,8 @@ class account_move_line(osv.osv): unlink_ids += rec_ids unlink_ids += part_rec_ids if unlink_ids: - if opening_reconcile: - obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconcile':False}) + if opening_reconciliation: + obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconciliation': False}) obj_move_rec.unlink(cr, uid, unlink_ids) return True diff --git a/addons/account/wizard/account_fiscalyear_close.py b/addons/account/wizard/account_fiscalyear_close.py index 600b0312596..e11b4dcd34b 100644 --- a/addons/account/wizard/account_fiscalyear_close.py +++ b/addons/account/wizard/account_fiscalyear_close.py @@ -60,7 +60,7 @@ class account_fiscalyear_close(osv.osv_memory): cr.execute('select distinct(company_id) from account_move_line where id in %s',(tuple(ids),)) if len(cr.fetchall()) > 1: raise osv.except_osv(_('Warning!'), _('The entries to reconcile should belong to the same company.')) - r_id = self.pool.get('account.move.reconcile').create(cr, uid, {'type': 'auto', 'opening_reconcile':True}) + r_id = self.pool.get('account.move.reconcile').create(cr, uid, {'type': 'auto', 'opening_reconciliation': True}) cr.execute('update account_move_line set reconcile_id = %s where id in %s',(r_id, tuple(ids),)) return r_id @@ -107,7 +107,7 @@ class account_fiscalyear_close(osv.osv_memory): ('journal_id', '=', new_journal.id), ('period_id', '=', period.id)]) if move_ids: move_line_ids = obj_acc_move_line.search(cr, uid, [('move_id', 'in', move_ids)]) - obj_acc_move_line._remove_move_reconcile(cr, uid, move_line_ids, opening_reconcile=True, context=context) + obj_acc_move_line._remove_move_reconcile(cr, uid, move_line_ids, opening_reconciliation=True, context=context) obj_acc_move_line.unlink(cr, uid, move_line_ids, context=context) obj_acc_move.unlink(cr, uid, move_ids, context=context) diff --git a/addons/account_payment/account_invoice.py b/addons/account_payment/account_invoice.py index 60085a890c7..2ef894df4b5 100644 --- a/addons/account_payment/account_invoice.py +++ b/addons/account_payment/account_invoice.py @@ -30,18 +30,16 @@ class Invoice(osv.osv): # used in a payment order. The risk is that importing the payment line # in the bank statement will result in a crash cause no more move will # be found in the payment line - def action_cancel(self, cr, uid, ids, *args): + def action_cancel(self, cr, uid, ids, context=None): payment_line_obj = self.pool.get('payment.line') - invoices = self.browse(cr, uid, ids) - for inv in invoices: - inv_mv_lines = map(lambda x: x.id, inv.move_id.line_id) - pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id','in',inv_mv_lines)]) + for inv in self.browse(cr, uid, ids, context=context): + inv_mv_lines = [x.id for x in inv.move_id.line_id] + pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id','in',inv_mv_lines)], context=context) if pl_line_ids: - pay_line = payment_line_obj.browse(cr,uid,pl_line_ids) + pay_line = payment_line_obj.browse(cr, uid, pl_line_ids, context=context) payment_order_name = ','.join(map(lambda x: x.order_id.reference, pay_line)) raise osv.except_osv(_('Error!'), _("You cannot cancel an invoice which has already been imported in a payment order. Remove it from the following payment order : %s."%(payment_order_name))) - result = super(Invoice, self).action_cancel(cr, uid, ids, *args) - return result + return super(Invoice, self).action_cancel(cr, uid, ids, context=context) def _amount_to_pay(self, cursor, user, ids, name, args, context=None): '''Return the amount still to pay regarding all the payment orders''' diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index a579617c754..ffc7cf1769d 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1550,14 +1550,12 @@ class account_bank_statement(osv.osv): return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line.id, company_currency_id, next_number, context=context) def write(self, cr, uid, ids, vals, context=None): - # We should not be able to change the journal if we already have - # bank statement line generated in case we use the voucher. This is because - # the import feature will write the current used journal. That lead to crap - # cause we should not be able to do that... A bank statement is by definition linked - # to one journal ! + # Restrict to modify the journal if we already have some voucher of reconciliation created/generated. + # Because the voucher keeps in memory the journal it was created with. for bk_st in self.browse(cr, uid, ids, context=context): - if vals.get('journal_id', False) and bk_st.line_ids: - raise osv.except_osv(_('Unable to change journal !'), _('You can not change the journal if you already generated lines !')) + if vals.get('journal_id') and bk_st.line_ids: + if any([x.voucher_id and True or False for x in bk_st.line_ids]): + raise osv.except_osv(_('Unable to change journal !'), _('You can not change the journal as you already reconciled some statement lines!')) return super(account_bank_statement, self).write(cr, uid, ids, vals, context=context) account_bank_statement() From 04ee6498553a34e9ecf691bbe9cd367d2980d1b5 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 22 Nov 2012 16:11:25 +0100 Subject: [PATCH 25/59] [ADD] When application starts, warn the user if it's timezone does not match it's browser's timezone bzr revid: fme@openerp.com-20121122151125-a8fybzmqyw8uvr7h --- addons/web/static/src/js/chrome.js | 32 ++++++++++++++++++++++++++---- addons/web/static/src/xml/base.xml | 14 +++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index f355322b489..b7cbbe37c9f 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -24,7 +24,7 @@ instance.web.Notification = instance.web.Widget.extend({ if (sticky) { opts.expires = false; } - this.$el.notify('create', { + return this.$el.notify('create', { title: title, text: text }, opts); @@ -35,7 +35,7 @@ instance.web.Notification = instance.web.Widget.extend({ if (sticky) { opts.expires = false; } - this.$el.notify('create', 'oe_notification_alert', { + return this.$el.notify('create', 'oe_notification_alert', { title: title, text: text }, opts); @@ -1151,6 +1151,30 @@ instance.web.WebClient = instance.web.Client.extend({ self.user_menu.do_update(); self.bind_hashchange(); self.set_title(); + self.check_timezone(); + }, + check_timezone: function() { + var self = this; + var user_offset = instance.session.user_context.tz_offset; + var offset = -(new Date().getTimezoneOffset()); + // _.str.sprintf()'s zero front padding is buggy with signed decimals, so doing it manually + var browser_offset = (offset < 0) ? "-" : "+"; + browser_offset += _.str.sprintf("%02d", Math.abs(offset / 60)); + browser_offset += _.str.sprintf("%02d", Math.abs(offset % 60)); + if (browser_offset !== user_offset) { + var notification = this.do_warn(_t("Timezone"), QWeb.render('WebClient.timezone_notification', { + user_timezone: instance.session.user_context.tz || 'UTC', + user_offset: user_offset, + browser_offset: browser_offset, + }), true); + notification.element.find('.oe_webclient_timezone_notification').on('click', function() { + notification.close(); + }).find('a').on('click', function() { + notification.close(); + self.user_menu.on_menu_settings(); + return false; + }); + } }, destroy_content: function() { _.each(_.clone(this.getChildren()), function(el) { @@ -1167,11 +1191,11 @@ instance.web.WebClient = instance.web.Client.extend({ }, do_notify: function() { var n = this.notification; - n.notify.apply(n, arguments); + return n.notify.apply(n, arguments); }, do_warn: function() { var n = this.notification; - n.warn.apply(n, arguments); + return n.warn.apply(n, arguments); }, on_logout: function() { var self = this; diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 6476462ff19..e405304b5ff 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -443,6 +443,20 @@ + +
+

Your user's preference timezone does not match your browser timezone:

+ +
+
User's timezone
+
()
+
Browser's timezone
+
+
+ +

Click here to change your user's timezone.

+
+
From b629963f24faec00ab6e25e5905f1cc1885e02ad Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 22 Nov 2012 16:14:25 +0100 Subject: [PATCH 26/59] [REM] Removed unused fields from base.sql bzr revid: fme@openerp.com-20121122151425-fr8o1v587vgmnf5f --- openerp/addons/base/base.sql | 2 -- 1 file changed, 2 deletions(-) diff --git a/openerp/addons/base/base.sql b/openerp/addons/base/base.sql index c490df32294..9da09c49a4a 100644 --- a/openerp/addons/base/base.sql +++ b/openerp/addons/base/base.sql @@ -149,8 +149,6 @@ CREATE TABLE res_users ( active boolean default True, login varchar(64) NOT NULL UNIQUE, password varchar(64) default null, - tz varchar(64) default null, - lang varchar(64) default '', -- No FK references below, will be added later by ORM -- (when the destination rows exist) company_id int, From ee8894d50937e4eb853fae7d4bd41aa66509522c Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 22 Nov 2012 16:14:58 +0100 Subject: [PATCH 27/59] [ADD] Add tz_offset function field in res.partner and set it in user's context bzr revid: fme@openerp.com-20121122151458-hvtrwogldt69djss --- openerp/addons/base/res/res_partner.py | 8 ++++++++ openerp/addons/base/res/res_users.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py index df42bb81521..165cbc94e07 100644 --- a/openerp/addons/base/res/res_partner.py +++ b/openerp/addons/base/res/res_partner.py @@ -19,6 +19,7 @@ # ############################################################################## +import datetime import math import openerp from osv import osv, fields @@ -178,6 +179,12 @@ class res_partner(osv.osv, format_address): result[obj.id] = tools.image_get_resized_images(obj.image) return result + def _get_tz_offset(self, cr, uid, ids, name, args, context=None): + result = dict.fromkeys(ids, False) + for obj in self.browse(cr, uid, ids, context=context): + result[obj.id] = datetime.datetime.now(pytz.timezone(obj.tz or 'GMT')).strftime('%z') + return result + def _set_image(self, cr, uid, id, name, value, args, context=None): return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) @@ -195,6 +202,7 @@ class res_partner(osv.osv, format_address): help="The partner's timezone, used to output proper date and time values inside printed reports. " "It is important to set a value for this field. You should use the same timezone " "that is otherwise used to pick and render date and time values: your computer's timezone."), + 'tz_offset': fields.function(_get_tz_offset, type='char', size=5, string='Timezone offset', store=True), 'user_id': fields.many2one('res.users', 'Salesperson', help='The internal user that is in charge of communicating with this contact if any.'), 'vat': fields.char('TIN', size=32, help="Tax Identification Number. Check the box if this contact is subjected to taxes. Used by the some of the legal statements."), 'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'), diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py index 5d376ccf07f..0a9ba2450f9 100644 --- a/openerp/addons/base/res/res_users.py +++ b/openerp/addons/base/res/res_users.py @@ -343,7 +343,7 @@ class res_users(osv.osv): for k in self._all_columns.keys(): if k.startswith('context_'): context_key = k[8:] - elif k in ['lang', 'tz']: + elif k in ['lang', 'tz', 'tz_offset']: context_key = k else: context_key = False From 0615c9faaf46bcad4e1d3ebddfccf0ffbfd4b6aa Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 22 Nov 2012 16:21:59 +0100 Subject: [PATCH 28/59] [FIX] lang on res.users is still used bzr revid: fme@openerp.com-20121122152159-a9lxb4fwn8qkvsvt --- openerp/addons/base/base.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/addons/base/base.sql b/openerp/addons/base/base.sql index 9da09c49a4a..3fa847ce23c 100644 --- a/openerp/addons/base/base.sql +++ b/openerp/addons/base/base.sql @@ -149,6 +149,7 @@ CREATE TABLE res_users ( active boolean default True, login varchar(64) NOT NULL UNIQUE, password varchar(64) default null, + lang varchar(64) default '', -- No FK references below, will be added later by ORM -- (when the destination rows exist) company_id int, From c5a9314a0f34ffc334ae2283fb79029d3cb70aba Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Mon, 26 Nov 2012 10:52:27 +0100 Subject: [PATCH 29/59] [FIX] Add a check on empty list to avoid a crash if no move lines in the related invoice. bzr revid: joel.grandguillaume@camptocamp.com-20121126095227-ubezcjc9q0mnr1gg --- addons/account_payment/account_invoice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/account_payment/account_invoice.py b/addons/account_payment/account_invoice.py index 2ef894df4b5..750be5b3e96 100644 --- a/addons/account_payment/account_invoice.py +++ b/addons/account_payment/account_invoice.py @@ -33,8 +33,10 @@ class Invoice(osv.osv): def action_cancel(self, cr, uid, ids, context=None): payment_line_obj = self.pool.get('payment.line') for inv in self.browse(cr, uid, ids, context=context): - inv_mv_lines = [x.id for x in inv.move_id.line_id] - pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id','in',inv_mv_lines)], context=context) + pl_line_ids = False + if inv.move_id and inv.move_id.line_id: + inv_mv_lines = [x.id for x in inv.move_id.line_id] + pl_line_ids = payment_line_obj.search(cr, uid, [('move_line_id','in',inv_mv_lines)], context=context) if pl_line_ids: pay_line = payment_line_obj.browse(cr, uid, pl_line_ids, context=context) payment_order_name = ','.join(map(lambda x: x.order_id.reference, pay_line)) From 59bbb0206481256610d47f29bd8e3f42977b2d2a Mon Sep 17 00:00:00 2001 From: Joel Grand-Guillaume Date: Mon, 26 Nov 2012 10:58:34 +0100 Subject: [PATCH 30/59] [FIX] YAML test on account voucher : Create a second USD account for testing, avoiding one of the account constraint to raise. bzr revid: joel.grandguillaume@camptocamp.com-20121126095834-o29f5cwoffu2t0ic --- addons/account_voucher/test/case5_suppl_usd_usd.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/account_voucher/test/case5_suppl_usd_usd.yml b/addons/account_voucher/test/case5_suppl_usd_usd.yml index 3cae9667d84..2829c9ed440 100644 --- a/addons/account_voucher/test/case5_suppl_usd_usd.yml +++ b/addons/account_voucher/test/case5_suppl_usd_usd.yml @@ -4,7 +4,7 @@ - I create a cash account with currency USD - - !record {model: account.account, id: account_cash_usd_id}: + !record {model: account.account, id: account_cash_usd_id2}: currency_id: base.USD name: "cash account in usd" code: "Xcash usd" @@ -56,8 +56,8 @@ type: bank analytic_journal_id: account.sit sequence_id: account.sequence_bank_journal - default_debit_account_id: account_cash_usd_id - default_credit_account_id: account_cash_usd_id + default_debit_account_id: account_cash_usd_id2 + default_credit_account_id: account_cash_usd_id2 currency: base.USD company_id: base.main_company view_id: account.account_journal_bank_view From 5b714460ba337e0f80101697b3d184e1ae7cc4c4 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Mon, 26 Nov 2012 11:38:04 +0100 Subject: [PATCH 31/59] [FIX] accuracy of the mail server action 'mark read' and 'mark unread' bzr revid: chm@openerp.com-20121126103804-yhcvekt0ruq4fef1 --- addons/base_calendar/crm_meeting_view.xml | 4 ++-- addons/crm/crm_lead_view.xml | 4 ++-- addons/crm/crm_phonecall_view.xml | 4 ++-- addons/event/event_view.xml | 8 ++++---- addons/hr_holidays/hr_holidays_view.xml | 4 ++-- addons/hr_recruitment/hr_recruitment_view.xml | 4 ++-- addons/project/project_view.xml | 8 ++++---- addons/project_issue/project_issue_view.xml | 4 ++-- addons/purchase/purchase_view.xml | 4 ++-- addons/sale/sale_view.xml | 4 ++-- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index 8590bcb8b49..a9c03db9dae 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -5,7 +5,7 @@ - Mark read + CRM meeting : Mark read True ir.actions.server @@ -23,7 +23,7 @@ - Mark unread + CRM meeting : Mark unread True ir.actions.server diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index d06b6232b5e..f7083f7cbde 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -4,7 +4,7 @@ - Mark unread + CRM lead : Mark unread True ir.actions.server @@ -22,7 +22,7 @@ - Mark read + CRM lead : Mark read True ir.actions.server diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml index 9cb09e71be2..3e5a2ec5413 100644 --- a/addons/crm/crm_phonecall_view.xml +++ b/addons/crm/crm_phonecall_view.xml @@ -4,7 +4,7 @@ - Mark unread + CRM phonecall : Mark unread True ir.actions.server @@ -22,7 +22,7 @@ - Mark read + CRM phonecall : Mark read True ir.actions.server diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 14895f2a1a7..da3803bfc9c 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -59,7 +59,7 @@ - Mark unread + Event : Mark unread True ir.actions.server @@ -77,7 +77,7 @@ - Mark read + Event : Mark read True ir.actions.server @@ -377,7 +377,7 @@ - Mark unread + Event registration : Mark unread True ir.actions.server @@ -395,7 +395,7 @@ - Mark read + Event registration : Mark read True ir.actions.server diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index 752d278baed..abf187017a5 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -3,7 +3,7 @@ - Mark unread + Holidays : Mark unread True ir.actions.server @@ -21,7 +21,7 @@ - Mark read + Holidays : Mark read True ir.actions.server diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index c3281b43b94..7a196287f5f 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -37,7 +37,7 @@ - Mark unread + HR applicant : Mark unread True ir.actions.server @@ -55,7 +55,7 @@ - Mark read + HR applicant : Mark read True ir.actions.server diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 8bb45ca6738..c3b71baa668 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -21,7 +21,7 @@ - Mark unread + Project : Mark unread True ir.actions.server @@ -39,7 +39,7 @@ - Mark read + Project : Mark read True ir.actions.server @@ -344,7 +344,7 @@ - Mark unread + Project task : Mark unread True ir.actions.server @@ -362,7 +362,7 @@ - Mark read + Project task : Mark read True ir.actions.server diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index e3105127c5d..dbc7a7ae8a4 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -6,7 +6,7 @@ - Mark unread + Project issue : Mark unread True ir.actions.server @@ -24,7 +24,7 @@ - Mark read + Project issue : Mark read True ir.actions.server diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 4c7aea1caac..1fc4925fc72 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -134,7 +134,7 @@ - Mark unread + Purchase : Mark unread True ir.actions.server @@ -152,7 +152,7 @@ - Mark read + Purchase : Mark read True ir.actions.server diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 419047dbbd2..cecfbae0aae 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -68,7 +68,7 @@ - Mark unread + Sale : Mark unread True ir.actions.server @@ -86,7 +86,7 @@ - Mark read + Sale : Mark read True ir.actions.server From 7aeaabaa8955ac3ff21e791be818c102aca41b8d Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 27 Nov 2012 09:39:35 +0100 Subject: [PATCH 32/59] [FIX] web kanban: Remember all Ids dataset. For bug : click on Show More...(At Bottom of Kanban view). After, when you click on a record, it give : could not find id#x error. bzr revid: chm@openerp.com-20121127083935-6tmbvee0ghuta00f --- addons/web_kanban/static/src/js/kanban.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 398d65d17ce..357fa049d5f 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -642,13 +642,12 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ }, do_show_more: function(evt) { var self = this; + var ids = self.view.dataset.ids.splice(0); return this.dataset.read_slice(this.view.fields_keys.concat(['__last_update']), { 'limit': self.view.limit, 'offset': self.dataset_offset += self.view.limit }).then(function(records) { - records.forEach(function(r) { - self.view.dataset.ids.push(r.id); - }); + self.view.dataset.ids = self.view.dataset.ids.concat(ids); self.do_add_records(records); return records; }); From fbb42640ead9eaa430b12c487cf2b9d03027e8cc Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 27 Nov 2012 09:49:17 +0100 Subject: [PATCH 33/59] [FIX] web kanban: vertical-align: top. For bug : Hole on the kanban view like projects because the images is in vertical-align: bottom; bzr revid: chm@openerp.com-20121127084917-npvir1lkgol5ysbk --- addons/web_kanban/static/src/css/kanban.css | 1 + addons/web_kanban/static/src/css/kanban.sass | 1 + 2 files changed, 2 insertions(+) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 9b74462ee13..81d8b709c21 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -279,6 +279,7 @@ .openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_column .oe_kanban_record { display: inline-block; padding: 2px; + vertical-align: top; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index b993b648691..8ccc831ed2c 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -266,6 +266,7 @@ .oe_kanban_record display: inline-block padding: 2px + vertical-align: top box-sizing: border-box -moz-box-sizing: border-box -webkit-box-sizing: border-box From ac7695e1c9eaad3a6d37508f3ec6b83711efefe1 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 27 Nov 2012 10:00:15 +0100 Subject: [PATCH 34/59] [FIX] web kanban: reset size-box bzr revid: chm@openerp.com-20121127090015-anw60k3u4tc49x5s --- addons/web_kanban/static/src/js/kanban.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 357fa049d5f..89142aeccb2 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -649,6 +649,7 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ }).then(function(records) { self.view.dataset.ids = self.view.dataset.ids.concat(ids); self.do_add_records(records); + self.compute_cards_auto_height(); return records; }); }, From 6f256d41dbffd50e0f99f31c61f0bbf51144f33e Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 27 Nov 2012 13:07:37 +0100 Subject: [PATCH 35/59] [FIX] account_analytic_contract, analytic_contract_hr_expense, hr_timesheet_invoice: fixed the computation of *to_invoice fields bzr revid: qdp-launchpad@openerp.com-20121127120737-5xeed6lobynqf10l --- .../account_analytic_analysis.py | 20 ++- .../analytic_contract_hr_expense.py | 51 +++--- .../hr_timesheet_invoice.py | 140 +++++++++++++++- .../wizard/hr_timesheet_invoice_create.py | 158 +----------------- 4 files changed, 174 insertions(+), 195 deletions(-) diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 06e2fd2bb18..466db63e61e 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -75,15 +75,21 @@ class account_analytic_account(osv.osv): res[id][f] = 0.0 res2 = {} for account in accounts: - cr.execute("SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id, name " \ - "FROM account_analytic_line as line " \ - "WHERE account_id = %s " \ - "AND invoice_id is NULL AND to_invoice IS NOT NULL " \ - "GROUP BY product_id, user_id, to_invoice, product_uom_id, name", (account.id,)) + cr.execute(""" + SELECT product_id, sum(amount), user_id, to_invoice, sum(unit_amount), product_uom_id, line.name + FROM account_analytic_line line + LEFT JOIN account_analytic_journal journal ON (journal.id = line.journal_id) + WHERE account_id = %s + AND journal.type != 'purchase' + AND invoice_id IS NULL + AND to_invoice IS NOT NULL + GROUP BY product_id, user_id, to_invoice, product_uom_id, line.name""", (account.id,)) res[account.id][f] = 0.0 - for product_id, user_id, factor_id, qty, uom, line_name in cr.fetchall(): - price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, qty, context) + for product_id, price, user_id, factor_id, qty, uom, line_name in cr.fetchall(): + price = -price + if product_id: + price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, qty, context) factor = self.pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context=context) res[account.id][f] += price * qty * (100-factor.factor or 0.0) / 100.0 diff --git a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py index 35177ee3a66..2b5584d4660 100644 --- a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py +++ b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py @@ -65,39 +65,26 @@ class account_analytic_account(osv.osv): def _expense_to_invoice_calc(self, cr, uid, ids, name, arg, context=None): res = {} - res_final = {} - child_ids = tuple(ids) #We don't want consolidation for each of these fields because those complex computation is resource-greedy. - for i in child_ids: - res[i] = 0.0 - if not child_ids: - return res + #We don't want consolidation for each of these fields because those complex computation is resource-greedy. + for account in self.pool.get('account.analytic.account').browse(cr, uid, ids, context=context): + cr.execute(""" + SELECT product_id, sum(amount), user_id, to_invoice, sum(unit_amount), product_uom_id, line.name + FROM account_analytic_line line + LEFT JOIN account_analytic_journal journal ON (journal.id = line.journal_id) + WHERE account_id = %s + AND journal.type = 'purchase' + AND invoice_id IS NULL + AND to_invoice IS NOT NULL + GROUP BY product_id, user_id, to_invoice, product_uom_id, line.name""", (account.id,)) - if child_ids: - cr.execute("""SELECT account_analytic_account.id, \ - COALESCE(SUM (product_template.list_price * \ - account_analytic_line.unit_amount * \ - ((100-hr_timesheet_invoice_factor.factor)/100)), 0.0) \ - AS ca_to_invoice \ - FROM product_template \ - JOIN product_product \ - ON product_template.id = product_product.product_tmpl_id \ - JOIN account_analytic_line \ - ON account_analytic_line.product_id = product_product.id \ - JOIN account_analytic_journal \ - ON account_analytic_line.journal_id = account_analytic_journal.id \ - JOIN account_analytic_account \ - ON account_analytic_account.id = account_analytic_line.account_id \ - JOIN hr_timesheet_invoice_factor \ - ON hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \ - WHERE account_analytic_account.id IN %s \ - AND account_analytic_line.invoice_id IS NULL \ - AND account_analytic_line.to_invoice IS NOT NULL \ - AND account_analytic_journal.type = 'purchase' \ - GROUP BY account_analytic_account.id;""",(child_ids,)) - for account_id, sum in cr.fetchall(): - res[account_id] = sum - res_final = res - return res_final + res[account.id] = 0.0 + for product_id, price, user_id, factor_id, qty, uom, line_name in cr.fetchall(): + price = -price + if product_id: + price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, qty, context) + factor = self.pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context=context) + res[account.id] += price * qty * (100 - factor.factor or 0.0) / 100.0 + return res def _expense_invoiced_calc(self, cr, uid, ids, name, arg, context=None): lines_obj = self.pool.get('account.analytic.line') diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index a24208d6ce0..be23faea804 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -19,8 +19,9 @@ # ############################################################################## -from osv import fields, osv +import time +from osv import fields, osv from tools.translate import _ class hr_timesheet_invoice_factor(osv.osv): @@ -172,6 +173,143 @@ class account_analytic_line(osv.osv): return super(account_analytic_line, self).copy(cursor, user, obj_id, default, context=context) + def _get_invoice_price(self, cr, uid, account, product_id, user_id, qty, context = {}): + pro_price_obj = self.pool.get('product.pricelist') + if account.pricelist_id: + pl = account.pricelist_id.id + price = pro_price_obj.price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id, context=context)[pl] + else: + price = 0.0 + return price + + def invoice_cost_create(self, cr, uid, ids, data=None, context=None): + analytic_account_obj = self.pool.get('account.analytic.account') + account_payment_term_obj = self.pool.get('account.payment.term') + invoice_obj = self.pool.get('account.invoice') + product_obj = self.pool.get('product.product') + invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor') + fiscal_pos_obj = self.pool.get('account.fiscal.position') + product_uom_obj = self.pool.get('product.uom') + invoice_line_obj = self.pool.get('account.invoice.line') + invoices = [] + if context is None: + context = {} + if data is None: + data = {} + + journal_types = {} + for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context): + if line.journal_id.type not in journal_types: + journal_types[line.journal_id.type] = set() + journal_types[line.journal_id.type].add(line.account_id.id) + for journal_type, account_ids in journal_types.items(): + for account in analytic_account_obj.browse(cr, uid, list(account_ids), context=context): + partner = account.partner_id + if (not partner) or not (account.pricelist_id): + raise osv.except_osv(_('Analytic Account incomplete !'), + _('Please fill in the Partner or Customer and Sale Pricelist fields in the Analytic Account:\n%s.') % (account.name,)) + + date_due = False + if partner.property_payment_term: + pterm_list= account_payment_term_obj.compute(cr, uid, + partner.property_payment_term.id, value=1, + date_ref=time.strftime('%Y-%m-%d')) + if pterm_list: + pterm_list = [line[0] for line in pterm_list] + pterm_list.sort() + date_due = pterm_list[-1] + + curr_invoice = { + 'name': time.strftime('%d/%m/%Y') + ' - '+account.name, + 'partner_id': account.partner_id.id, + 'company_id': account.company_id.id, + 'payment_term': partner.property_payment_term.id or False, + 'account_id': partner.property_account_receivable.id, + 'currency_id': account.pricelist_id.currency_id.id, + 'date_due': date_due, + 'fiscal_position': account.partner_id.property_account_position.id + } + + context2 = context.copy() + context2['lang'] = partner.lang + # set company_id in context, so the correct default journal will be selected + context2['force_company'] = curr_invoice['company_id'] + # set force_company in context so the correct product properties are selected (eg. income account) + context2['company_id'] = curr_invoice['company_id'] + + last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2) + invoices.append(last_invoice) + + cr.execute("""SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id + FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id) + WHERE account_id = %s + AND id IN %s AND journal.type IN %s AND to_invoice IS NOT NULL + GROUP BY product_id, user_id, to_invoice, product_uom_id""", (account.id, tuple(ids), journal_type)) + + for product_id, user_id, factor_id, qty, uom in cr.fetchall(): + if data.get('product'): + product_id = data['product'][0] + product = product_obj.browse(cr, uid, product_id, context=context2) + if not product: + raise osv.except_osv(_('Error!'), _('There is no product defined. Please select one or force the product through the wizard.')) + factor = invoice_factor_obj.browse(cr, uid, factor_id, context=context2) + factor_name = product_obj.name_get(cr, uid, [product_id], context=context2)[0][1] + if factor.customer_name: + factor_name += ' - ' + factor.customer_name + + ctx = context.copy() + ctx.update({'uom':uom}) + + price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, ctx) + + general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ + if not general_account: + raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name) + taxes = product.taxes_id or general_account.tax_ids + tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes) + curr_line = { + 'price_unit': price, + 'quantity': qty, + 'discount':factor.factor, + 'invoice_line_tax_id': [(6,0,tax )], + 'invoice_id': last_invoice, + 'name': factor_name, + 'product_id': product_id, + 'invoice_line_tax_id': [(6,0,tax)], + 'uos_id': uom, + 'account_id': general_account.id, + 'account_analytic_id': account.id, + } + + # + # Compute for lines + # + cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id)) + + line_ids = cr.dictfetchall() + note = [] + for line in line_ids: + # set invoice_line_note + details = [] + if data.get('date', False): + details.append(line['date']) + if data.get('time', False): + if line['product_uom_id']: + details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name)) + else: + details.append("%s" % (line['unit_amount'], )) + if data.get('name', False): + details.append(line['name']) + note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) + + if note: + curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note))) + invoice_line_obj.create(cr, uid, curr_line, context=context) + cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids))) + + invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context) + return invoices + account_analytic_line() diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py index 3fd7cca9b3b..0a8fd36f469 100644 --- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py +++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py @@ -19,163 +19,10 @@ # ############################################################################## -import time from osv import osv, fields from tools.translate import _ -## Create an invoice based on selected timesheet lines -# - -class account_analytic_line(osv.osv): - _inherit = "account.analytic.line" - def _get_invoice_price(self, cr, uid, account, product_id, user_id, qty, context = {}): - pro_price_obj = self.pool.get('product.pricelist') - if account.pricelist_id: - pl = account.pricelist_id.id - price = pro_price_obj.price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id, context=context)[pl] - else: - price = 0.0 - return price - - - # - # data = { - # 'date': boolean - # 'time': boolean - # 'name': boolean - # 'price': boolean - # 'product': many2one id - # } - def invoice_cost_create(self, cr, uid, ids, data=None, context=None): - analytic_account_obj = self.pool.get('account.analytic.account') - account_payment_term_obj = self.pool.get('account.payment.term') - invoice_obj = self.pool.get('account.invoice') - product_obj = self.pool.get('product.product') - invoice_factor_obj = self.pool.get('hr_timesheet_invoice.factor') - fiscal_pos_obj = self.pool.get('account.fiscal.position') - product_uom_obj = self.pool.get('product.uom') - invoice_line_obj = self.pool.get('account.invoice.line') - invoices = [] - if context is None: - context = {} - if data is None: - data = {} - - account_ids = [line.account_id.id for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context)] - for account in analytic_account_obj.browse(cr, uid, account_ids, context=context): - partner = account.partner_id - if (not partner) or not (account.pricelist_id): - raise osv.except_osv(_('Analytic Account incomplete !'), - _('Please fill in the Partner or Customer and Sale Pricelist fields in the Analytic Account:\n%s.') % (account.name,)) - - - - date_due = False - if partner.property_payment_term: - pterm_list= account_payment_term_obj.compute(cr, uid, - partner.property_payment_term.id, value=1, - date_ref=time.strftime('%Y-%m-%d')) - if pterm_list: - pterm_list = [line[0] for line in pterm_list] - pterm_list.sort() - date_due = pterm_list[-1] - - curr_invoice = { - 'name': time.strftime('%d/%m/%Y')+' - '+account.name, - 'partner_id': account.partner_id.id, - 'company_id': account.company_id.id, - 'payment_term': partner.property_payment_term.id or False, - 'account_id': partner.property_account_receivable.id, - 'currency_id': account.pricelist_id.currency_id.id, - 'date_due': date_due, - 'fiscal_position': account.partner_id.property_account_position.id - } - - context2 = context.copy() - context2['lang'] = partner.lang - # set company_id in context, so the correct default journal will be selected - context2['force_company'] = curr_invoice['company_id'] - # set force_company in context so the correct product properties are selected (eg. income account) - context2['company_id'] = curr_invoice['company_id'] - - last_invoice = invoice_obj.create(cr, uid, curr_invoice, context=context2) - invoices.append(last_invoice) - - cr.execute("SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id " \ - "FROM account_analytic_line as line " \ - "WHERE account_id = %s " \ - "AND id IN %s AND to_invoice IS NOT NULL " \ - "GROUP BY product_id, user_id, to_invoice, product_uom_id", (account.id, tuple(ids),)) - - for product_id, user_id, factor_id, qty, uom in cr.fetchall(): - if data.get('product'): - product_id = data['product'][0] - product = product_obj.browse(cr, uid, product_id, context=context2) - if not product: - raise osv.except_osv(_('Error!'), _('There is no product defined. Please select one or force the product through the wizard.')) - factor = invoice_factor_obj.browse(cr, uid, factor_id, context=context2) - factor_name = product_obj.name_get(cr, uid, [product_id], context=context2)[0][1] - if factor.customer_name: - factor_name += ' - ' + factor.customer_name - - ctx = context.copy() - ctx.update({'uom':uom}) - - price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, ctx) - - general_account = product.product_tmpl_id.property_account_income or product.categ_id.property_account_income_categ - if not general_account: - raise osv.except_osv(_("Configuration Error!"), _("Please define income account for product '%s'.") % product.name) - taxes = product.taxes_id or general_account.tax_ids - tax = fiscal_pos_obj.map_tax(cr, uid, account.partner_id.property_account_position, taxes) - curr_line = { - 'price_unit': price, - 'quantity': qty, - 'discount':factor.factor, - 'invoice_line_tax_id': [(6,0,tax )], - 'invoice_id': last_invoice, - 'name': factor_name, - 'product_id': product_id, - 'invoice_line_tax_id': [(6,0,tax)], - 'uos_id': uom, - 'account_id': general_account.id, - 'account_analytic_id': account.id, - } - - # - # Compute for lines - # - cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id)) - - line_ids = cr.dictfetchall() - note = [] - for line in line_ids: - # set invoice_line_note - details = [] - if data.get('date', False): - details.append(line['date']) - if data.get('time', False): - if line['product_uom_id']: - details.append("%s %s" % (line['unit_amount'], product_uom_obj.browse(cr, uid, [line['product_uom_id']],context2)[0].name)) - else: - details.append("%s" % (line['unit_amount'], )) - if data.get('name', False): - details.append(line['name']) - note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) - - if note: - curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note))) - invoice_line_obj.create(cr, uid, curr_line, context=context) - cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids))) - - invoice_obj.button_reset_taxes(cr, uid, [last_invoice], context) - return invoices - -# -# TODO: check unit of measure !!! -# - class hr_timesheet_invoice_create(osv.osv_memory): _name = 'hr.timesheet.invoice.create' @@ -189,8 +36,8 @@ class hr_timesheet_invoice_create(osv.osv_memory): } _defaults = { - 'date': lambda *args: 1, - 'name': lambda *args: 1 + 'date': 1, + 'name': 1, } def view_init(self, cr, uid, fields, context=None): @@ -210,6 +57,7 @@ class hr_timesheet_invoice_create(osv.osv_memory): def do_create(self, cr, uid, ids, context=None): data = self.read(cr, uid, ids, [], context=context)[0] + # Create an invoice based on selected timesheet lines invs = self.pool.get('account.analytic.line').invoice_cost_create(cr, uid, context['active_ids'], data, context=context) mod_obj = self.pool.get('ir.model.data') act_obj = self.pool.get('ir.actions.act_window') From 19b743f80f0287dd16d91429ba0c6a219648fdf1 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 27 Nov 2012 13:20:37 +0100 Subject: [PATCH 36/59] [FIX] account_voucher: fixed newly added constraint bzr revid: qdp-launchpad@openerp.com-20121127122037-zvm4rdl15h8vj326 --- addons/account_voucher/test/case5_suppl_usd_usd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account_voucher/test/case5_suppl_usd_usd.yml b/addons/account_voucher/test/case5_suppl_usd_usd.yml index 2829c9ed440..4caeb19c847 100644 --- a/addons/account_voucher/test/case5_suppl_usd_usd.yml +++ b/addons/account_voucher/test/case5_suppl_usd_usd.yml @@ -7,7 +7,7 @@ !record {model: account.account, id: account_cash_usd_id2}: currency_id: base.USD name: "cash account in usd" - code: "Xcash usd" + code: "Xcash usd2" type: 'liquidity' user_type: "account.data_account_type_cash" From 14c220cb9aadee863f6995e6bd93654d811a2873 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 27 Nov 2012 13:24:16 +0100 Subject: [PATCH 37/59] [FIX] account: passing context in action_cancel() method of account.invoice. Fixing the runbot bzr revid: qdp-launchpad@openerp.com-20121127122416-x9220fce3gbv2vr5 --- addons/account/account_invoice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index b0671e501ae..efd15fbe595 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1068,8 +1068,9 @@ class account_invoice(osv.osv): self.message_post(cr, uid, [inv_id], body=message, context=context) return True - def action_cancel(self, cr, uid, ids, *args): - context = {} # TODO: Use context from arguments + def action_cancel(self, cr, uid, ids, context=None): + if context is None: + context = {} account_move_obj = self.pool.get('account.move') invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids']) move_ids = [] # ones that we will need to remove From 77de5e8f72bb7f774952fd06050e163408eaaa3a Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 27 Nov 2012 13:50:22 +0100 Subject: [PATCH 38/59] [FIX] membership: passing context in action_cancel() method of account.invoice. Fixing the runbot bzr revid: qdp-launchpad@openerp.com-20121127125022-27bon20szhgyjtt9 --- addons/membership/membership.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/membership/membership.py b/addons/membership/membership.py index 12e1c89a04a..4ffd067d73e 100644 --- a/addons/membership/membership.py +++ b/addons/membership/membership.py @@ -483,16 +483,16 @@ class Invoice(osv.osv): '''Invoice''' _inherit = 'account.invoice' - def action_cancel(self, cr, uid, ids, *args): + def action_cancel(self, cr, uid, ids, context=None): '''Create a 'date_cancel' on the membership_line object''' member_line_obj = self.pool.get('membership.membership_line') today = time.strftime('%Y-%m-%d') - for invoice in self.browse(cr, uid, ids): + for invoice in self.browse(cr, uid, ids, context=context): mlines = member_line_obj.search(cr, uid, [('account_invoice_line', 'in', [l.id for l in invoice.invoice_line])]) member_line_obj.write(cr, uid, mlines, {'date_cancel': today}) - return super(Invoice, self).action_cancel(cr, uid, ids) + return super(Invoice, self).action_cancel(cr, uid, ids, context=context) Invoice() From 60f1d800a2f3a547279d78f82e992aa0b00ebcb9 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 27 Nov 2012 13:53:09 +0100 Subject: [PATCH 39/59] [FIX] analytic_contract_hr_expense: use the real cost when reinvoicing expenses bzr revid: qdp-launchpad@openerp.com-20121127125309-t3lp3abnzh2se0zr --- .../analytic_contract_hr_expense.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py index 2b5584d4660..ee92b469744 100644 --- a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py +++ b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py @@ -79,9 +79,8 @@ class account_analytic_account(osv.osv): res[account.id] = 0.0 for product_id, price, user_id, factor_id, qty, uom, line_name in cr.fetchall(): + #the amount to reinvoice is the real cost. We don't use the pricelist price = -price - if product_id: - price = self.pool.get('account.analytic.line')._get_invoice_price(cr, uid, account, product_id, user_id, qty, context) factor = self.pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context=context) res[account.id] += price * qty * (100 - factor.factor or 0.0) / 100.0 return res From b296312be9614f6eecec7020d0c2cc8d1575ccab Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 27 Nov 2012 15:43:38 +0100 Subject: [PATCH 40/59] [FIX] hr_timesheet_invoice: fixing the redbot bzr revid: qdp-launchpad@openerp.com-20121127144338-j2803m1x08xwr6v4 --- addons/hr_timesheet_invoice/hr_timesheet_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index be23faea804..0a3998623b6 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -243,7 +243,7 @@ class account_analytic_line(osv.osv): cr.execute("""SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id) WHERE account_id = %s - AND id IN %s AND journal.type IN %s AND to_invoice IS NOT NULL + AND id IN %s AND journal.type = %s AND to_invoice IS NOT NULL GROUP BY product_id, user_id, to_invoice, product_uom_id""", (account.id, tuple(ids), journal_type)) for product_id, user_id, factor_id, qty, uom in cr.fetchall(): From b194e225f11b1f23f63b58ff767efa6df95caf8a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 28 Nov 2012 11:47:16 +0100 Subject: [PATCH 41/59] [FIX] py_date should return a local date, not a UTC date bzr revid: xmo@openerp.com-20121128104716-w7zqiljfazj597pg --- addons/web/static/src/js/corelib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 6d7e3c39054..0a6f4ed3a29 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -990,7 +990,7 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, { }; date.today = new py.def(function () { var d = new Date(); - return new date(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()); + return new date(d.getFullYear(), d.getMonth() + 1, d.getDate()); }); datetime.time = new py.type(function time() { throw new Error('datetime.time not implemented'); From 71639ff39399cf5f2242c9b04499fe629767b3e8 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 28 Nov 2012 11:53:42 +0100 Subject: [PATCH 42/59] [IMP] auth_oauth: factor js method to make it reusable bzr revid: rco@openerp.com-20121128105342-2djfotkdvzpcbsgb --- addons/auth_oauth/static/src/js/auth_oauth.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/auth_oauth/static/src/js/auth_oauth.js b/addons/auth_oauth/static/src/js/auth_oauth.js index fdf7baf607c..c24dab3d55a 100644 --- a/addons/auth_oauth/static/src/js/auth_oauth.js +++ b/addons/auth_oauth/static/src/js/auth_oauth.js @@ -36,6 +36,9 @@ openerp.auth_oauth = function(instance) { ev.preventDefault(); var index = $(ev.target).data('index'); var provider = this.oauth_providers[index]; + return this.do_oauth_sign_in(provider); + }, + do_oauth_sign_in: function(provider) { var return_url = _.str.sprintf('%s//%s/auth_oauth/signin', location.protocol, location.host); if (instance.session.debug) { return_url += '?debug'; From cb32b7e76fbf59049654cbff7ad87c3bc5a881cb Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 28 Nov 2012 11:55:20 +0100 Subject: [PATCH 43/59] [IMP] auth_reset_password: when sending email, return error message when user has no email address bzr revid: rco@openerp.com-20121128105520-0dpug00newn3rpbm --- addons/auth_reset_password/res_users.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/auth_reset_password/res_users.py b/addons/auth_reset_password/res_users.py index b1039031812..cfac20c76f9 100644 --- a/addons/auth_reset_password/res_users.py +++ b/addons/auth_reset_password/res_users.py @@ -21,6 +21,7 @@ from openerp.osv import osv, fields from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools.translate import _ from datetime import datetime, timedelta @@ -54,6 +55,8 @@ class res_users(osv.osv): template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_reset_password', 'reset_password_email') assert template._name == 'email.template' for user in self.browse(cr, uid, ids, context): + if not user.email: + raise osv.except_osv(_("Cannot send email: user has no email address."), user.name) self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, context=context) return True From 8dbdbf713f998f6bc3b96c1e5cd838185d6b56d1 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 28 Nov 2012 12:12:52 +0100 Subject: [PATCH 44/59] [FIX] account: pay_and_reconcile() function fixed as per new behavior (accounting entries are created for the parent company if needed) bzr revid: qdp-launchpad@openerp.com-20121128111252-1m37zos11iwg9xyr --- addons/account/account_invoice.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 5a599759f7d..6804d5116f1 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -1245,12 +1245,15 @@ class account_invoice(osv.osv): ref = invoice.reference else: ref = self._convert_ref(cr, uid, invoice.number) + partner = invoice.partner_id + if partner.parent_id and not partner.is_company: + partner = partner.parent_id # Pay attention to the sign for both debit/credit AND amount_currency l1 = { 'debit': direction * pay_amount>0 and direction * pay_amount, 'credit': direction * pay_amount<0 and - direction * pay_amount, 'account_id': src_account_id, - 'partner_id': invoice.partner_id.id, + 'partner_id': partner.id, 'ref':ref, 'date': date, 'currency_id':currency_id, @@ -1261,7 +1264,7 @@ class account_invoice(osv.osv): 'debit': direction * pay_amount<0 and - direction * pay_amount, 'credit': direction * pay_amount>0 and direction * pay_amount, 'account_id': pay_account_id, - 'partner_id': invoice.partner_id.id, + 'partner_id': partner.id, 'ref':ref, 'date': date, 'currency_id':currency_id, From b59aa36e2e28e9830fb25f866281d163d25b7784 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 28 Nov 2012 12:16:54 +0100 Subject: [PATCH 45/59] [FIX] purchase: fixed 'print quotation' report and removed reference to unexisting field 'notes' of purchase.order.line bzr revid: qdp-launchpad@openerp.com-20121128111654-iy9b7wbq14z5zjhg --- addons/purchase/report/request_quotation.rml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/addons/purchase/report/request_quotation.rml b/addons/purchase/report/request_quotation.rml index 495c8790e7b..c1500c48710 100644 --- a/addons/purchase/report/request_quotation.rml +++ b/addons/purchase/report/request_quotation.rml @@ -149,11 +149,6 @@ [[ (order_line.product_uom and order_line.product_uom.name) or '' ]] - - - [[ format(order_line.notes or removeParentNode('tr')) ]] - - From 21baf8dfac5fd73c32748f15167a3c863e477a13 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 28 Nov 2012 12:26:45 +0100 Subject: [PATCH 46/59] [FIX] hr_timesheet_invoice: fixed ambigous reference in sql query. Fixed the redbot bzr revid: qdp-launchpad@openerp.com-20121128112645-5r42r5tajeq0fzs8 --- addons/hr_timesheet_invoice/hr_timesheet_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index 0a3998623b6..af5a755e81d 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -243,7 +243,7 @@ class account_analytic_line(osv.osv): cr.execute("""SELECT product_id, user_id, to_invoice, sum(unit_amount), product_uom_id FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id) WHERE account_id = %s - AND id IN %s AND journal.type = %s AND to_invoice IS NOT NULL + AND line.id IN %s AND journal.type = %s AND to_invoice IS NOT NULL GROUP BY product_id, user_id, to_invoice, product_uom_id""", (account.id, tuple(ids), journal_type)) for product_id, user_id, factor_id, qty, uom in cr.fetchall(): From 0cf6aaf67a0992d8f7dbbc58d5e0352e4dcbe154 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 28 Nov 2012 12:41:46 +0100 Subject: [PATCH 47/59] [FIX] purchase: fixing the redbot bzr revid: qdp-launchpad@openerp.com-20121128114146-jqzn3wv7vlsipb07 --- addons/purchase/report/request_quotation.rml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/addons/purchase/report/request_quotation.rml b/addons/purchase/report/request_quotation.rml index 495c8790e7b..c1500c48710 100644 --- a/addons/purchase/report/request_quotation.rml +++ b/addons/purchase/report/request_quotation.rml @@ -149,11 +149,6 @@ [[ (order_line.product_uom and order_line.product_uom.name) or '' ]] - - - [[ format(order_line.notes or removeParentNode('tr')) ]] - - From 9784c7dfa5dd0fa4ccbb8c84bdf1b46dcf218af9 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 28 Nov 2012 12:54:17 +0100 Subject: [PATCH 48/59] [REM] Removed manual many2many tags from kanban views bzr revid: fme@openerp.com-20121128115417-sgpf3hr4rgwglrbm --- addons/fleet/fleet_view.xml | 7 +--- addons/hr_recruitment/__openerp__.py | 1 - addons/hr_recruitment/hr_recruitment_view.xml | 4 +-- .../static/src/js/hr_recruitment.js | 35 ------------------- .../portal_project_issue_view.xml | 7 +--- addons/project/project_view.xml | 6 +--- addons/project/static/src/js/project.js | 29 --------------- addons/project_issue/__openerp__.py | 1 - addons/project_issue/project_issue_view.xml | 4 +-- .../static/src/js/project_issue.js | 13 ------- 10 files changed, 5 insertions(+), 102 deletions(-) delete mode 100644 addons/hr_recruitment/static/src/js/hr_recruitment.js delete mode 100644 addons/project_issue/static/src/js/project_issue.js diff --git a/addons/fleet/fleet_view.xml b/addons/fleet/fleet_view.xml index 8c7ceaf8d2d..416ca191a6e 100644 --- a/addons/fleet/fleet_view.xml +++ b/addons/fleet/fleet_view.xml @@ -318,12 +318,7 @@ -
- -
- -
- +
diff --git a/addons/hr_recruitment/__openerp__.py b/addons/hr_recruitment/__openerp__.py index 63c1cf47ccb..f2ba3d55057 100644 --- a/addons/hr_recruitment/__openerp__.py +++ b/addons/hr_recruitment/__openerp__.py @@ -60,7 +60,6 @@ You can define the different phases of interviews and easily rate the applicant 'hr_recruitment_data.xml' ], 'demo': ['hr_recruitment_demo.yml'], - 'js': ['static/src/js/hr_recruitment.js'], 'test': ['test/recruitment_process.yml'], 'installable': True, 'auto_install': False, diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index c3281b43b94..5622e4bfa6f 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -314,9 +314,7 @@
diff --git a/addons/hr_recruitment/static/src/js/hr_recruitment.js b/addons/hr_recruitment/static/src/js/hr_recruitment.js deleted file mode 100644 index d11326ac9de..00000000000 --- a/addons/hr_recruitment/static/src/js/hr_recruitment.js +++ /dev/null @@ -1,35 +0,0 @@ -openerp.hr_recruitment = function(openerp) { - openerp.web_kanban.KanbanView.include({ - applicant_display_categ_names: function() { - /* - * Set proper names to applicant categories. - * In kanban views, many2many fields only return a list of ids. - * Therefore, we have to fetch the matching data by ourselves. - */ - var self = this; - var categ_ids = []; - - // Collect categories ids - self.$el.find('span[data-categ_id]').each(function() { - categ_ids.push($(this).data('categ_id')); - }); - - // Find their matching names - var dataset = new openerp.web.DataSetSearch(self, 'hr.applicant_category', self.session.context, [['id', 'in', _.uniq(categ_ids)]]); - dataset.read_slice(['id', 'name']).done(function(result) { - _.each(result, function(v, k) { - // Set the proper value in the DOM and display the element - self.$el.find('span[data-categ_id=' + v.id + ']').text(v.name); - }); - }); - }, - on_groups_started: function() { - var self = this; - self._super.apply(self, arguments); - - if (self.dataset.model === 'hr.applicant') { - self.applicant_display_categ_names(); - } - } - }); -}; diff --git a/addons/portal_project_issue/portal_project_issue_view.xml b/addons/portal_project_issue/portal_project_issue_view.xml index 6502d92b4dc..c943bb3b825 100644 --- a/addons/portal_project_issue/portal_project_issue_view.xml +++ b/addons/portal_project_issue/portal_project_issue_view.xml @@ -24,12 +24,7 @@
diff --git a/addons/project/static/src/js/project.js b/addons/project/static/src/js/project.js index 38578a6226e..ce8be146e54 100644 --- a/addons/project/static/src/js/project.js +++ b/addons/project/static/src/js/project.js @@ -25,37 +25,12 @@ openerp.project = function(openerp) { }); }); }, - project_display_categ_names: function() { - /* - * Set proper names to project categories. - * In kanban views, many2many fields only return a list of ids. - * Therefore, we have to fetch the matching data by ourselves. - */ - var self = this; - var categ_ids = []; - - // Collect categories ids - self.$el.find('span[data-categ_id]').each(function() { - categ_ids.push($(this).data('categ_id')); - }); - - // Find their matching names - var dataset = new openerp.web.DataSetSearch(self, 'project.category', self.session.context, [['id', 'in', _.uniq(categ_ids)]]); - dataset.read_slice(['id', 'name']).done(function(result) { - _.each(result, function(v, k) { - // Set the proper value in the DOM and display the element - self.$el.find('span[data-categ_id=' + v.id + ']').text(v.name); - }); - }); - }, on_groups_started: function() { var self = this; self._super.apply(self, arguments); if (self.dataset.model === 'project.project') { self.project_display_members_names(); - } else if (self.dataset.model === 'project.task') { - self.project_display_categ_names(); } }, on_record_moved: function(record, old_group, old_index, new_group, new_index){ @@ -74,9 +49,5 @@ openerp.project = function(openerp) { this._super.apply(this, arguments); } }, - bind_events: function() { - this._super(); - this.view.project_display_categ_names(); - }, }); }; diff --git a/addons/project_issue/__openerp__.py b/addons/project_issue/__openerp__.py index b2e9197cefd..2c5f311c044 100644 --- a/addons/project_issue/__openerp__.py +++ b/addons/project_issue/__openerp__.py @@ -61,7 +61,6 @@ It allows the manager to quickly check the issues, assign them and decide on the 'installable': True, 'auto_install': False, 'application': True, - 'js': ['static/src/js/project_issue.js'], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index e3105127c5d..f98fadbbb48 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -264,9 +264,7 @@