From 1040ce923192451900bfe75a101eac3f8ca799da Mon Sep 17 00:00:00 2001 From: Olivier Colson Date: Tue, 5 Jun 2018 16:47:50 +0200 Subject: [PATCH 01/11] [FIX] account: dashboard: display latest statement balance for bank and cash journals only if it is different from the balance in GL. The condition to do that was already present, but it wasn't evaluated; the t-if needed to be oustide of the div. --- .../views/account_journal_dashboard_view.xml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/addons/account/views/account_journal_dashboard_view.xml b/addons/account/views/account_journal_dashboard_view.xml index ad4f5a1c96c..d8122815d8c 100644 --- a/addons/account/views/account_journal_dashboard_view.xml +++ b/addons/account/views/account_journal_dashboard_view.xml @@ -240,14 +240,16 @@ -
-
- Latest Statement + +
+
+ Latest Statement +
+
+ +
-
- -
-
+
From 367fadc8d51f6169e2931270730357fdaed81986 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Tue, 5 Jun 2018 14:39:10 +0200 Subject: [PATCH 02/11] [FIX] account: Currency rate conversion issue in vendor bill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce: - Enable multi currencies - Make sure that the current rate of a foreign currency (i.e. $) is not 1.0 (i.e. 0.5) - Create a purchasable product with a cost (i.e. 100€) - Create a vendor bill in a foreign currency (i.e. $) - Add the product on the invoice Bug: The unit price was equal to 25$ instead of 50$ Technical reason: The function "_onchange_product_id" was called twice (the second time by function "_onchange_uom_id") and the price unit was converted twice in the currency of the vendor bill. The idea of this fix is to only convert in the currency when the unit price is set by the system. Fixes #24751 opw:1849212 --- addons/account/models/account_invoice.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index a22913be9d1..a42da9eab0a 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1169,6 +1169,13 @@ class AccountInvoiceLine(models.Model): return accounts['income'] return accounts['expense'] + def _set_currency(self): + company = self.invoice_id.company_id + currency = self.invoice_id.currency_id + if company and currency: + if company.currency_id != currency: + self.price_unit = self.price_unit * currency.with_context(dict(self._context or {}, date=self.invoice_id.date_invoice)).rate + def _set_taxes(self): """ Used in on_change to set taxes and price.""" if self.invoice_id.type in ('out_invoice', 'out_refund'): @@ -1187,8 +1194,10 @@ class AccountInvoiceLine(models.Model): prec = self.env['decimal.precision'].precision_get('Product Price') if not self.price_unit or float_compare(self.price_unit, self.product_id.standard_price, precision_digits=prec) == 0: self.price_unit = fix_price(self.product_id.standard_price, taxes, fp_taxes) + self._set_currency() else: self.price_unit = fix_price(self.product_id.lst_price, taxes, fp_taxes) + self._set_currency() @api.onchange('product_id') def _onchange_product_id(self): @@ -1237,8 +1246,6 @@ class AccountInvoiceLine(models.Model): domain['uom_id'] = [('category_id', '=', product.uom_id.category_id.id)] if company and currency: - if company.currency_id != currency: - self.price_unit = self.price_unit * currency.with_context(dict(self._context or {}, date=self.invoice_id.date_invoice)).rate if self.uom_id and self.uom_id.id != product.uom_id.id: self.price_unit = self.env['product.uom']._compute_price( From ea23a1ac8c6c68520f4f02a2f35ef7ab474b868a Mon Sep 17 00:00:00 2001 From: Adrian Torres Date: Wed, 6 Jun 2018 10:00:54 +0200 Subject: [PATCH 03/11] [FIX] orm: re-create constraints of extended fields Before this commit: * Module A defines a field X of model M * Module B inherits from model M without touching field X * Module C inherits from model M and extends field X by giving an INDEX / NOT NULL constraint. * Module B and C depend from Module A, but not each other If all three modules are installed and Module B is updated, the INDEX / NOT NULL constraint could be dropped. This happens because Module B can be loaded before Module C is loaded, if that's the case, then after the upgrade of Module B, during the schema checking, we verify that the field object we have and the field on the DB are the same, since Module B doesn't introduce the index then this check is false and we drop the index. When we get to loading Module C, we do not do any schema checking because the module is not marked as `to upgrade`, therefore the index is lost forever. To solve this, we re-init the models that belong to the set of the intersection between upgraded and modified models and loaded and modified models. Fixes #24958 --- openerp/modules/loading.py | 55 +++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index be94697ad1d..82980e2a252 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -29,7 +29,8 @@ _logger = logging.getLogger(__name__) _test_logger = logging.getLogger('openerp.tests') -def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None): +def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None, + models_to_check=None): """Migrates+Updates or Installs all module nodes from ``graph`` :param graph: graph of module nodes to load :param status: deprecated parameter, unused, left to avoid changing signature in 8.0 @@ -100,6 +101,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= if kind in ('demo', 'test'): threading.currentThread().testing = False + if models_to_check is None: + models_to_check = set() + processed_modules = [] loaded_modules = [] registry = openerp.registry(cr.dbname) @@ -112,6 +116,8 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= t0 = time.time() t0_sql = openerp.sql_db.sql_counter + models_updated = set() + for index, package in enumerate(graph): module_name = package.name module_id = package.id @@ -130,11 +136,22 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= getattr(py_module, pre_init)(cr) models = registry.load(cr, package) + model_names = [m._name for m in models] loaded_modules.append(package.name) - if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): + if (hasattr(package, 'init') or hasattr(package, 'update') + or package.state in ('to install', 'to upgrade')): + models_updated |= set(model_names) + models_to_check -= set(model_names) registry.setup_models(cr, partial=True) init_module_models(cr, package.name, models) + elif package.state != 'to remove': + # The current module has simply been loaded. The models extended by this module + # and for which we updated the schema, must have their schema checked again. + # This is because the extension may have changed the model, + # e.g. adding required=True to an existing field, but the schema has not been + # updated by this module because it's not marked as 'to upgrade/to install'. + models_to_check |= set(model_names) & models_updated idref = {} @@ -225,9 +242,14 @@ def _check_module_names(cr, module_names): incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()]) _logger.warning('invalid module names, ignored: %s', ", ".join(incorrect_names)) -def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, perform_checks): +def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules, + perform_checks, models_to_check=None): """Loads modules marked with ``states``, adding them to ``graph`` and ``loaded_modules`` and returns a list of installed/upgraded modules.""" + + if models_to_check is None: + models_to_check = set() + processed_modules = [] while True: cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),)) @@ -236,7 +258,10 @@ def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_m break graph.add_modules(cr, module_list, force) _logger.debug('Updating graph with %d more modules', len(module_list)) - loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, perform_checks=perform_checks) + loaded, processed = load_module_graph( + cr, graph, progressdict, report=report, skip_modules=loaded_modules, + perform_checks=perform_checks, models_to_check=models_to_check + ) processed_modules.extend(processed) loaded_modules.extend(loaded) if not processed: @@ -250,6 +275,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False): if force_demo: force.append('demo') + models_to_check = set() + cr = db.cursor() try: if not openerp.modules.db.is_initialized(cr): @@ -278,7 +305,10 @@ 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 report = registry._assertion_report - loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report) + loaded_modules, processed_modules = load_module_graph( + cr, graph, status, perform_checks=update_module, + report=report, models_to_check=models_to_check + ) load_lang = tools.config.pop('load_language') if load_lang or update_module: @@ -333,11 +363,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False): previously_processed = len(processed_modules) processed_modules += load_marked_modules(cr, graph, ['installed', 'to upgrade', 'to remove'], - force, status, report, loaded_modules, update_module) + force, status, report, loaded_modules, update_module, models_to_check) if update_module: processed_modules += load_marked_modules(cr, graph, ['to install'], force, status, report, - loaded_modules, update_module) + loaded_modules, update_module, models_to_check) registry.setup_models(cr) @@ -398,6 +428,17 @@ def load_modules(db, force_demo=False, status=None, update_module=False): openerp.api.Environment.reset() return openerp.modules.registry.RegistryManager.new(cr.dbname, force_demo, status, update_module) + # STEP 5.5: Verify extended fields on every model + # This will fix the schema of all models in a situation such as: + # - module A is loaded and defines model M; + # - module B is installed/upgraded and extends model M; + # - module C is loaded and extends model M; + # - module B and C depend on A but not on each other; + # The changes introduced by module C are not taken into account by the upgrade of B. + if models_to_check: + module_objs_to_check = [registry.models[m] for m in models_to_check] + init_module_models(cr, 'verification in progress', module_objs_to_check) + # STEP 6: verify custom views on every model if update_module: Views = registry['ir.ui.view'] From a52c545d12039617432793f5ce8d8f4a4044b6c8 Mon Sep 17 00:00:00 2001 From: Olivier-LAURENT Date: Wed, 23 May 2018 15:56:36 +0200 Subject: [PATCH 04/11] [FIX] base: timeout on smtp connections Currently when connecting to a smtp server, if no response is returned and no error is raised, the `SMTP()` instantiation method never ends. This PR is inspired by 54a477d7971e8bff08b related to `fetchmail()`. Possibly, a better way exists but it has to change some methods signatures. Closes #24877 opw-1851030 --- openerp/addons/base/ir/ir_mail_server.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_mail_server.py b/openerp/addons/base/ir/ir_mail_server.py index b5fb772a851..50d96e9710d 100644 --- a/openerp/addons/base/ir/ir_mail_server.py +++ b/openerp/addons/base/ir/ir_mail_server.py @@ -28,6 +28,8 @@ from openerp.loglevels import ustr _logger = logging.getLogger(__name__) _test_logger = logging.getLogger('openerp.tests') +SMTP_TIMEOUT = 60 + class MailDeliveryException(except_orm): """Specific exception subclass for mail delivery errors""" @@ -215,9 +217,9 @@ class ir_mail_server(osv.osv): if not 'SMTP_SSL' in smtplib.__all__: raise UserError(_("Your OpenERP Server does not support SMTP-over-SSL. You could use STARTTLS instead." "If SSL is needed, an upgrade to Python 2.6 on the server-side should do the trick.")) - connection = smtplib.SMTP_SSL(host, port) + connection = smtplib.SMTP_SSL(host, port, timeout=SMTP_TIMEOUT) else: - connection = smtplib.SMTP(host, port) + connection = smtplib.SMTP(host, port, timeout=SMTP_TIMEOUT) connection.set_debuglevel(smtp_debug) if encryption == 'starttls': # starttls() will perform ehlo() if needed first From 4f2442c4f6f9cfc2b694b3a4fc7b5c01a48a1cd5 Mon Sep 17 00:00:00 2001 From: Toufik Benjaa Date: Mon, 14 May 2018 17:10:27 +0200 Subject: [PATCH 05/11] [FIX] crm: Access rights issues in contact merging - When merging partners with fields restricted to certain groups which the user doesn't belong, an access error is raised. To avoid this, we only merge fields that are accessible by the user. We do so by using the method fields_get() that only returns the fields accessible by the current user. --- addons/crm/base_partner_merge.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/crm/base_partner_merge.py b/addons/crm/base_partner_merge.py index 59ffb1845ea..86b05f6fe10 100644 --- a/addons/crm/base_partner_merge.py +++ b/addons/crm/base_partner_merge.py @@ -267,8 +267,7 @@ class MergePartnerAutomatic(osv.TransientModel): def _update_values(self, cr, uid, src_partners, dst_partner, context=None): _logger.debug('_update_values for dst_partner: %s for src_partners: %r', dst_partner.id, list(map(operator.attrgetter('id'), src_partners))) - - columns = dst_partner._columns + columns = dst_partner.fields_get().keys() def write_serializer(column, item): if isinstance(item, browse_record): return item.id @@ -276,8 +275,9 @@ class MergePartnerAutomatic(osv.TransientModel): return item values = dict() - for column, field in columns.iteritems(): - if field._type not in ('many2many', 'one2many') and not isinstance(field, fields.function): + for column in columns: + field = dst_partner._fields[column] + if field.type not in ('many2many', 'one2many') and field.compute is None: for item in itertools.chain(src_partners, [dst_partner]): if item[column]: values[column] = write_serializer(column, item[column]) From 64645457dd3cc834970a6892bc32a29ed281e8c8 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 5 Jun 2018 19:05:58 +0200 Subject: [PATCH 06/11] [FIX] base: Allow to create a contact without type If you don't define an explicit address type, then the record is not going to be accessible. In the ORM: >>> env['res.partner'].create({'name': 'Test', 'type': False}) res.partner(44356,) >>> env['res.partner'].search([('type', '!=', 'private'), ('id', '=', 44356)]) res.partner() >>> env['res.partner'].search([('type', '=', False), ('id', '=', 44356)]) res.partner(44356,) due to the fact that NULL in the database is undefined and can't be compared to a specific key. --- openerp/addons/base/security/base_security.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/security/base_security.xml b/openerp/addons/base/security/base_security.xml index aab7d358f8d..04ad8b35258 100644 --- a/openerp/addons/base/security/base_security.xml +++ b/openerp/addons/base/security/base_security.xml @@ -162,7 +162,7 @@ res.partner.rule.private.employee - [('type', '!=', 'private')] + ['|', ('type', '!=', 'private'), ('type', '=', False)] Date: Tue, 6 Feb 2018 08:13:23 +0100 Subject: [PATCH 08/11] [CLA] backport of WT-IO-IT GmbH signature backport to 9.0 as asked at #25185 --- doc/cla/corporate/wt-io-it.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 doc/cla/corporate/wt-io-it.md diff --git a/doc/cla/corporate/wt-io-it.md b/doc/cla/corporate/wt-io-it.md new file mode 100644 index 00000000000..c9bbebc2445 --- /dev/null +++ b/doc/cla/corporate/wt-io-it.md @@ -0,0 +1,16 @@ +Austria, 2018-02-06 + +WT-IO-IT GmbH agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Wolfgang Taferner wolfgang.taferner@wt-io-it.at https://github.com/wt-io-it + +List of contributors: + +Wolfgang Taferner wolfgang.taferner@wt-io-it.at https://github.com/wtaferner +Andreas Perhab andreas.perhab@wt-io-it.at https://github.com/ap-wtioit From 05e914629b2782b500106a04d4c241c2ea88840e Mon Sep 17 00:00:00 2001 From: RomainLibert Date: Mon, 11 Jun 2018 17:10:50 +0200 Subject: [PATCH 09/11] [FIX] event: fix rights for event.registration --- addons/event/security/ir.model.access.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/event/security/ir.model.access.csv b/addons/event/security/ir.model.access.csv index 35f82422352..a232fd9e74b 100644 --- a/addons/event/security/ir.model.access.csv +++ b/addons/event/security/ir.model.access.csv @@ -5,7 +5,7 @@ access_event_event,event.event,model_event_event,event.group_event_user,1,1,1,1 access_event_registration,event.registration,model_event_registration,event.group_event_user,1,1,1,1 access_report_event_registration,report.event.registration,model_report_event_registration,event.group_event_user,1,1,1,1 access_event_event_portal,event.event,model_event_event,,1,0,0,0 -access_event_registration_portal,event.registration,model_event_registration,,0,0,0,0 +access_event_registration_portal,event.registration,model_event_registration,,1,0,0,0 access_event_mail,event.mail,model_event_mail,event.group_event_user,1,0,0,0 access_event_mail_manager,event.mail manager,model_event_mail,event.group_event_manager,1,1,1,1 access_event_mail_registration,event.mail.registration,model_event_mail_registration,event.group_event_user,1,0,0,0 From e44cb0ae343758ec03999d81e23a147d5451d458 Mon Sep 17 00:00:00 2001 From: Invitu Date: Fri, 8 Jan 2016 14:45:01 -1000 Subject: [PATCH 10/11] [FIX] hr_attendance: Correctly compute worked hours Fixes #10381 (wrong calculation on datetime) if worked hours > 24h, calculation is wrong because "seconds" funtion does not take into account days See: https://docs.python.org/2.4/lib/datetime-timedelta.html total_seconds() should be used instead of seconds function --- addons/hr_attendance/hr_attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_attendance/hr_attendance.py b/addons/hr_attendance/hr_attendance.py index cd8bae0e1a1..9be9fa16c5c 100644 --- a/addons/hr_attendance/hr_attendance.py +++ b/addons/hr_attendance/hr_attendance.py @@ -51,7 +51,7 @@ class hr_attendance(osv.osv): last_signin_datetime = datetime.strptime(last_signin.name, '%Y-%m-%d %H:%M:%S') signout_datetime = datetime.strptime(obj.name, '%Y-%m-%d %H:%M:%S') workedhours_datetime = (signout_datetime - last_signin_datetime) - res[obj.id] = ((workedhours_datetime.seconds) / 60) / 60.0 + res[obj.id] = ((workedhours_datetime.total_seconds()) / 60) / 60.0 else: res[obj.id] = False return res From 06a085b25fa0c97fd9a44849076b87df07533066 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Mon, 11 Jun 2018 17:15:50 +0200 Subject: [PATCH 11/11] Revert "[FIX] event: fix rights for event.registration" This reverts commit 05e914629b2782b500106a04d4c241c2ea88840e while waiting a clear commit message to justify an access right modification in a stable release. --- addons/event/security/ir.model.access.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/event/security/ir.model.access.csv b/addons/event/security/ir.model.access.csv index a232fd9e74b..35f82422352 100644 --- a/addons/event/security/ir.model.access.csv +++ b/addons/event/security/ir.model.access.csv @@ -5,7 +5,7 @@ access_event_event,event.event,model_event_event,event.group_event_user,1,1,1,1 access_event_registration,event.registration,model_event_registration,event.group_event_user,1,1,1,1 access_report_event_registration,report.event.registration,model_report_event_registration,event.group_event_user,1,1,1,1 access_event_event_portal,event.event,model_event_event,,1,0,0,0 -access_event_registration_portal,event.registration,model_event_registration,,1,0,0,0 +access_event_registration_portal,event.registration,model_event_registration,,0,0,0,0 access_event_mail,event.mail,model_event_mail,event.group_event_user,1,0,0,0 access_event_mail_manager,event.mail manager,model_event_mail,event.group_event_manager,1,1,1,1 access_event_mail_registration,event.mail.registration,model_event_mail_registration,event.group_event_user,1,0,0,0