From f5e197570bafef79cfaea0ee36180013e4fae9ae Mon Sep 17 00:00:00 2001 From: xmo-odoo Date: Fri, 21 Apr 2017 14:46:05 +0200 Subject: [PATCH 01/34] [FIX] translation --- addons/l10n_be/i18n/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_be/i18n/fr.po b/addons/l10n_be/i18n/fr.po index 0707b259f49..cdcb2fa046c 100644 --- a/addons/l10n_be/i18n/fr.po +++ b/addons/l10n_be/i18n/fr.po @@ -553,7 +553,7 @@ msgstr "Montant de la TVA" #: code:addons/l10n_be/wizard/l10n_be_vat_intra.py:246 #, python-format msgid "No vat number defined for %s." -msgstr "Aucun numéro de TVA défini pour %." +msgstr "Aucun numéro de TVA défini pour %s." #. module: l10n_be #: model:account.financial.report,name:l10n_be.account_financial_report_bnficepertereporte2 From 5b502841276dda0dbc0161fd0fa080d1790086bb Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Tue, 18 Apr 2017 12:00:14 +0200 Subject: [PATCH 02/34] [FIX] point_of_sale: fiscal position with included taxes in POS To substract the included tax from the unit price when it's mapped by a fiscal position in the POS. To keep the same behavior introduced by this commit odoo@503820a opw:710437 --- addons/point_of_sale/static/src/js/models.js | 32 ++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index fc9a877f3b9..b0abc797f08 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -1345,7 +1345,7 @@ exports.Orderline = Backbone.Model.extend({ } return false; }, - compute_all: function(taxes, price_unit, quantity, currency_rounding) { + compute_all: function(taxes, price_unit, quantity, currency_rounding, no_map_tax) { var self = this; var list_taxes = []; var currency_rounding_bak = currency_rounding; @@ -1356,7 +1356,9 @@ exports.Orderline = Backbone.Model.extend({ var total_included = total_excluded; var base = total_excluded; _(taxes).each(function(tax) { - tax = self._map_tax_fiscal_position(tax); + if (!no_map_tax){ + tax = self._map_tax_fiscal_position(tax); + } if (tax.amount_type === 'group'){ var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding); total_excluded = ret.total_excluded; @@ -1813,6 +1815,27 @@ exports.Order = Backbone.Model.extend({ this.orderlines.remove(line); this.select_orderline(this.get_last_orderline()); }, + + fix_tax_included_price: function(line){ + if(this.fiscal_position){ + var unit_price = line.price; + var taxes = line.get_taxes(); + var mapped_included_taxes = []; + _(taxes).each(function(tax) { + var line_tax = line._map_tax_fiscal_position(tax); + if(tax.price_include && tax.id != line_tax.id){ + + mapped_included_taxes.push(tax); + } + }) + + unit_price = line.compute_all(mapped_included_taxes, unit_price, 1, this.pos.currency.rounding, true).total_excluded; + + line.set_unit_price(unit_price); + } + + }, + add_product: function(product, options){ if(this._printed){ this.destroy(); @@ -1828,9 +1851,14 @@ exports.Order = Backbone.Model.extend({ if(options.quantity !== undefined){ line.set_quantity(options.quantity); } + if(options.price !== undefined){ line.set_unit_price(options.price); } + + //To substract from the unit price the included taxes mapped by the fiscal position + this.fix_tax_included_price(line); + if(options.discount !== undefined){ line.set_discount(options.discount); } From 577d9863919d5b1a9d8a21db81123f90949e1342 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 21 Apr 2017 17:21:21 +0200 Subject: [PATCH 03/34] [FIX] account: subtract refund amount from "awaiting payments" total Currently the amounts of all invoices (linked to sale or purchase journals) is summed regardless of these invoices being bills or refunds, this means e.g. if you create an invoice for 1000 then a refund for 500 on this invoice, the dashboard will display an "awaiting payment" total of 1500 which while technically correct is not really logical or helpful from a user's POV. Fix summation to take in account the invoice type (invoice or refund) and remove refund amount from the total amount. Intent validated by @lap-odoo, also not ignoring or removing refunds from count to avoid situations like "0 Awaiting Payments [for] $-1200" which would be really strange. OPW-728860 --- .../models/account_journal_dashboard.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 8fc73d1a3ca..505442c36dd 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -138,7 +138,8 @@ class account_journal(models.Model): number_to_reconcile = last_balance = account_sum = 0 ac_bnk_stmt = [] title = '' - number_draft = number_waiting = number_late = sum_draft = sum_waiting = sum_late = 0 + number_draft = number_waiting = number_late = 0 + sum_draft = sum_waiting = sum_late = 0.0 if self.type in ['bank', 'cash']: last_bank_stmt = self.env['account.bank.statement'].search([('journal_id', 'in', self.ids)], order="date desc, id desc", limit=1) last_balance = last_bank_stmt and last_bank_stmt[0].balance_end or 0 @@ -171,30 +172,33 @@ class account_journal(models.Model): elif self.type in ['sale', 'purchase']: title = _('Bills to pay') if self.type == 'purchase' else _('Invoices owed to you') # optimization to find total and sum of invoice that are in draft, open state - query = """SELECT state, amount_total, currency_id AS currency FROM account_invoice WHERE journal_id = %s AND state NOT IN ('paid', 'cancel');""" + query = """SELECT state, amount_total, currency_id AS currency, type FROM account_invoice WHERE journal_id = %s AND state NOT IN ('paid', 'cancel');""" self.env.cr.execute(query, (self.id,)) query_results = self.env.cr.dictfetchall() today = datetime.today() - query = """SELECT amount_total, currency_id AS currency FROM account_invoice WHERE journal_id = %s AND date < %s AND state = 'open';""" + query = """SELECT amount_total, currency_id AS currency, type FROM account_invoice WHERE journal_id = %s AND date < %s AND state = 'open';""" self.env.cr.execute(query, (self.id, today)) late_query_results = self.env.cr.dictfetchall() - sum_draft = 0.0 - number_draft = 0 - number_waiting = 0 for result in query_results: + if result['type'] in ['in_refund', 'out_refund']: + factor = -1 + else: + factor = 1 cur = self.env['res.currency'].browse(result.get('currency')) if result.get('state') in ['draft', 'proforma', 'proforma2']: number_draft += 1 - sum_draft += cur.compute(result.get('amount_total'), currency) + sum_draft += cur.compute(result.get('amount_total'), currency) * factor elif result.get('state') == 'open': number_waiting += 1 - sum_waiting += cur.compute(result.get('amount_total'), currency) - sum_late = 0.0 - number_late = 0 + sum_waiting += cur.compute(result.get('amount_total'), currency) * factor for result in late_query_results: + if result['type'] in ['in_refund', 'out_refund']: + factor = -1 + else: + factor = 1 cur = self.env['res.currency'].browse(result.get('currency')) number_late += 1 - sum_late += cur.compute(result.get('amount_total'), currency) + sum_late += cur.compute(result.get('amount_total'), currency) * factor return { 'number_to_reconcile': number_to_reconcile, From ec8699db1773381e3e5d6e999d4dee038d2b993c Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Fri, 14 Apr 2017 16:58:49 +0200 Subject: [PATCH 04/34] [FIX] account: performance in issued total When a partner has a lot of contacts, the computation of the issue total takes a lot of time. Indeed, it performs the computation for the partner, but also for all the children, despite the fact that the amount is not displayed. opw-726355 --- addons/account/models/partner.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index bb78919c767..8e18962ab00 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -349,12 +349,9 @@ class ResPartner(models.Model): def _compute_issued_total(self): """ Returns the issued total as will be displayed on partner view """ today = fields.Date.context_today(self) - for partner in self: - domain = partner.get_followup_lines_domain(today, overdue_only=True) - issued_total = 0 - for aml in self.env['account.move.line'].search(domain): - issued_total += aml.amount_residual - partner.issued_total = issued_total + domain = self.get_followup_lines_domain(today, overdue_only=True) + for aml in self.env['account.move.line'].search(domain): + aml.partner_id.issued_total += aml.amount_residual @api.one def _compute_has_unreconciled_entries(self): From cb999050bb18cc1fbae11ba34f41195f0ce29ca0 Mon Sep 17 00:00:00 2001 From: SauliusZ Date: Mon, 24 Apr 2017 15:02:23 +0200 Subject: [PATCH 05/34] [CLA] signature for realsaiko Done at #16600 --- doc/cla/individual/realsaiko.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/realsaiko.md diff --git a/doc/cla/individual/realsaiko.md b/doc/cla/individual/realsaiko.md new file mode 100644 index 00000000000..b38a5fd06f1 --- /dev/null +++ b/doc/cla/individual/realsaiko.md @@ -0,0 +1,11 @@ +Spain, 2017-04-24 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Saulius Žilys realsaiko@gmail.com https://github.com/realsaiko From 7d15a6e6d67ebd867c69ae9153dd80b70dbebac8 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Wed, 12 Apr 2017 09:51:06 +0200 Subject: [PATCH 06/34] [FIX] payment_stripe: handler.open in deferred As explained in https://stripe.com/docs/checkout#integration-more-runloop the function handler.open cannot be called in a deferred because it can block in some browser. opw:726101 --- addons/payment_stripe/static/src/js/stripe.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/payment_stripe/static/src/js/stripe.js b/addons/payment_stripe/static/src/js/stripe.js index 5d0fa66ff23..7338313e245 100644 --- a/addons/payment_stripe/static/src/js/stripe.js +++ b/addons/payment_stripe/static/src/js/stripe.js @@ -55,19 +55,18 @@ odoo.define('payment_stripe.stripe', function(require) { amount: $("input[name='amount']").val(), currency_id: $("input[name='currency_id']").val(), acquirer_id: acquirer_id - }).then(function (data) { + }) handler.open({ name: $("input[name='merchant']").val(), description: $("input[name='invoice_num']").val(), currency: $("input[name='currency']").val(), amount: $("input[name='amount']").val()*100 }); - }); } else { ajax.jsonRpc('/shop/payment/transaction/' + acquirer_id, 'call', { so_id: so_id, so_token: so_token - }).then(function (data) { + }, {'async': false}).then(function (data) { $form.html(data); handler.open({ name: $("input[name='merchant']").val(), From 08fcda5c94635b182f1e78176c0384c00952791f Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 25 Apr 2017 09:18:24 +0200 Subject: [PATCH 07/34] [CLA] realsaiko no longer works for vialaurea Closes #16599 --- doc/cla/corporate/via_laurea.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/cla/corporate/via_laurea.md b/doc/cla/corporate/via_laurea.md index 66986aeaa6e..223b0317a54 100644 --- a/doc/cla/corporate/via_laurea.md +++ b/doc/cla/corporate/via_laurea.md @@ -6,8 +6,8 @@ I declare that I am authorized and able to make this agreement and sign this dec Signed, -Saulius Zilys saulius@vialaurea.lt https://github.com/realsaiko +Saulius Zilys saulius@vialaurea.lt (up to 2017-04-24) List of contributors: -Saulius Zilys saulius@vialaurea.lt https://github.com/realsaiko +Saulius Zilys saulius@vialaurea.lt (up to 2017-04-24) From 4c8c996f4d1ae3844a96ffcee7739db809ed2033 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Mon, 24 Apr 2017 22:44:18 +0200 Subject: [PATCH 08/34] [FIX] payment: Deleting payment tokens from website When deleting payment tokens from website, in My account, by clicking on button "Manage your payment methods", the public user, the user and the portal user got a 403 error. But when creating a subscription for a customer with admin user and setting a payment token for the company of this customer. This payment token could not be deleted or modified by its users. In a few cases, it's needed to delete or modify a payment token, for example, when the expiration date of the payment token is expired. opw:740169 --- addons/payment/security/payment_security.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/payment/security/payment_security.xml b/addons/payment/security/payment_security.xml index c5ad3f62fe7..df797070aed 100644 --- a/addons/payment/security/payment_security.xml +++ b/addons/payment/security/payment_security.xml @@ -15,7 +15,7 @@ Access own payment tokens only - [('partner_id','=',user.partner_id.id)] + ['|', ('partner_id','=',user.partner_id.id), ('partner_id', '=', user.partner_id.commercial_partner_id.id)] From 83ec253b78ee07290a64f71b08d24ff56feb1c4e Mon Sep 17 00:00:00 2001 From: Rogan Date: Mon, 24 Apr 2017 23:21:29 +0800 Subject: [PATCH 09/34] [CLA] signature for RoganW Closes #16608 --- doc/cla/individual/roganw.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/roganw.md diff --git a/doc/cla/individual/roganw.md b/doc/cla/individual/roganw.md new file mode 100644 index 00000000000..75afdc09ae3 --- /dev/null +++ b/doc/cla/individual/roganw.md @@ -0,0 +1,11 @@ +China, 2017-04-24 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Rogan Wang quangenw@gmail.com https://github.com/RoganW From d5384f8f9d76063f008b8107bb6d945405501ac5 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Tue, 25 Apr 2017 10:16:35 +0200 Subject: [PATCH 10/34] [FIX] website_portal: portal user don't have access to website.company_id Avoid portal user to receive 403 when trying to edit vat code on /my/account There are no risk to read field in sudo in this case. opw-740314 --- addons/website_portal/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_portal/controllers/main.py b/addons/website_portal/controllers/main.py index a39a7ed6114..117e0f7dba7 100644 --- a/addons/website_portal/controllers/main.py +++ b/addons/website_portal/controllers/main.py @@ -105,7 +105,7 @@ class website_account(http.Controller): # vat validation if data.get("vat") and hasattr(request.env["res.partner"], "check_vat"): - if request.website.company_id.vat_check_vies: + if request.website.company_id.sudo().vat_check_vies: # force full VIES online check check_func = request.env["res.partner"].vies_vat_check else: From 863f46ec71a61f26a03959443db2c42bbc17c01e Mon Sep 17 00:00:00 2001 From: Olivier Colson Date: Tue, 25 Apr 2017 11:01:33 +0200 Subject: [PATCH 11/34] [FIX] resource: conditions on id Don't check ids with "id is None" statements, as it won't match if id is False. This caused some bugs with MRP when confirming a MO with no calendar defined, if mrp_operations was installed. --- addons/resource/resource.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/resource/resource.py b/addons/resource/resource.py index 09a5c2f3add..5a2b3559497 100644 --- a/addons/resource/resource.py +++ b/addons/resource/resource.py @@ -179,7 +179,7 @@ class resource_calendar(osv.osv): def get_weekdays(self, cr, uid, id, default_weekdays=None, context=None): """ Return the list of weekdays that contain at least one working interval. If no id is given (no calendar), return default weekdays. """ - if id is None: + if not id: return default_weekdays if default_weekdays is not None else [0, 1, 2, 3, 4] calendar = self.browse(cr, uid, id, context=None) weekdays = set() @@ -329,7 +329,7 @@ class resource_calendar(osv.osv): work_dt = start_dt.replace(hour=0, minute=0, second=0) # no calendar: try to use the default_interval, then return directly - if id is None: + if not id: working_interval = [] if default_interval: working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0), start_dt.replace(hour=default_interval[1], minute=0, second=0)) @@ -448,7 +448,7 @@ class resource_calendar(osv.osv): working_intervals = self.get_working_intervals_of_day(cr, uid, id, **call_args) - if id is None and not working_intervals: # no calendar -> consider working 8 hours + if not id and not working_intervals: # no calendar -> consider working 8 hours remaining_hours -= 8.0 elif working_intervals: if backwards: @@ -541,7 +541,7 @@ class resource_calendar(osv.osv): compute_leaves=compute_leaves, resource_id=resource_id, default_interval=default_interval, context=context) - if id is None or working_intervals: # no calendar -> no working hours, but day is considered as worked + if not id or working_intervals: # no calendar -> no working hours, but day is considered as worked planned_days += 1 intervals += working_intervals # get next day From 42bb9aab2710ea02cab2e32a3928c77284ee3ae3 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 25 Apr 2017 12:19:31 +0200 Subject: [PATCH 12/34] [FIX] hr_maintenance: allow to search and group equipments on employees and departments --- addons/hr_maintenance/views/maintenance_views.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addons/hr_maintenance/views/maintenance_views.xml b/addons/hr_maintenance/views/maintenance_views.xml index 72970f9cd77..976279d57b6 100644 --- a/addons/hr_maintenance/views/maintenance_views.xml +++ b/addons/hr_maintenance/views/maintenance_views.xml @@ -65,6 +65,14 @@ [('employee_id', '=', False), ('department_id', '=', False)] + + + + + + + + From 29bc0d811dd1f6ce32baa0a6d0e602cabef64909 Mon Sep 17 00:00:00 2001 From: Wolfgang Taferner Date: Sat, 4 Mar 2017 08:11:23 +0100 Subject: [PATCH 13/34] [FIX] base: Make sure start time is not undefined As debugging might be enabled during runtime we need to make sure that the condition does only pass in case start time is defined --- odoo/addons/base/ir/ir_cron.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/ir/ir_cron.py b/odoo/addons/base/ir/ir_cron.py index ee58bfb9145..f8240431ef5 100644 --- a/odoo/addons/base/ir/ir_cron.py +++ b/odoo/addons/base/ir/ir_cron.py @@ -114,10 +114,11 @@ class ir_cron(models.Model): if hasattr(model, method_name): log_depth = (None if _logger.isEnabledFor(logging.DEBUG) else 1) odoo.netsvc.log(_logger, logging.DEBUG, 'cron.object.execute', (self._cr.dbname, self._uid, '*', model_name, method_name)+tuple(args), depth=log_depth) + start_time = False if _logger.isEnabledFor(logging.DEBUG): start_time = time.time() getattr(model, method_name)(*args) - if _logger.isEnabledFor(logging.DEBUG): + if start_time and _logger.isEnabledFor(logging.DEBUG): end_time = time.time() _logger.debug('%.3fs (%s, %s)', end_time - start_time, model_name, method_name) self.pool.signal_caches_change() From e25b191b0a4964f207495d9239199b65358bd6a8 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Mon, 24 Apr 2017 10:27:43 +0200 Subject: [PATCH 14/34] [FIX] web_kanban: _is_quick_create_enabled grouping by day, month or year When grouping by day, by month or by year on a field date in kanban view, it raised a traceback. opw:727266 --- addons/web_kanban/static/src/js/kanban_view.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js index 167f5ee2a58..4d2aed09323 100644 --- a/addons/web_kanban/static/src/js/kanban_view.js +++ b/addons/web_kanban/static/src/js/kanban_view.js @@ -307,7 +307,8 @@ var KanbanView = View.extend({ return this.fields_view.fields.active; }, _is_quick_create_enabled: function() { - if(!_.contains(['char', 'boolean', 'many2one'], this.fields[this.group_by_field].type)){ + var group_by_field = this.group_by_field.split(':')[0] + if(!_.contains(['char', 'boolean', 'many2one'], this.fields[group_by_field].type)){ return false; } if (!this.quick_creatable || !this.is_action_enabled('create')) From 7416afed578f0c933c215fbc5f0354ac663a9340 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Tue, 25 Apr 2017 11:46:35 +0200 Subject: [PATCH 15/34] [FIX] account: Impossible to call pay_and_reconcile by rpc In 9.0 the argument pay_journal in function pay_and_reconcile must be an id but in 10.0, pay_journal must be a recordset with one element. So it was not possible to call this method by rpc in 10.0 opw:725737 --- addons/account/models/account_invoice.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 9af966fce26..3c99f337c05 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1077,6 +1077,8 @@ class AccountInvoice(models.Model): :param date: payment date, defaults to fields.Date.context_today(self) :param writeoff_acc: account in which to create a writeoff if pay_amount < self.residual, so that the invoice is fully paid """ + if isinstance( pay_journal, ( int, long ) ): + pay_journal = self.env['account.journal'].browse([pay_journal]) assert len(self) == 1, "Can only pay one invoice at a time." payment_type = self.type in ('out_invoice', 'in_refund') and 'inbound' or 'outbound' if payment_type == 'inbound': @@ -1107,6 +1109,8 @@ class AccountInvoice(models.Model): }) payment.post() + return True + @api.multi def _track_subtype(self, init_values): self.ensure_one() From 79519ce40096b64d447c293c241c1ef9c05c99e3 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 28 Mar 2017 10:54:44 +0200 Subject: [PATCH 16/34] [FIX] registry: load inheritS models too When adding a field on a model, only the _inherit were checked, not the _inherits. This commit fixes the following bug: 1. install `sale` (adding the field `sale_order_count` on `res.partner`) - field `sale_order_count` is created on `res.partner` - the ir.model.field is tagged with module `sale` 2. install `point_of_sale` (adding another field on `res.users`) - field `sale_order_count` is created on `res.users` - the ir.model.field is tagged with module `point_of_sale` When installing sale, the model res.partner is returned in the list of impacted models, on which _create_fields method is called but not res.users. A direct consequence of this bug is that, the fields translations are in the wrong module. May fix some uninstallation bugs too. Closes #16104 --- odoo/modules/registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index 6e5378d33c8..d6da9812701 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -261,7 +261,7 @@ class Registry(Mapping): model = cls._build_model(self, cr) model_names.append(model._name) - return self.descendants(model_names, '_inherit') + return self.descendants(model_names, '_inherit', '_inherits') def setup_models(self, cr, partial=False): """ Complete the setup of models. From f698ed534193067c31acfeeaa6ea3c2cbc4ca13a Mon Sep 17 00:00:00 2001 From: amoyaux Date: Mon, 13 Feb 2017 15:49:13 +0100 Subject: [PATCH 17/34] [FIX][Accounting] Entry chatter "has been created from : empty" When a journal entry is created automatically from an invoice validation, that logs an internal note with the text "This account entry has been created from:". There should be a clickable invoice's reference after but there was only an empty space. The problem come from the method that call the template "message_origin_link" with the selected invoices as arguments. The template use the attributes id, _name and name. But for the invoice object we want to use the number attribute instead of name. The name of an invoice is empty by default and thus is rendered as a blank in the template. This commit modify the template and use the property display_name instead of name. "display_name" use the number for the invoice and is still correct for other object. The invoice name is just a reference and is not required, so if you use the field name of the invoice it can return an empty string. The name_get method returns the invoice id + invoice number and eventualy the invoice name. --- addons/mail/views/mail_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/views/mail_templates.xml b/addons/mail/views/mail_templates.xml index 91f7be41d96..ed840d6bfe1 100644 --- a/addons/mail/views/mail_templates.xml +++ b/addons/mail/views/mail_templates.xml @@ -36,7 +36,7 @@ From 0550da4d880ad2cd387bf17577de4776d10da867 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 20 Apr 2017 13:19:26 +0200 Subject: [PATCH 18/34] [FIX] mrp: public/rpc methods not returning any value XML-RPC can't serialize None, as a result methods callable over RPC must return something (True, False, an actual value) otherwise an error is generated when their result is serialized and sent back to the client. It happens that JSON-RPC has different limitations and can serialize None, so MRP had multiple button/action methods which could be called over JSON-RPC but would break when called in XML-RPC for no reason. Fix them by returning `True` or result of `write`. OPW-728844 --- addons/mrp/models/mrp_production.py | 6 ++++-- addons/mrp/models/mrp_unbuild.py | 2 +- addons/mrp/models/mrp_workorder.py | 12 ++++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 6cbaf4c6629..8a637080c3b 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -410,7 +410,7 @@ class MrpProduction(models.Model): quantity = order.product_uom_id._compute_quantity(order.product_qty, order.bom_id.product_uom_id) / order.bom_id.product_qty boms, lines = order.bom_id.explode(order.product_id, quantity, picking_type=order.bom_id.picking_type_id) order._generate_workorders(boms) - orders_to_plan.write({'state': 'planned'}) + return orders_to_plan.write({'state': 'planned'}) @api.multi def _generate_workorders(self, exploded_boms): @@ -539,17 +539,19 @@ class MrpProduction(models.Model): moves_to_cancel.action_cancel() self.write({'state': 'done', 'date_finished': fields.Datetime.now()}) self.env["procurement.order"].search([('production_id', 'in', self.ids)]).check() - self.write({'state': 'done'}) + return self.write({'state': 'done'}) @api.multi def do_unreserve(self): for production in self: production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')).do_unreserve() + return True @api.multi def button_unreserve(self): self.ensure_one() self.do_unreserve() + return True @api.multi def button_scrap(self): diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py index 5b426901256..dc22a8e87f1 100644 --- a/addons/mrp/models/mrp_unbuild.py +++ b/addons/mrp/models/mrp_unbuild.py @@ -131,7 +131,7 @@ class MrpUnbuild(models.Model): produced_quant_ids = produce_moves.mapped('quant_ids').filtered(lambda quant: quant.qty > 0) consume_move.quant_ids.sudo().write({'produced_quant_ids': [(6, 0, produced_quant_ids.ids)]}) - self.write({'state': 'done'}) + return self.write({'state': 'done'}) def _generate_consume_moves(self): moves = self.env['stock.move'] diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index 23cd851b780..86107856873 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -342,6 +342,7 @@ class MrpWorkorder(models.Model): self.final_lot_id = False if self.qty_produced >= self.production_id.product_qty: self.button_finish() + return True @api.multi def button_start(self): @@ -369,7 +370,7 @@ class MrpWorkorder(models.Model): 'date_start': datetime.now(), 'user_id': self.env.user.id }) - self.write({'state': 'progress', + return self.write({'state': 'progress', 'date_start': datetime.now(), }) @@ -377,7 +378,7 @@ class MrpWorkorder(models.Model): def button_finish(self): self.ensure_one() self.end_all() - self.write({'state': 'done', 'date_finished': fields.Datetime.now()}) + return self.write({'state': 'done', 'date_finished': fields.Datetime.now()}) @api.multi def end_previous(self, doall=False): @@ -405,6 +406,7 @@ class MrpWorkorder(models.Model): if not len(loss_id): raise UserError(_("You need to define at least one unactive productivity loss in the category 'Performance'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses.")) timeline.copy({'date_start': maxdate, 'date_end': enddate, 'loss_id': loss_id.id}) + return True @api.multi def end_all(self): @@ -413,22 +415,24 @@ class MrpWorkorder(models.Model): @api.multi def button_pending(self): self.end_previous() + return True @api.multi def button_unblock(self): for order in self: order.workcenter_id.unblock() + return True @api.multi def action_cancel(self): - self.write({'state': 'cancel'}) + return self.write({'state': 'cancel'}) @api.multi def button_done(self): if any([x.state in ('done', 'cancel') for x in self]): raise UserError(_('A Manufacturing Order is already done or cancelled!')) self.end_all() - self.write({'state': 'done', + return self.write({'state': 'done', 'date_finished': datetime.now()}) @api.multi From 5f7fc56b7aa47b9fa6015a05f8d755f1f9670076 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Tue, 25 Apr 2017 10:39:08 +0200 Subject: [PATCH 19/34] [FIX] website_sale: fix settings for front end group_xxx During refacotring of the new settings, the group='xxx' has been removed. Withtout it, the implied_group are only apply to the group Employee. In this case we want that group_portal and group_public have also the group to have the option on the website. opw-740751 --- addons/website_sale/models/res_config.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/website_sale/models/res_config.py b/addons/website_sale/models/res_config.py index cfc9bdf5844..81da35177ca 100644 --- a/addons/website_sale/models/res_config.py +++ b/addons/website_sale/models/res_config.py @@ -30,7 +30,7 @@ class WebsiteConfigSettings(models.TransientModel): module_sale_ebay = fields.Boolean("eBay connector") module_sale_coupon = fields.Boolean("Discount Programs") - group_website_multiimage = fields.Boolean(string='Multi-Images', implied_group='website_sale.group_website_multi_image') + group_website_multiimage = fields.Boolean(string='Multi-Images', implied_group='website_sale.group_website_multi_image', group='base.group_portal,base.group_user,base.group_public') group_discount_per_so_line = fields.Boolean(string="Discounted Prices", implied_group='sale.group_discount_per_so_line') group_delivery_invoice_address = fields.Boolean(string="Shipping Address", implied_group='sale.group_delivery_invoice_address') @@ -58,8 +58,14 @@ class WebsiteConfigSettings(models.TransientModel): order_mail_template = fields.Many2one('mail.template', string='Order Confirmation Email', default=_default_order_mail_template, domain="[('model', '=', 'sale.order')]", help="Email sent to customer at the end of the checkout process") - group_show_price_subtotal = fields.Boolean("Show subtotal", implied_group='sale.group_show_price_subtotal') - group_show_price_total = fields.Boolean("Show total", implied_group='sale.group_show_price_total') + group_show_price_subtotal = fields.Boolean( + "Show subtotal", + implied_group='sale.group_show_price_subtotal', + group='base.group_portal,base.group_user,base.group_public') + group_show_price_total = fields.Boolean( + "Show total", + implied_group='sale.group_show_price_total', + group='base.group_portal,base.group_user,base.group_public') default_invoice_policy = fields.Selection([ ('order', 'Ordered quantities'), From 33630d8f4290e1693b3d0872a5e10e9ae9ddb7ee Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 26 Apr 2017 11:26:46 +0200 Subject: [PATCH 20/34] [FIX] sale, sale_stock: inconsistent decorator The decorator is inconsistent, and actually useless. opw-740787 --- addons/sale/models/sale.py | 1 - addons/sale_stock/models/sale_order.py | 1 - 2 files changed, 2 deletions(-) diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 4902c4d6fb2..53ee89c5c98 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -442,7 +442,6 @@ class SaleOrder(models.Model): def action_done(self): self.write({'state': 'done'}) - @api.multi def _prepare_procurement_group(self): return {'name': self.name} diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 49f8652719d..748ca14924c 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -72,7 +72,6 @@ class SaleOrder(models.Model): invoice_vals['incoterms_id'] = self.incoterm.id or False return invoice_vals - @api.model def _prepare_procurement_group(self): res = super(SaleOrder, self)._prepare_procurement_group() res.update({'move_type': self.picking_policy, 'partner_id': self.partner_shipping_id.id}) From 3b2ad20d243fb62f7d5e0b03f8ec1b0dd7189c8e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 26 Apr 2017 12:03:09 +0200 Subject: [PATCH 21/34] [FIX] point_of_sale: remove useless code Introduced at 36e7327f as a patch for stable and 1d2ca932 fixed in master. Maybe the capslock was not big enough, will use next time. --- addons/point_of_sale/models/pos_session.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index a664e639c3b..68eb8bbbc71 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -236,10 +236,6 @@ class PosSession(models.Model): @api.multi def action_pos_session_closing_control(self): for session in self: - #DO NOT FORWARD-PORT - if session.state == 'closing_control': - session.action_pos_session_close() - continue for statement in session.statement_ids: if (statement != session.cash_register_id) and (statement.balance_end != statement.balance_end_real): statement.write({'balance_end_real': statement.balance_end}) From e3cd54bb1d1bb4eda4937504636e277edf5aa9c2 Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Wed, 26 Apr 2017 11:11:04 +0200 Subject: [PATCH 22/34] [FIX] pylint: using variable before assignment Since last version (1.7) of pylint, new errors have been detected. Related to rev: https://github.com/odoo/odoo/commit/ecac88dcc074b81ede4925a75080e8d2b4975924 --- odoo/addons/base/res/res_lang.py | 2 +- odoo/osv/orm.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/odoo/addons/base/res/res_lang.py b/odoo/addons/base/res/res_lang.py index d1517a69764..6320f9d822d 100644 --- a/odoo/addons/base/res/res_lang.py +++ b/odoo/addons/base/res/res_lang.py @@ -259,7 +259,7 @@ class Lang(models.Model): if percent[-1] in 'eEfFgG': parts = formatted.split('.') - parts[0], _ = intersperse(parts[0], eval_lang_grouping, thousands_sep) + parts[0] = intersperse(parts[0], eval_lang_grouping, thousands_sep)[0] formatted = decimal_point.join(parts) diff --git a/odoo/osv/orm.py b/odoo/osv/orm.py index 2e383ad0d9e..6a815c9577a 100644 --- a/odoo/osv/orm.py +++ b/odoo/osv/orm.py @@ -118,13 +118,13 @@ def test_modifiers(what, expected): node = etree.fromstring(what) transfer_node_to_modifiers(node, modifiers) simplify_modifiers(modifiers) - json = json.dumps(modifiers) - assert json == expected, "%s != %s" % (json, expected) + dump = json.dumps(modifiers) + assert dump == expected, "%s != %s" % (dump, expected) elif isinstance(what, dict): transfer_field_to_modifiers(what, modifiers) simplify_modifiers(modifiers) - json = json.dumps(modifiers) - assert json == expected, "%s != %s" % (json, expected) + dump = json.dumps(modifiers) + assert dump == expected, "%s != %s" % (dump, expected) # To use this test: From 831322bb92105324d12f8f387ed05b86af57154b Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 26 Apr 2017 13:44:21 +0200 Subject: [PATCH 23/34] [FIX] hr_expense: expense lines > and < 0 - Create an expense sheet - Line 1 is 500 - Line 2 is -200 - The total is 300 => a payment of 300 is made When the payment is validated, the expense is not set as paid. In v10, one expense line = one account move. In order to know if the expense is paid we check that each account move is completely reconciled. The account move of 500 is never completely reconciled by the payment of 300, so the expense stays opened. This simply cannot be supported in v10, since each expense line is handled in a separate account move. opw-740492 --- addons/hr_expense/models/hr_expense.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 55c366ba1de..05581af684a 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -4,7 +4,7 @@ import re from odoo import api, fields, models, _ -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.tools import email_split, float_is_zero import odoo.addons.decimal_precision as dp @@ -545,3 +545,12 @@ class HrExpenseSheet(models.Model): res['domain'] = [('ref', 'in', self.mapped('name'))] res['context'] = {} return res + + @api.one + @api.constrains('expense_line_ids') + def _check_amounts(self): + # DO NOT FORWARD-PORT! ONLY FOR v10 + positive_lines = any([l.total_amount > 0 for l in self.expense_line_ids]) + negative_lines = any([l.total_amount < 0 for l in self.expense_line_ids]) + if positive_lines and negative_lines: + raise ValidationError(_('You cannot have a positive and negative amounts on the same expense report.')) From 4287a701103e557dc577005dfb8867d2f0970e39 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Apr 2017 11:31:12 +0200 Subject: [PATCH 24/34] [FIX] models: remove `ir.model.fields` that no longer exist in registry This fixes the following issue. Assume a custom field F is created on `res.partner`. This field will appear on `res.users` as an inherited field, and will be reflected in `ir.model.fields` as a 'base' field. When F is removed from the parent model, it no longer appears on the child model in registry, but remains in `ir.model.fields`. The patch allows the proper removal of the no-longer-inherited field from `ir.model.fields`. --- odoo/models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/odoo/models.py b/odoo/models.py index 4c8dc9c6630..e908f3609f5 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -368,6 +368,15 @@ class BaseModel(object): field_id = cr.fetchone()[0] self._context['todo'].append((20, Fields.browse(field_id).modified, [names])) + if not self.pool._init: + # remove ir.model.fields that are not in self._fields + fields = Fields.browse([col['id'] + for name, col in cols.iteritems() + if name not in self._fields]) + # add key '_force_unlink' in context to (1) force the removal of the + # fields and (2) not reload the registry + fields.with_context(_force_unlink=True).unlink() + self.invalidate_cache() @api.model From 482fec483874337a5df7d1b19d950273727036c0 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 26 Apr 2017 15:23:06 +0200 Subject: [PATCH 25/34] [FIX] models: support removing an `ir.model.fields` that no longer appears on model --- odoo/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odoo/models.py b/odoo/models.py index e908f3609f5..99c1cfe8075 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -398,7 +398,7 @@ class BaseModel(object): This method should only be used for manual fields. """ cls = type(self) - field = cls._fields.pop(name) + field = cls._fields.pop(name, None) if hasattr(cls, name): delattr(cls, name) return field From 7e1a523fbff1df45e41fb24b71ea6d0bbb86d0e9 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 25 Apr 2017 17:57:07 +0200 Subject: [PATCH 26/34] [FIX] product: missing string on field Closes #16640 --- addons/product/i18n/product.pot | 12 ++++-------- addons/product/models/product.py | 3 ++- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/addons/product/i18n/product.pot b/addons/product/i18n/product.pot index 842fd0356db..5669c10274f 100644 --- a/addons/product/i18n/product.pot +++ b/addons/product/i18n/product.pot @@ -4,10 +4,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 10.0c\n" +"Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-29 14:00+0000\n" -"PO-Revision-Date: 2016-09-29 14:00+0000\n" +"POT-Creation-Date: 2017-04-26 13:35+0000\n" +"PO-Revision-Date: 2017-04-26 13:35+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -602,6 +602,7 @@ msgid "Companies" msgstr "" #. module: product +#: model:ir.model.fields,field_description:product.field_product_price_history_company_id #: model:ir.model.fields,field_description:product.field_product_pricelist_company_id #: model:ir.model.fields,field_description:product.field_product_pricelist_item_company_id #: model:ir.model.fields,field_description:product.field_product_product_company_id @@ -610,11 +611,6 @@ msgstr "" msgid "Company" msgstr "" -#. module: product -#: model:ir.model.fields,field_description:product.field_product_price_history_company_id -msgid "Company id" -msgstr "" - #. module: product #: model:ir.ui.view,arch_db:product.product_pricelist_item_form_view msgid "Compute Price" diff --git a/addons/product/models/product.py b/addons/product/models/product.py index 39505563ba9..ca31a71f90b 100644 --- a/addons/product/models/product.py +++ b/addons/product/models/product.py @@ -94,7 +94,8 @@ class ProductPriceHistory(models.Model): def _get_default_company_id(self): return self._context.get('force_company', self.env.user.company_id.id) - company_id = fields.Many2one('res.company', default=_get_default_company_id, required=True) + company_id = fields.Many2one('res.company', string='Company', + default=_get_default_company_id, required=True) product_id = fields.Many2one('product.product', 'Product', ondelete='cascade', required=True) datetime = fields.Datetime('Date', default=fields.Datetime.now) cost = fields.Float('Cost', digits=dp.get_precision('Product Price')) From fe70f0711f2a198aa84a5bdbe1e1db703cb52831 Mon Sep 17 00:00:00 2001 From: Wolfgang Taferner Date: Wed, 26 Apr 2017 17:00:46 +0200 Subject: [PATCH 27/34] [FIX] point_of_sale: backport of a2319b4 + fix When a PoS order is closed, the payment and the sales journal item are not automatically reconciled. This potentially leaves a lot of unreconciled entries for a customer, which will appear in his follow-up report. This introduces an auto-reconciliation process upon order closing. The process restricts the reconciliation to an order individually, i.e. we don't try to cross-reconcile several orders for a customer. opw-697565 Add missing sudo to allow a user without accounting rights to close a session and reconcile entries. Closes #16517 --- addons/point_of_sale/models/pos_order.py | 14 ++++++++++++++ addons/point_of_sale/models/pos_session.py | 2 ++ 2 files changed, 16 insertions(+) diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index ec9f38a9890..35cc1340c8b 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -332,6 +332,20 @@ class PosOrder(models.Model): move.sudo().post() return True + def _reconcile_payments(self): + for order in self: + aml = order.statement_ids.mapped('journal_entry_ids').mapped('line_ids') | order.account_move.line_ids | order.invoice_id.move_id.line_ids + aml = aml.filtered(lambda r: not r.reconciled and r.account_id.internal_type == 'receivable' and r.partner_id == order.partner_id.commercial_partner_id) + try: + aml.reconcile() + except: + # There might be unexpected situations where the automatic reconciliation won't + # work. We don't want the user to be blocked because of this, since the automatic + # reconciliation is introduced for convenience, not for mandatory accounting + # reasons. + _logger.error('Reconciliation did not work for order %s', order.name) + continue + def _default_session(self): return self.env['pos.session'].search([('state', '=', 'opened'), ('user_id', '=', self.env.uid)], limit=1) diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index 9f2e9ddddd6..3c51f279bb2 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -27,6 +27,8 @@ class PosSession(models.Model): if order.state not in ('paid'): raise UserError(_("You cannot confirm all orders of this session, because they have not the 'paid' status")) order.action_pos_order_done() + orders = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done']) + orders.sudo()._reconcile_payments() config_id = fields.Many2one( 'pos.config', string='Point of Sale', From cd2b07ea44a58f576ce8ca95acca499267afdbbf Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Tue, 21 Mar 2017 16:04:56 +0100 Subject: [PATCH 28/34] [FIX] pad: remove wrong warning at pad creation When creating a pad before this change these were the steps: - modify pad URL for new pad URL - end of record loading This caused an issue because modifying the record at this point would mark it as dirty before displaying it (which would result in a erroneous warning "The record has been modified, your changes will be discarded. Are you sure you want to leave this page ?"). With this fix: - modifying the pad URL is done after the record is ready - deferred have been unified (to remove possible race conditions) closes #15239 opw-702797 forward port note: this change is needed up to saas-15, over saas-15 only a small part is needed (because x2many views are loaded with the root view and there is one widget instance per record). code note: it would be better to modify load_record of form view and delay `record_loaded` upto after x2many views are loaded, but possible impacts have been judged too risky so this is done only in pad widget. --- addons/pad/static/src/js/pad.js | 61 ++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js index 30c89d82819..cebe92055a7 100644 --- a/addons/pad/static/src/js/pad.js +++ b/addons/pad/static/src/js/pad.js @@ -17,7 +17,9 @@ var FieldPad = form_common.AbstractField.extend(form_common.ReinitializeWidgetMi event.preventDefault(); self.set("configured", true); }); - this.pad_loading_request = null; + // CHANGES ONLY NEEDED UNTIL SAAS-15 + // deferred for request getting pad content (readonly) or new pad url (edit) + this._pad_loading_deferred = null; }, initialize_content: function() { var self = this; @@ -36,14 +38,25 @@ var FieldPad = form_common.AbstractField.extend(form_common.ReinitializeWidgetMi }, render_value: function() { var self = this; - $.when(this._configured_deferred, this.pad_loading_request).always(function() { + $.when(this._configured_deferred).always(function() { if (!self.get('configured')){ return; } + + // reject previously ongoing _pad_loading_deferred + if (self._pad_loading_deferred !== null) { + self._pad_loading_deferred.reject(); + self.$('.oe_pad_content').removeClass('oe_pad_loading').html(''); + } + self._pad_loading_deferred = $.Deferred(); + // keep reference to current _pad_loading_deferred + var loading_def = self._pad_loading_deferred; + var value = self.get('value'); if (self.get('effective_readonly')) { if (_.str.startsWith(value, 'http')) { - self.pad_loading_request = self.view.dataset.call('pad_get_content', {url: value}).done(function(data) { + self.view.dataset.call('pad_get_content', {url: value}).then(loading_def.resolve, loading_def.reject); + loading_def.done(function(data) { self.$('.oe_pad_content').removeClass('oe_pad_loading').html('
'); self.$('.oe_pad_readonly').html(data); }).fail(function() { @@ -54,21 +67,37 @@ var FieldPad = form_common.AbstractField.extend(form_common.ReinitializeWidgetMi } } else { - var def = $.when(); + var def = $.Deferred(); if (! value || !_.str.startsWith(value, 'http')) { - def = self.view.dataset.call('pad_generate_url', { - context: { - model: self.view.model, - field_name: self.name, - object_id: self.view.datarecord.id - }, - }).then(function(data) { - if (! data.url) { - self.set("configured", false); - } else { - self.internal_set_value(data.url); - } + var deferreds = [ + self.view.dataset.call('pad_generate_url', { + context: { + model: self.view.model, + field_name: self.name, + object_id: self.view.datarecord.id + } + }), + // change record only after record_loaded and its call stack is finished + self.view.record_loaded.then(function() { + var call_stack_ended = $.Deferred(); + _.defer(call_stack_ended.resolve); + return call_stack_ended; + }) + ]; + // delay onchange after x2many views are loaded + deferreds = deferreds.concat(_.compact(_.pluck(self.view.fields, 'is_loaded'))); + $.when.apply($, deferreds).then(function(data) { + // update value only if loading_def has not been previously rejected + loading_def.resolve().done(function(){ + if (! data.url) { + self.set("configured", false); + } else { + self.internal_set_value(data.url); + } + }).then(def.resolve, def.reject); }); + } else { + def.resolve(); } def.then(function() { value = self.get('value'); From 551719a81e97807f8114c5f09a9115f7fbd8d4e6 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Wed, 5 Apr 2017 11:39:29 +0200 Subject: [PATCH 29/34] [FIX] mass_mailing: mail template not in english Mass mailing `mail templates` when selected in a mass mailing are always used in the english version. But when another language is involved the edition of mass mailing `templates` had a number of issue. For example saving the edited english would only modify the user current language. These issues are cause because the widget with snippets used for mass mailing templates edition is not compatible. Several fixes were proposed, what was choosen finally was to use the correct widget with the following behavior changes: - the snippets are not showed anymore in mass mailing templates, - the current language can be edited and is saved rightly, The shortcoming this give mass mailing templates can be partly compensated by: - editing the template back (if someone want the old behavior), - duplicating mass mailings which keep the snippets and translations, - doing a customization to improve the situation closes #16255 fixes #16165 opw-726746 opw-727232 note: this fix is only needed for 9.0 and saas-11. --- addons/mass_mailing/views/email_template.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mass_mailing/views/email_template.xml b/addons/mass_mailing/views/email_template.xml index 4f179c60983..570130efd9b 100644 --- a/addons/mass_mailing/views/email_template.xml +++ b/addons/mass_mailing/views/email_template.xml @@ -27,7 +27,7 @@ - + From 2a65b8156d79902ae95013622f55780fbfff3ad4 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 26 Apr 2017 19:36:10 +0200 Subject: [PATCH 30/34] [FIX] base: run defined modifiers tests --- openerp/addons/base/tests/test_views.py | 5 +++++ openerp/osv/orm.py | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/tests/test_views.py b/openerp/addons/base/tests/test_views.py index bd297d2789c..027a2d9262c 100644 --- a/openerp/addons/base/tests/test_views.py +++ b/openerp/addons/base/tests/test_views.py @@ -9,6 +9,7 @@ from lxml.builder import E from psycopg2 import IntegrityError +from openerp.osv.orm import modifiers_tests from openerp.exceptions import ValidationError from openerp.tests import common import openerp.tools @@ -854,6 +855,10 @@ class test_views(ViewCase): string="Replacement title", version="7.0" )) + def test_modifiers(self): + # implemeted elsewhere... + modifiers_tests() + class ViewModeField(ViewCase): """ This should probably, eventually, be folded back into other test case diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 835e29da7f9..be5c69d9a01 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -118,13 +118,13 @@ def test_modifiers(what, expected): node = etree.fromstring(what) transfer_node_to_modifiers(node, modifiers) simplify_modifiers(modifiers) - json = simplejson.dumps(modifiers) - assert json == expected, "%s != %s" % (json, expected) + dumped = simplejson.dumps(modifiers) + assert dumped == expected, "%s != %s" % (dumped, expected) elif isinstance(what, dict): transfer_field_to_modifiers(what, modifiers) simplify_modifiers(modifiers) - json = simplejson.dumps(modifiers) - assert json == expected, "%s != %s" % (json, expected) + dumped = simplejson.dumps(modifiers) + assert dumped == expected, "%s != %s" % (dumped, expected) # To use this test: From 11f8a2157df0a7be459571f44db0d2391d59b759 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 26 Apr 2017 19:45:27 +0200 Subject: [PATCH 31/34] [FIX] base: res.lang: do not redefine `_` variable --- openerp/addons/base/res/res_lang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py index fc45d18e420..75d72c8a363 100644 --- a/openerp/addons/base/res/res_lang.py +++ b/openerp/addons/base/res/res_lang.py @@ -269,7 +269,7 @@ class Lang(models.Model): if percent[-1] in 'eEfFgG': parts = formatted.split('.') - parts[0], _ = intersperse(parts[0], eval_lang_grouping, thousands_sep) + parts[0] = intersperse(parts[0], eval_lang_grouping, thousands_sep)[0] formatted = decimal_point.join(parts) From 75d9674d45968d948f682ebb0a3db99dee32024e Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Thu, 27 Apr 2017 08:02:02 +0200 Subject: [PATCH 32/34] [FIX] mass_mailing: revert typo forward port ed0233f This commit revert the change of 551719a8 forward ported in ed0233f. The file and feature modified is not present after saas-11. --- .../mass_mailing/views/mass_mailing_views.xml | 765 ------------------ 1 file changed, 765 deletions(-) delete mode 100644 addons/mass_mailing/views/mass_mailing_views.xml diff --git a/addons/mass_mailing/views/mass_mailing_views.xml b/addons/mass_mailing/views/mass_mailing_views.xml deleted file mode 100644 index adcd0599afa..00000000000 --- a/addons/mass_mailing/views/mass_mailing_views.xml +++ /dev/null @@ -1,765 +0,0 @@ - - - - - - - - - - - - - - List Subscription - mail.mass_mailing.contact - - {'group_by': ['create_date:month'], 'col_group_by': ['list_id']} - - - List Unsubscription - mail.mass_mailing.contact - - [('opt_out','=',True)] - {'group_by': ['unsubscription_date:month'], 'col_group_by': ['list_id']} - - - - mail.mass_mailing.contact.search - mail.mass_mailing.contact - - - - - - - - - - - - - - - - - - - mail.mass_mailing.contact.tree - mail.mass_mailing.contact - 10 - - - - - - - - - - - - - - - mail.mass_mailing.contact.graph - mail.mass_mailing.contact - 10 - - - - - - - - - - Mailing List Subscribers - mail.mass_mailing.contact - form - tree,graph - {'search_default_not_opt_out': 1} - - - - Recipients - mail.mass_mailing.contact - form - tree,form,graph - {'search_default_list_id': active_id, 'search_default_not_opt_out': 1} - -

- Click to create a recipient. -

-
-
- - - - - - mail.mass_mailing.list.search - mail.mass_mailing.list - - - - - - - - - - - - - - mail.mass_mailing.list.tree - mail.mass_mailing.list - 10 - - - - - - - - - - - mail.mass_mailing.list.form - mail.mass_mailing.list - -
- -
- - -
-
-
-
-
-
-
- - - Mailing Lists - mail.mass_mailing.list - form - tree,form - -

- Click here to create a new mailing list. -

- Mailing lists allows you to to manage customers and - contacts easily and to send to mailings in a single click. -

-
- - - - - - mail.mass_mailing.search - mail.mass_mailing - - - - - - - - - - - - - - - - - mail.mass_mailing.tree - mail.mass_mailing - 10 - - - - - - - - - - - - - - mail.mass_mailing.form - mail.mass_mailing - -
-
-
-
-

- - emails are in queue and will be sent soon. -

-
-
-

- - emails could not be sent. -

-
-
-

- This mass mailing is scheduled for - -

-
- -
- - - - - - - -
- - - - - - - - - - - - - - - - - - -
-
-
-
- - - mail.mass_mailing.kanban - mail.mass_mailing - - - - - - - - -
- -
-
-

-
-

- - -
-
-
- -
-
- Opened -
-
- % -
-
-
-
- Replied -
-
- % -
-
-
-
- Clicks -
-
- % -
-
-
- -
-
-
-
-
-
-
-
-
-
-
- - - mail.mass_mailing.graph - mail.mass_mailing - - - - - - - - - Mass Mailings - mail.mass_mailing - form - kanban,tree,form,graph - -

- Click here to create a new mailing. -

- Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. -

-
- - - Mass Mailings - mail.mass_mailing - form - kanban,tree,form - { - 'search_default_mass_mailing_campaign_id': [active_id], - 'default_mass_mailing_campaign_id': active_id, - } - - -

- Click here to create a new mailing. -

- Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. -

-
- - - Mass Mailings - mail.mass_mailing - form - form,kanban,tree - { - 'search_default_mass_mailing_campaign_id': [active_id], - 'default_mass_mailing_campaign_id': active_id, - } - - -

- Click here to create a new mailing. -

- Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. -

-
- - - Mass Mailings - mail.mass_mailing - form - form,kanban,tree - { - 'search_default_mass_mailing_campaign_id': [active_id], - 'default_mass_mailing_campaign_id': active_id, - 'default_contact_ab_pc': 10, - } - - -

- Click here to create a new mailing. -

- Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. -

-
- - - - - - mail.mass_mailing.stage.search - mail.mass_mailing.stage - - - - - - - - - mail.mass_mailing.stage.tree - mail.mass_mailing.stage - 10 - - - - - - - - - Mass Mailing Stages - mail.mass_mailing.stage - form - tree,form - - - - - - - - - mail.mass_mailing.tag.form - mail.mass_mailing.tag - -
- - - -
-
-
- - Campaign Tags - mail.mass_mailing.tag - form - -

- Click to add a new tag. -

-
-
- - - - - - - mail.mass_mailing.campaign.search - mail.mass_mailing.campaign - - - - - - - - - - - - - - - mail.mass_mailing.campaign.tree - mail.mass_mailing.campaign - 10 - - - - - - - - - - - - mail.mass_mailing.campaign.form - mail.mass_mailing.campaign - -
-
-
- - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - + +
+
+
+ + + + + + + Mailing Lists + mail.mass_mailing.list + form + tree,form + +

+ Click here to create a new mailing list. +

+ Mailing lists allows you to to manage customers and + contacts easily and to send to mailings in a single click. +

+
+ + + + + + mail.mass_mailing.search + mail.mass_mailing + + + + + + + + + + + + + + + + + mail.mass_mailing.tree + mail.mass_mailing + 10 + + + + + + + + + + + + + + mail.mass_mailing.form + mail.mass_mailing + +
+
+
+
+

+ + emails are in queue and will be sent soon. +

+
+
+

+ + emails could not be sent. +

+
+
+

+ This mass mailing is scheduled for + +

+
+ +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+
+
+
+ + + mail.mass_mailing.kanban + mail.mass_mailing + + + + + + + + +
+ +
+
+

+
+

+ - +
+
+
+ +
+
+ Opened +
+
+ % +
+
+
+
+ Replied +
+
+ % +
+
+
+
+ Clicks +
+
+ % +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + + mail.mass_mailing.graph + mail.mass_mailing + + + + + + + + + Mass Mailings + mail.mass_mailing + form + kanban,tree,form,graph + +

+ Click here to create a new mailing. +

+ Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. +

+
+ + + Mass Mailings + mail.mass_mailing + form + kanban,tree,form + { + 'search_default_mass_mailing_campaign_id': [active_id], + 'default_mass_mailing_campaign_id': active_id, + } + + +

+ Click here to create a new mailing. +

+ Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. +

+
+ + + Mass Mailings + mail.mass_mailing + form + form,kanban,tree + { + 'search_default_mass_mailing_campaign_id': [active_id], + 'default_mass_mailing_campaign_id': active_id, + } + + +

+ Click here to create a new mailing. +

+ Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. +

+
+ + + Mass Mailings + mail.mass_mailing + form + form,kanban,tree + { + 'search_default_mass_mailing_campaign_id': [active_id], + 'default_mass_mailing_campaign_id': active_id, + 'default_contact_ab_pc': 10, + } + + +

+ Click here to create a new mailing. +

+ Mass mailing allows you to to easily design and send mass mailings to your contacts, customers or leads using mailing lists. +

+
+ + + + + + mail.mass_mailing.stage.search + mail.mass_mailing.stage + + + + + + + + + mail.mass_mailing.stage.tree + mail.mass_mailing.stage + 10 + + + + + + + + + Mass Mailing Stages + mail.mass_mailing.stage + form + tree,form + + + + + + + + + mail.mass_mailing.tag.form + mail.mass_mailing.tag + +
+ + + +
+
+
+ + Campaign Tags + mail.mass_mailing.tag + form + +

+ Click to add a new tag. +

+
+
+ + + + + + + mail.mass_mailing.campaign.search + mail.mass_mailing.campaign + + + + + + + + + + + + + + + mail.mass_mailing.campaign.tree + mail.mass_mailing.campaign + 10 + + + + + + + + + + + + mail.mass_mailing.campaign.form + mail.mass_mailing.campaign + +
+
+
+ + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +