From 80f97fbd19d3ec24b8dbb949809f2d81460c08b0 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Fri, 2 Mar 2018 13:17:46 +0100 Subject: [PATCH 01/10] [FIX] website_slides: load viewer for app's iframes only The website_slides JS was working on all iframe in all pages while it should only affect its own ones, which contains a PDF document. --- addons/website_slides/models/slides.py | 2 +- addons/website_slides/static/src/js/slides.js | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/website_slides/models/slides.py b/addons/website_slides/models/slides.py index b4eb7be8036..c1eea51f6ab 100644 --- a/addons/website_slides/models/slides.py +++ b/addons/website_slides/models/slides.py @@ -322,7 +322,7 @@ class Slide(models.Model): base_url = self.env['ir.config_parameter'].get_param('web.base.url') for record in self: if record.datas and (not record.document_id or record.slide_type in ['document', 'presentation']): - record.embed_code = '' % (base_url, record.id, 315, 420) + record.embed_code = '' % (base_url, record.id, 315, 420) elif record.slide_type == 'video' and record.document_id: if not record.mime_type: # embed youtube video diff --git a/addons/website_slides/static/src/js/slides.js b/addons/website_slides/static/src/js/slides.js index 71d20378044..ab7da4a937f 100644 --- a/addons/website_slides/static/src/js/slides.js +++ b/addons/website_slides/static/src/js/slides.js @@ -108,11 +108,12 @@ $(document).ready(function () { }, }); - $('iframe').ready(function() { + $('iframe.o_wslides_iframe_viewer').ready(function() { // TODO : make it work. For now, once the iframe is loaded, the value of #page_count is // still now set (the pdf is still loading) - var max_page = $('iframe').contents().find('#page_count').val(); - new SlideSocialEmbed($(this), max_page).setElement($('.oe_slide_js_embed_code_widget')); + var $iframe = $(this); + var max_page = $iframe.contents().find('#page_count').val(); + new SlideSocialEmbed($iframe, max_page).setElement($('.oe_slide_js_embed_code_widget')); }); From e710e5df7a88b601f117c84d62930486b1d58415 Mon Sep 17 00:00:00 2001 From: len-odoo Date: Mon, 5 Mar 2018 11:51:19 +0100 Subject: [PATCH 02/10] [FIX] web: show error when trying to export a report with more than 256 columns If the user tried to generate a report with more than 256 columns, he would get a traceback instead of an error message. Furthermore, if only one measure was selected, then the check was not done and the user would also get a traceback. opw-1816931 --- addons/web/static/src/js/views/pivot_view.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/views/pivot_view.js b/addons/web/static/src/js/views/pivot_view.js index c874a87665f..40b18fff86c 100644 --- a/addons/web/static/src/js/views/pivot_view.js +++ b/addons/web/static/src/js/views/pivot_view.js @@ -803,7 +803,7 @@ var PivotView = View.extend({ framework.blockUI(); var nbr_measures = this.active_measures.length, headers = this.compute_headers(), - measure_row = nbr_measures > 1 ? _.last(headers) : [], + measure_row = nbr_measures >= 1 ? _.last(headers) : [], rows = this.compute_rows(), i, j, value; headers[0].splice(0,1); @@ -829,7 +829,8 @@ var PivotView = View.extend({ title: this.title, }; if(table.measure_row.length + 1 > 256) { - c.show_message(_t("For Excel compatibility, data cannot be exported if there are more than 256 columns.\n\nTip: try to flip axis, filter further or reduce the number of measures.")); + crash_manager.show_message(_t("For Excel compatibility, data cannot be exported if there are more than 256 columns.\n\nTip: try to flip axis, filter further or reduce the number of measures.")); + framework.unblockUI(); return; } session.get_file({ From 0e881fcd3a5671c4fe84b2a47c9d3611e4b823a1 Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Mon, 5 Mar 2018 14:50:22 +0100 Subject: [PATCH 03/10] [FIX] account: forbid the change of currency_id if the accounting app has already been used --- addons/account/i18n/account.pot | 6 ++++++ addons/account/models/company.py | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index 70464967077..4a1fdb752fa 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -8812,6 +8812,12 @@ msgstr "" msgid "You cannot cancel an invoice which is partially paid. You need to unreconcile related payment entries first." msgstr "" +#. module: account +#: code:addons/account/models/company.py:118 +#, python-format +msgid "You cannot change the currency of the company since some journal items already exist" +msgstr "" + #. module: account #: code:addons/account/models/account.py:162 #, python-format diff --git a/addons/account/models/company.py b/addons/account/models/company.py index 13591c254de..5eb3e27e8b3 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from openerp import fields, models, api, _ -from openerp.exceptions import ValidationError +from openerp.exceptions import ValidationError, UserError from datetime import timedelta @@ -111,4 +111,10 @@ Best Regards,''') company.reflect_code_prefix_change(company.cash_account_code_prefix, new_cash_code, digits) if values.get('accounts_code_digits'): company.reflect_code_digits_change(digits) + + #forbid the change of currency_id if there are already some accounting entries existing + if 'currency_id' in values and values['currency_id'] != company.currency_id.id: + if self.env['account.move.line'].search([('company_id', '=', company.id)]): + raise UserError(_('You cannot change the currency of the company since some journal items already exist')) + return super(ResCompany, self).write(values) From 8b119e2ae9039fe368cfadffd80d7fb559bf5ab7 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 5 Mar 2018 14:39:08 +0100 Subject: [PATCH 04/10] [FIX] base: skip failed autovaccum This patch prevents bugs fixed at 5d463d2 to block the database. It was preventing the autovacuum to properly clean old records. Failing to unlink (may have various different reasons) should not block the autovacuum to try on other models Closes #23455 --- odoo/addons/base/ir/ir_autovacuum.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/ir/ir_autovacuum.py b/odoo/addons/base/ir/ir_autovacuum.py index 774a6613592..1bbbd4ee096 100644 --- a/odoo/addons/base/ir/ir_autovacuum.py +++ b/odoo/addons/base/ir/ir_autovacuum.py @@ -17,7 +17,11 @@ class AutoVacuum(models.AbstractModel): for mname in self.env: model = self.env[mname] if model.is_transient(): - model._transient_vacuum(force=True) + try: + with cr.savepoint(): + model._transient_vacuum(force=True) + except Exception as e: + _logger.warning("Failed to clean transient model %s\n%s", model, str(e)) @api.model def _gc_user_logs(self): From 6deed451ee686d104583cb33d66c5fbc400cad8d Mon Sep 17 00:00:00 2001 From: qdp-odoo Date: Mon, 5 Mar 2018 17:39:10 +0100 Subject: [PATCH 05/10] [FIX] account: fix the runbot after commit 0e881fcd3a5671c4fe84b2a47c9d3611e4b823a1 The previous commit forbids to change the currency of a company having accounting entries, but there are some created by demo data and some tests were trying to set a different currency (to set up a fix environment). So in order to allow that in tests, we do it via a SQL query directly --- addons/account/tests/test_payment.py | 4 +++- addons/account/tests/test_reconciliation.py | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/account/tests/test_payment.py b/addons/account/tests/test_payment.py index 3e9971709ac..5c9a7531bf5 100644 --- a/addons/account/tests/test_payment.py +++ b/addons/account/tests/test_payment.py @@ -17,7 +17,9 @@ class TestPayment(AccountingTestCase): self.currency_chf_id = self.env.ref("base.CHF").id self.currency_usd_id = self.env.ref("base.USD").id self.currency_eur_id = self.env.ref("base.EUR").id - self.env.ref('base.main_company').write({'currency_id': self.currency_eur_id}) + + company = self.env.ref('base.main_company') + self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [self.currency_eur_id, company.id]) self.product = self.env.ref("product.product_product_4") self.payment_method_manual_in = self.env.ref("account.account_payment_method_manual_in") self.payment_method_manual_out = self.env.ref("account.account_payment_method_manual_out") diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index d1dc1ac191c..14ac45ec76f 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -23,7 +23,8 @@ class TestReconciliation(AccountingTestCase): self.currency_swiss_id = self.env.ref("base.CHF").id self.currency_usd_id = self.env.ref("base.USD").id self.currency_euro_id = self.env.ref("base.EUR").id - self.env.ref('base.main_company').write({'currency_id': self.currency_euro_id}) + company = self.env.ref('base.main_company') + self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [self.currency_euro_id, company.id]) self.account_rcv = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1) self.account_rsa = self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_payable').id)], limit=1) self.product = self.env.ref("product.product_product_4") @@ -477,4 +478,4 @@ class TestReconciliation(AccountingTestCase): self.assertEquals(round(aml.debit, 2), line['debit']) self.assertEquals(round(aml.credit, 2), line['credit']) self.assertEquals(round(aml.amount_currency, 2), line['amount_currency']) - self.assertEquals(aml.currency_id.id, line['currency_id']) \ No newline at end of file + self.assertEquals(aml.currency_id.id, line['currency_id']) From 31ad2aa31ea663ce957adc6ab8b55a64d66e8f1f Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Tue, 27 Feb 2018 18:35:38 -0800 Subject: [PATCH 06/10] [FIX] crm: avoid timezone date change to mess with dashboard stats Have your OS be in Vancouver with time = 15:59:00 We are on day X Have a next activity on a lead, scheduled for today At 15:59:00 the next activity is not overdue Before this commit: Past 16:00:00 the next activity becomes overdue This is because 16:00:00 is 00:00:00 in UTC (+8 hours) And the next activity is scheduled for day X But in UTC, we just passed at day X+1 Hence, the next activity being overdue After this commit: The next activity is not overdue, since we are actually on day X OPW 1815323 --- addons/crm/crm_lead.py | 33 ++++++++++++------- .../crm/static/src/js/sales_team_dashboard.js | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 465ab6540eb..2f996e00579 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import pytz from datetime import datetime, timedelta, date from dateutil.relativedelta import relativedelta import logging @@ -1105,6 +1106,14 @@ Update your business card, phone book, social media,... Send an email right now return res def retrieve_sales_dashboard(self, cr, uid, context=None): + date_today = date.today() + # We imitate here what the JS search does in the user's browser: + # retrieve the user's local date + if context and context.get('tz'): + user_tz = pytz.timezone(context['tz']) + now_utc = pytz.utc.localize(datetime.now()) + now_user_tz = now_utc.astimezone(user_tz) + date_today = date(now_user_tz.year, now_user_tz.month, now_user_tz.day) res = { 'meeting': { @@ -1143,32 +1152,32 @@ Update your business card, phone book, social media,... Send an email right now if opp['date_deadline']: date_deadline = datetime.strptime(opp['date_deadline'], tools.DEFAULT_SERVER_DATE_FORMAT).date() - if date_deadline == date.today(): + if date_deadline == date_today: res['closing']['today'] += 1 - if date_deadline >= date.today() and date_deadline <= date.today() + timedelta(days=7): + if date_deadline >= date_today and date_deadline <= date_today + timedelta(days=7): res['closing']['next_7_days'] += 1 - if date_deadline < date.today() and not opp['date_closed']: + if date_deadline < date_today and not opp['date_closed']: res['closing']['overdue'] += 1 # Next activities if opp['next_activity_id'] and opp['date_action']: date_action = datetime.strptime(opp['date_action'], tools.DEFAULT_SERVER_DATE_FORMAT).date() - if date_action == date.today(): + if date_action == date_today: res['activity']['today'] += 1 - if date_action >= date.today() and date_action <= date.today() + timedelta(days=7): + if date_action >= date_today and date_action <= date_today + timedelta(days=7): res['activity']['next_7_days'] += 1 - if date_action < date.today() and not opp['date_closed']: + if date_action < date_today and not opp['date_closed']: res['activity']['overdue'] += 1 # Won in Opportunities if opp['date_closed']: date_closed = datetime.strptime(opp['date_closed'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() - if date_closed <= date.today() and date_closed >= date.today().replace(day=1): + if date_closed <= date_today and date_closed >= date_today.replace(day=1): if opp['planned_revenue']: res['won']['this_month'] += opp['planned_revenue'] - elif date_closed < date.today().replace(day=1) and date_closed >= date.today().replace(day=1) - relativedelta(months=+1): + elif date_closed < date_today.replace(day=1) and date_closed >= date_today.replace(day=1) - relativedelta(months=+1): if opp['planned_revenue']: res['won']['last_month'] += opp['planned_revenue'] @@ -1198,9 +1207,9 @@ Update your business card, phone book, social media,... Send an email right now for act in activites_done: if act['date']: date_act = datetime.strptime(act['date'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() - if date_act <= date.today() and date_act >= date.today().replace(day=1): + if date_act <= date_today and date_act >= date_today.replace(day=1): res['done']['this_month'] += 1 - elif date_act < date.today().replace(day=1) and date_act >= date.today().replace(day=1) - relativedelta(months=+1): + elif date_act < date_today.replace(day=1) and date_act >= date_today.replace(day=1) - relativedelta(months=+1): res['done']['last_month'] += 1 # Meetings @@ -1216,9 +1225,9 @@ Update your business card, phone book, social media,... Send an email right now if meeting['start']: start = datetime.strptime(meeting['start'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() - if start == date.today(): + if start == date_today: res['meeting']['today'] += 1 - if start >= date.today() and start <= date.today() + timedelta(days=7): + if start >= date_today and start <= date_today + timedelta(days=7): res['meeting']['next_7_days'] += 1 res['nb_opportunities'] = len(opportunities) diff --git a/addons/crm/static/src/js/sales_team_dashboard.js b/addons/crm/static/src/js/sales_team_dashboard.js index b2bec86a753..2f1df97bbb1 100644 --- a/addons/crm/static/src/js/sales_team_dashboard.js +++ b/addons/crm/static/src/js/sales_team_dashboard.js @@ -8,7 +8,7 @@ SalesTeamDashboardView.include({ fetch_data: function() { return new Model('crm.lead') - .call('retrieve_sales_dashboard', []); + .call('retrieve_sales_dashboard', [], {context: this.session.user_context || {}}); } }); From b466b3bc608b54d96ca0242b4d9c6176b76d88b6 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 1 Mar 2018 13:32:22 +0100 Subject: [PATCH 07/10] [FIX] web: create favorite filters in dialogs Consider the following scenario: open in edit mode a form view containing a many2many field displayed with widget many2many_tags, on the many2many field dropdown, click on 'Search More' (it opens a dialog), and in the dialog, try to save the current search in the searchview's favorite menu. Before this rev., it tried to retrieve the context of the active view in the closest instance of a ViewManager-like widget in the SearchView's ancestors. First, it is wrong as in the given scenario, the requested context is actually the one of the list controller in the dialog, not the one of the form controller in the background. Second, it produces a crash in Studio, when trying to save filters in the dialog opened by clicking on 'Search More' on the groups many2many field in the sidebar. This part of the code can't be tested in this version, but tests will be added in master. Fixes #23293 --- addons/web/static/src/js/view_manager.js | 22 +++++++++++++++++++ addons/web/static/src/js/views/form_common.js | 20 +++++++++++++++++ .../web/static/src/js/views/search_menus.js | 16 ++++++++------ 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/addons/web/static/src/js/view_manager.js b/addons/web/static/src/js/view_manager.js index a351a813263..490b2d5cfba 100644 --- a/addons/web/static/src/js/view_manager.js +++ b/addons/web/static/src/js/view_manager.js @@ -15,6 +15,9 @@ var _t = core._t; var ViewManager = Widget.extend(ControlPanelMixin, { className: "o_view_manager_content", + custom_events: { + get_controller_context: '_onGetControllerContext', + }, /** * Called each time the view manager is attached into the DOM */ @@ -474,6 +477,25 @@ var ViewManager = Widget.extend(ControlPanelMixin, { } return this._super.apply(this, arguments); }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + // ONLY FORWARDPORT THIS COMMIT UP TO SAAS-15 + /** + * Handles a context request: provides to the caller the context of the + * active view. + * + * @private + * @param {OdooEvent} ev + * @param {function} ev.data.callback used to send the requested context + */ + _onGetControllerContext: function (ev) { + var controller = this.active_view && this.active_view.controller; + var context = controller && controller.get_context(); + ev.data.callback(context); + }, }); return ViewManager; diff --git a/addons/web/static/src/js/views/form_common.js b/addons/web/static/src/js/views/form_common.js index 16e8c088368..f5853475231 100644 --- a/addons/web/static/src/js/views/form_common.js +++ b/addons/web/static/src/js/views/form_common.js @@ -976,6 +976,9 @@ var SelectCreateListView = ListView.extend({ * Search dialog (displays a list of records and permits to create a new one by switching to a form view) */ var SelectCreateDialog = ViewDialog.extend({ + custom_events: { + get_controller_context: '_onGetControllerContext', + }, /** * options: * - initial_ids @@ -1106,6 +1109,23 @@ var SelectCreateDialog = ViewDialog.extend({ return new FormViewDialog(this.__parentedParent, this.options).open(); }, on_view_list_loaded: function() {}, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Handles a context request: provides to the caller the context of the + * list view. + * + * @private + * @param {OdooEvent} ev + * @param {function} ev.data.callback used to send the requested context + */ + _onGetControllerContext: function (ev) { + var context = this.view_list.get_context(); + ev.data.callback(context); + }, }); var DomainEditorDialog = SelectCreateDialog.extend({ diff --git a/addons/web/static/src/js/views/search_menus.js b/addons/web/static/src/js/views/search_menus.js index a8ce7274256..6c7069312aa 100644 --- a/addons/web/static/src/js/views/search_menus.js +++ b/addons/web/static/src/js/views/search_menus.js @@ -93,13 +93,15 @@ return Widget.extend({ this.$inputs.first().focus(); return; } - var search = this.searchview.build_search_data(), - view_manager = this.findAncestor(function (a) { - // HORRIBLE HACK. PLEASE SAVE ME FROM MYSELF (BUT IN A PAINLESS WAY IF POSSIBLE) - return 'active_view' in a; - }), - view_context = view_manager ? view_manager.active_view.controller.get_context() : {}, - results = pyeval.sync_eval_domains_and_contexts({ + var search = this.searchview.build_search_data(); + // ONLY FORWARDPORT THIS COMMIT UP TO SAAS-15 + var view_context; + this.trigger_up('get_controller_context', { + callback: function (ctx) { + view_context = ctx; + }, + }); + var results = pyeval.sync_eval_domains_and_contexts({ domains: search.domains, contexts: search.contexts.concat(view_context || []), group_by_seq: search.groupbys || [], From 037cfc9f67f86bc05aa9b239d8e479fd07ebaff9 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Mon, 5 Mar 2018 10:44:07 +0100 Subject: [PATCH 08/10] [FIX] delivery: products not shared - Define 2 companies A and B. Delivery methods are linked to products in company A - Deactivate 'Share product to all companies' - Create a user B in Company B only - As user B, create a SO and open the drop-down list for delivery methods An access error is raised. Delivery methods should follow the same record rules than the products they are linked to. opw-1815490 --- addons/delivery/__manifest__.py | 1 + addons/delivery/models/__init__.py | 1 + addons/delivery/models/base_config_settings.py | 17 +++++++++++++++++ addons/delivery/security/delivery_security.xml | 12 ++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 addons/delivery/models/base_config_settings.py create mode 100644 addons/delivery/security/delivery_security.xml diff --git a/addons/delivery/__manifest__.py b/addons/delivery/__manifest__.py index e91e09fef5f..462a3d7bddb 100644 --- a/addons/delivery/__manifest__.py +++ b/addons/delivery/__manifest__.py @@ -16,6 +16,7 @@ invoices from picking, the system is able to add and compute the shipping line. 'depends': ['sale_stock'], 'data': [ 'security/ir.model.access.csv', + 'security/delivery_security.xml', 'views/delivery_view.xml', 'views/partner_view.xml', 'views/product_template_view.xml', diff --git a/addons/delivery/models/__init__.py b/addons/delivery/models/__init__.py index 33fb6434d71..140f3e93204 100644 --- a/addons/delivery/models/__init__.py +++ b/addons/delivery/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import base_config_settings import delivery_carrier import delivery_price_rule import product_template diff --git a/addons/delivery/models/base_config_settings.py b/addons/delivery/models/base_config_settings.py new file mode 100644 index 00000000000..2ab67cbebdd --- /dev/null +++ b/addons/delivery/models/base_config_settings.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class BaseConfigSettings(models.TransientModel): + _inherit = 'base.config.settings' + + @api.multi + def set_auth_company_share_product(self): + self.ensure_one() + res = super(BaseConfigSettings, self).set_auth_company_share_product() + delivery_rule = self.env.ref('delivery.delivery_comp_rule', False) + if delivery_rule: + delivery_rule.write({'active': not bool(self.company_share_product)}) + return res diff --git a/addons/delivery/security/delivery_security.xml b/addons/delivery/security/delivery_security.xml new file mode 100644 index 00000000000..ae588bc245c --- /dev/null +++ b/addons/delivery/security/delivery_security.xml @@ -0,0 +1,12 @@ + + + + + Delivery multi-company + + + ['|',('product_id.company_id','=',user.company_id.id),('product_id.company_id','=',False)] + + + + From 36d691026989508d7e5272561bb5f1f16dad0bc1 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 5 Mar 2018 17:32:17 +0100 Subject: [PATCH 09/10] [FIX] survey: constraint method on recordset --- addons/survey/models/survey.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/survey/models/survey.py b/addons/survey/models/survey.py index e40e8d29235..861055fd7f2 100644 --- a/addons/survey/models/survey.py +++ b/addons/survey/models/survey.py @@ -682,6 +682,7 @@ class SurveyLabel(models.Model): value = fields.Char('Suggested value', translate=True, required=True) quizz_mark = fields.Float('Score for this choice', help="A positive score indicates a correct choice; a negative or null score indicates a wrong answer") + @api.one @api.constrains('question_id', 'question_id_2') def _check_question_not_empty(self): """Ensure that field question_id XOR field question_id_2 is not null""" From 4aea19d9e81a65ffefa9c770622b7772d071b781 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 6 Mar 2018 11:23:46 +0100 Subject: [PATCH 10/10] [FIX] sale_crm,crm: handle timezone in dashboard Complete parent commit by correcting overwritten method. Also use existing method to retrive date depending on tz in context. --- addons/crm/crm_lead.py | 15 +++------------ addons/sale_crm/crm_lead.py | 14 ++++++++------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 2f996e00579..bdc01a8781b 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -1,15 +1,13 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import pytz -from datetime import datetime, timedelta, date +from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta import logging from operator import itemgetter -from werkzeug import url_encode from openerp import SUPERUSER_ID -from openerp import tools, api +from openerp import tools, api, fields as newfields from openerp.addons.base.res.res_partner import format_address from openerp.addons.crm import crm_stage from openerp.osv import fields, osv @@ -1106,14 +1104,7 @@ Update your business card, phone book, social media,... Send an email right now return res def retrieve_sales_dashboard(self, cr, uid, context=None): - date_today = date.today() - # We imitate here what the JS search does in the user's browser: - # retrieve the user's local date - if context and context.get('tz'): - user_tz = pytz.timezone(context['tz']) - now_utc = pytz.utc.localize(datetime.now()) - now_user_tz = now_utc.astimezone(user_tz) - date_today = date(now_user_tz.year, now_user_tz.month, now_user_tz.day) + date_today = newfields.Date.from_string(fields.date.context_today(self, cr, uid, context=context)) res = { 'meeting': { diff --git a/addons/sale_crm/crm_lead.py b/addons/sale_crm/crm_lead.py index 77c05f1be2b..d8fd0493960 100644 --- a/addons/sale_crm/crm_lead.py +++ b/addons/sale_crm/crm_lead.py @@ -1,8 +1,8 @@ -from openerp import models, fields, api, _, tools -from datetime import datetime, date +from openerp import models, fields, api, tools +from openerp.osv import fields as oldfields +from datetime import datetime from dateutil.relativedelta import relativedelta -import openerp.addons.decimal_precision as dp class crm_lead(models.Model): _inherit = ['crm.lead'] @@ -28,6 +28,8 @@ class crm_lead(models.Model): def retrieve_sales_dashboard(self, cr, uid, context=None): res = super(crm_lead, self).retrieve_sales_dashboard(cr, uid, context=None) + today = fields.Date.from_string(oldfields.date.context_today(self, cr, uid, context=context)) + res['invoiced'] = { 'this_month': 0, 'last_month': 0, @@ -35,7 +37,7 @@ class crm_lead(models.Model): account_invoice_domain = [ ('state', 'in', ['open', 'paid']), ('user_id', '=', uid), - ('date', '>=', date.today().replace(day=1) - relativedelta(months=+1)), + ('date', '>=', today.replace(day=1) - relativedelta(months=+1)), ('type', 'in', ['out_invoice', 'out_refund']) ] @@ -43,9 +45,9 @@ class crm_lead(models.Model): for inv in invoice_ids: if inv['date']: inv_date = datetime.strptime(inv['date'], tools.DEFAULT_SERVER_DATE_FORMAT).date() - if inv_date <= date.today() and inv_date >= date.today().replace(day=1): + if inv_date <= today and inv_date >= today.replace(day=1): res['invoiced']['this_month'] += inv['amount_untaxed_signed'] - elif inv_date < date.today().replace(day=1) and inv_date >= date.today().replace(day=1) - relativedelta(months=+1): + elif inv_date < today.replace(day=1) and inv_date >= today.replace(day=1) - relativedelta(months=+1): res['invoiced']['last_month'] += inv['amount_untaxed_signed'] res['invoiced']['target'] = self.pool('res.users').browse(cr, uid, uid, context=context).target_sales_invoiced