summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/account/data/invoice_action_data.xml2
-rw-r--r--addons/account/models/account.py19
-rw-r--r--addons/account/models/account_move.py2
-rw-r--r--addons/account/report/account_aged_partner_balance.py6
-rw-r--r--addons/account/static/src/js/account_reconciliation_widgets.js20
-rw-r--r--addons/barcodes/static/src/js/form_view_barcode_handler.js5
-rw-r--r--addons/crm/wizard/base_partner_merge.py2
-rw-r--r--addons/hr_expense/i18n/hr_expense.pot2
-rw-r--r--addons/hr_expense/models/hr_expense.py2
-rw-r--r--addons/hr_expense/views/hr_expense_views.xml4
-rw-r--r--addons/im_livechat/views/im_livechat_channel_templates.xml7
-rw-r--r--addons/project_issue/models/res_partner.py3
-rw-r--r--addons/project_issue/views/res_partner_views.xml2
-rw-r--r--addons/stock/models/stock_pack_operation.py16
-rw-r--r--addons/stock/models/stock_picking.py4
-rw-r--r--addons/stock/models/stock_warehouse.py2
-rw-r--r--addons/survey/views/survey_views.xml1
-rw-r--r--addons/web/static/src/js/views/list_view.js4
-rw-r--r--addons/web/static/src/js/views/tree_view.js2
-rw-r--r--addons/web_editor/static/src/js/snippets.editor.js5
-rw-r--r--addons/web_editor/static/src/xml/snippets.xml2
-rw-r--r--addons/web_kanban/static/src/js/kanban_view.js1
-rw-r--r--addons/website/static/src/js/website.snippets.gallery.js16
-rw-r--r--addons/website/views/snippets.xml1
-rw-r--r--addons/website_sale/controllers/main.py2
-rw-r--r--addons/website_sale/models/product.py3
-rw-r--r--odoo/addons/base/ir/ir_http.py2
-rw-r--r--odoo/addons/base/res/res_partner_view.xml1
-rw-r--r--odoo/addons/test_new_api/models.py9
-rw-r--r--odoo/addons/test_new_api/tests/test_onchange.py27
-rw-r--r--odoo/addons/test_new_api/views.xml1
-rw-r--r--odoo/fields.py10
-rw-r--r--odoo/modules/module.py2
-rw-r--r--odoo/tools/convert.py3
34 files changed, 142 insertions, 48 deletions
diff --git a/addons/account/data/invoice_action_data.xml b/addons/account/data/invoice_action_data.xml
index 96fa2342f06..96e55557c1e 100644
--- a/addons/account/data/invoice_action_data.xml
+++ b/addons/account/data/invoice_action_data.xml
@@ -33,7 +33,7 @@ amounting in <strong>${object.amount_total} ${object.currency_id.name}</strong>
from ${object.company_id.name}.
</p>
-% if is_inline:
+% if is_online:
<br/><br/>
<center>
<a href="${access_url}" style="background-color: #1abc9c; padding: 20px; text-decoration: none; color: #fff; border-radius: 5px; font-size: 16px;" class="o_default_snippet_text">View Invoice</a>
diff --git a/addons/account/models/account.py b/addons/account/models/account.py
index eb0b26b6963..15e25f1d9fb 100644
--- a/addons/account/models/account.py
+++ b/addons/account/models/account.py
@@ -699,7 +699,18 @@ class AccountTax(models.Model):
# the 'Account' decimal precision + 5), and that way it's like
# rounding after the sum of the tax amounts of each line
prec = currency.decimal_places
- if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)):
+
+ # In some cases, it is necessary to force/prevent the rounding of the tax and the total
+ # amounts. For example, in SO/PO line, we don't want to round the price unit at the
+ # precision of the currency.
+ # The context key 'round' allows to force the standard behavior.
+ round_tax = False if company_id.tax_calculation_rounding_method == 'round_globally' else True
+ round_total = True
+ if 'round' in self.env.context:
+ round_tax = bool(self.env.context['round'])
+ round_total = bool(self.env.context['round'])
+
+ if not round_tax:
prec += 5
total_excluded = total_included = base = round(price_unit * quantity, prec)
@@ -718,7 +729,7 @@ class AccountTax(models.Model):
continue
tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner)
- if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)):
+ if not round_tax:
tax_amount = round(tax_amount, prec)
else:
tax_amount = currency.round(tax_amount)
@@ -744,8 +755,8 @@ class AccountTax(models.Model):
return {
'taxes': sorted(taxes, key=lambda k: k['sequence']),
- 'total_excluded': currency.round(total_excluded) if bool(self.env.context.get("round", True)) else total_excluded,
- 'total_included': currency.round(total_included) if bool(self.env.context.get("round", True)) else total_included,
+ 'total_excluded': currency.round(total_excluded) if round_total else total_excluded,
+ 'total_included': currency.round(total_included) if round_total else total_included,
'base': base,
}
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 2b82cef6c85..8e4d908b176 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -1057,7 +1057,7 @@ class AccountMoveLine(models.Model):
taxes = self.env['account.tax'].browse(tax_ids)
currency = self.env['res.currency'].browse(vals.get('currency_id'))
partner = self.env['res.partner'].browse(vals.get('partner_id'))
- res = taxes.compute_all(amount,
+ res = taxes.with_context(round=True).compute_all(amount,
currency, 1, vals.get('product_id'), partner)
# Adjust line amount if any tax is price_include
if abs(res['total_excluded']) < abs(amount):
diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index cf4fe079eba..fb5f90f96ec 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -61,7 +61,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
# Build a string like (1,2,3) for easy use in SQL query
partner_ids = [partner['partner_id'] for partner in partners if partner['partner_id']]
- lines = dict([(partner['partner_id'], []) for partner in partners if partner['partner_id']])
+ lines = dict((partner['partner_id'] or False, []) for partner in partners)
if not partner_ids:
return [], [], []
@@ -80,7 +80,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
for line in self.env['account.move.line'].browse(aml_ids):
- partner_id = line.partner_id.id or None
+ partner_id = line.partner_id.id or False
if partner_id not in undue_amounts:
undue_amounts[partner_id] = 0.0
line_amount = line.balance
@@ -131,7 +131,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
aml_ids = cr.fetchall()
aml_ids = aml_ids and [x[0] for x in aml_ids] or []
for line in self.env['account.move.line'].browse(aml_ids):
- partner_id = line.partner_id.id or None
+ partner_id = line.partner_id.id or False
if partner_id not in partners_amount:
partners_amount[partner_id] = 0.0
line_amount = line.balance
diff --git a/addons/account/static/src/js/account_reconciliation_widgets.js b/addons/account/static/src/js/account_reconciliation_widgets.js
index 848b9b5093a..95cd682a9ee 100644
--- a/addons/account/static/src/js/account_reconciliation_widgets.js
+++ b/addons/account/static/src/js/account_reconciliation_widgets.js
@@ -16,6 +16,7 @@ var pyeval = require('web.pyeval');
var web_client = require('web.web_client');
var parse_value = require('web.web_client');
var Widget = require('web.Widget');
+var session = require('web.session');
var FieldMany2One = core.form_widget_registry.get('many2one');
var FieldChar = core.form_widget_registry.get('char');
@@ -71,8 +72,6 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, {
this.action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof ActionManager });
this.crash_manager = new CrashManager();
- // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more
- this.monetaryIsZero = _.partial(utils.float_is_zero, _, 4);
this.formatCurrencies; // Method that formats the currency ; loaded from the server
this.model_res_users = new Model("res.users");
this.model_tax = new Model("account.tax");
@@ -365,6 +364,11 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, {
if (line.ref && line.ref !== line.name)
line.q_label = line.q_label + " : " + line.ref;
},
+
+ // Method that tests if a monetary amount == 0, we use 4 digits because no currency uses more
+ monetaryIsZero: function(amount, digits) {
+ return utils.float_is_zero(amount, digits === undefined ? 4 : digits);
+ },
});
var abstractReconciliationLine = Widget.extend({
@@ -1868,7 +1872,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({
// Find out if the counterpart is lower than, equal or greater than the transaction being reconciled
var balance_type = undefined;
- if (self.monetaryIsZero(self.get("balance"))) balance_type = "equal";
+ var digits = 4;
+ if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) {
+ digits = session.get_currency(this.get("currency_id")).digits[1];
+ }
+ if (self.monetaryIsZero(self.get("balance"), digits)) balance_type = "equal";
else if (self.get("balance") * self.st_line.amount > 0) balance_type = "greater";
else if (self.get("balance") * self.st_line.amount < 0) balance_type = "lower";
@@ -2063,7 +2071,11 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({
var payment_aml = _.filter(this.get("mv_lines_selected"), function(line) { return line.already_paid });
var payment_aml_ids = _.collect(payment_aml, function(line) { return line.id });
var new_aml_dicts = this.prepareCreatedMoveLinesForPersisting(this.getCreatedLines());
- if (! self.monetaryIsZero(this.get("balance"))) new_aml_dicts.push(this.prepareOpenBalanceForPersisting());
+ var digits = 4;
+ if (this.get("currency_id") && session.get_currency(this.get("currency_id"))) {
+ digits = session.get_currency(this.get("currency_id")).digits[1];
+ }
+ if (! self.monetaryIsZero(this.get("balance"), digits)) new_aml_dicts.push(this.prepareOpenBalanceForPersisting());
return {
'counterpart_aml_dicts': counterpart_aml_dicts,
'payment_aml_ids': payment_aml_ids,
diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js
index a6fa4b55adf..b00bce51c5e 100644
--- a/addons/barcodes/static/src/js/form_view_barcode_handler.js
+++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js
@@ -52,6 +52,11 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
}
},
+ destroy: function () {
+ this.stop_listening();
+ this._super.apply(this, arguments);
+ },
+
_display_no_edit_mode_warning: function() {
this.do_warn(_t('Error : Document not editable'), _t('To modify this document, please first start edition.'));
},
diff --git a/addons/crm/wizard/base_partner_merge.py b/addons/crm/wizard/base_partner_merge.py
index d2ddf32afcf..44320807797 100644
--- a/addons/crm/wizard/base_partner_merge.py
+++ b/addons/crm/wizard/base_partner_merge.py
@@ -306,7 +306,7 @@ class MergePartnerAutomatic(models.TransientModel):
raise UserError(_("You cannot merge a contact with one of his parent."))
# check only admin can merge partners with different emails
- if SUPERUSER_ID != self.env.uid and len(set(partner.email for partner in Partner.browse(partner_ids))) > 1:
+ if SUPERUSER_ID != self.env.uid and len(set(partner.email for partner in partner_ids)) > 1:
raise UserError(_("All contacts must have the same email. Only the Administrator can merge contacts with different emails."))
# remove dst_partner from partners to merge
diff --git a/addons/hr_expense/i18n/hr_expense.pot b/addons/hr_expense/i18n/hr_expense.pot
index 78f13f7787e..edc3c84a67d 100644
--- a/addons/hr_expense/i18n/hr_expense.pot
+++ b/addons/hr_expense/i18n/hr_expense.pot
@@ -1156,7 +1156,7 @@ msgstr ""
#. module: hr_expense
#: selection:hr.expense,state:0
#: model:ir.ui.view,arch_db:hr_expense.view_hr_expense_filter
-msgid "To Report"
+msgid "To Submit"
msgstr ""
#. module: hr_expense
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index 9b4367f2992..84886443a37 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -35,7 +35,7 @@ class HrExpense(models.Model):
payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By")
attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
state = fields.Selection([
- ('draft', 'To Report'),
+ ('draft', 'To Submit'),
('reported', 'Reported'),
('done', 'Posted'),
('refused', 'Refused')
diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml
index 22073f88003..3c35430bf39 100644
--- a/addons/hr_expense/views/hr_expense_views.xml
+++ b/addons/hr_expense/views/hr_expense_views.xml
@@ -56,7 +56,7 @@
<field name="quantity" class="oe_inline"/>
<field name="product_uom_id" widget="selection" class="oe_inline" groups="product.group_uom"/>
</div>
- <field name="tax_ids" domain="[('type_tax_use', '=', 'purchase')]" widget="many2many_tags" groups="account.group_no_one"/>
+ <field name="tax_ids" domain="[('type_tax_use', '=', 'purchase')]" widget="many2many_tags" groups="base.group_no_one"/>
</group><group>
<field name="reference"/>
<field name="date"/>
@@ -161,7 +161,7 @@
<field name="product_id"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
<separator />
- <filter domain="[('state', '=', 'draft')]" string="To Report" name="to_report" help="New Expense"/>
+ <filter domain="[('state', '=', 'draft')]" string="To Submit" name="to_report" help="New Expense"/>
<filter domain="[('state', '=', 'report')]" string="Reported" name="submitted" help="Confirmed Expenses"/>
<filter domain="[('state', '=', 'refused')]" string="Refused" name="refused" help="Refused Expenses"/>
<filter domain="[('state', '!=', 'cancel')]" string="Not Refused" name="uncancelled" help="Actual expense sheets, not the refused ones"/>
diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml
index bbb4c9663b6..0e3a4b911c8 100644
--- a/addons/im_livechat/views/im_livechat_channel_templates.xml
+++ b/addons/im_livechat/views/im_livechat_channel_templates.xml
@@ -112,6 +112,7 @@
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
<!-- Odoo JS Framework -->
<script type="text/javascript" src="/web/static/src/js/boot.js"></script>
+ <script type="text/javascript" src="/web/static/src/js/config.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/class.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/translation.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/ajax.js"></script>
@@ -121,7 +122,11 @@
<script type="text/javascript" src="/web/static/src/js/framework/registry.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/session.js"></script>
<script type="text/javascript" src="/web/static/src/js/framework/utils.js"></script>
- <script type="text/javascript" src="/web/static/src/js/framework/core.js"></script>
+ <script type="text/javascript" src="/web/static/src/js/framework/bus.js"></script>
+ <script type="text/javascript" src="/web/static/src/js/framework/local_storage.js"></script>
+ <script type="text/javascript" src="/web/static/src/js/framework/qweb.js"></script>
+ <script type="text/javascript" src="/web/static/src/js/services/core.js"></script>
+ <script type="text/javascript" src="/web/static/src/js/services/session.js"></script>
<!-- Bus, Mail, Livechat -->
<script type="text/javascript" src="/bus/static/src/js/bus.js"></script>
<script type="text/javascript" src="/mail/static/src/js/chat_window.js"></script>
diff --git a/addons/project_issue/models/res_partner.py b/addons/project_issue/models/res_partner.py
index 8bfe514987f..91dd0af3eec 100644
--- a/addons/project_issue/models/res_partner.py
+++ b/addons/project_issue/models/res_partner.py
@@ -11,6 +11,5 @@ class ResPartner(models.Model):
def _compute_issue_count(self):
Issue = self.env['project.issue']
- partners = {id: self.search([('id', 'child_of', self.ids)]).ids for id in self.ids}
for partner in self:
- partner.issue_count = Issue.search_count([('partner_id', 'in', partners[partner.id])])
+ partner.issue_count = Issue.search_count([('partner_id', 'child_of', partner.commercial_partner_id.id)])
diff --git a/addons/project_issue/views/res_partner_views.xml b/addons/project_issue/views/res_partner_views.xml
index aa2ac8a80b5..10aafb7d24f 100644
--- a/addons/project_issue/views/res_partner_views.xml
+++ b/addons/project_issue/views/res_partner_views.xml
@@ -10,7 +10,7 @@
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button class="oe_stat_button" type="action" name="%(action_view_issues)d"
- context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}"
+ context="{'search_default_partner_id': commercial_partner_id, 'default_partner_id': active_id}"
attrs="{'invisible': [('customer', '=', False)]}"
icon="fa-bug">
<field name="issue_count" string="Issues" widget="statinfo"/>
diff --git a/addons/stock/models/stock_pack_operation.py b/addons/stock/models/stock_pack_operation.py
index a581d4028d3..01a24de273d 100644
--- a/addons/stock/models/stock_pack_operation.py
+++ b/addons/stock/models/stock_pack_operation.py
@@ -164,8 +164,9 @@ class PackOperation(models.Model):
def split_quantities(self):
for operation in self:
if float_compare(operation.product_qty, operation.qty_done, precision_rounding=operation.product_uom_id.rounding) == 1:
- operation.copy(default={'qty_done': 0.0, 'product_qty': operation.product_qty - operation.qty_done})
+ cpy = operation.copy(default={'qty_done': 0.0, 'product_qty': operation.product_qty - operation.qty_done})
operation.write({'product_qty': operation.qty_done})
+ operation._copy_remaining_pack_lot_ids(cpy)
else:
raise UserError(_('The quantity to split should be smaller than the quantity To Do. '))
return True
@@ -238,6 +239,19 @@ class PackOperation(models.Model):
raise UserError(_('You should provide a different serial number for each piece'))
check_tracking = _check_serial_number
+ @api.multi
+ def _copy_remaining_pack_lot_ids(self, new_operation):
+ for op in self:
+ for lot in op.pack_lot_ids:
+ new_qty_todo = lot.qty_todo - lot.qty
+
+ if float_compare(new_qty_todo, 0, precision_rounding=op.product_uom_id.rounding) > 0:
+ lot.copy({
+ 'operation_id': new_operation.id,
+ 'qty_todo': new_qty_todo,
+ 'qty': 0,
+ })
+
class PackOperationLot(models.Model):
_name = "stock.pack.operation.lot"
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index e2cdafe0ce1..7ae10fe6bfc 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -999,6 +999,10 @@ class Picking(models.Model):
packlots_transfer = [(4, x.id) for x in operation.pack_lot_ids]
new_operation.write({'pack_lot_ids': packlots_transfer})
+ # the stock.pack.operation.lot records now belong to the new, packaged stock.pack.operation
+ # we have to create new ones with new quantities for our original, unfinished stock.pack.operation
+ new_operation._copy_remaining_pack_lot_ids(operation)
+
op = new_operation
pack_operation_ids |= op
if operations:
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index 051bc2774a6..900e7f18309 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -808,7 +808,7 @@ class Orderpoint(models.Model):
def _get_date_planned(self, start_date):
days = self.lead_days or 0.0
- if self.lead_type == 'purchase':
+ if self.lead_type == 'supplier':
# These days will be substracted when creating the PO
days += self.product_id._select_seller().delay or 0.0
date_planned = start_date + relativedelta.relativedelta(days=days)
diff --git a/addons/survey/views/survey_views.xml b/addons/survey/views/survey_views.xml
index 07655717df5..7ebe9a72f22 100644
--- a/addons/survey/views/survey_views.xml
+++ b/addons/survey/views/survey_views.xml
@@ -237,6 +237,7 @@
<field name="model">survey.survey</field>
<field name="arch" type="xml">
<search string="Survey">
+ <field string="Title" name="title"/>
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
</search>
</field>
diff --git a/addons/web/static/src/js/views/list_view.js b/addons/web/static/src/js/views/list_view.js
index 22ca3e0b1ad..327edcbb052 100644
--- a/addons/web/static/src/js/views/list_view.js
+++ b/addons/web/static/src/js/views/list_view.js
@@ -1092,8 +1092,8 @@ ListView.List = Class.extend({
_(names).pluck(1).join(', '));
record.set(column.id, ids);
});
- // temp empty value
- record.set(column.id, false);
+ // temporary empty display name
+ record.set(column.id + '__display', false);
}
}
return column.format(record.toForm().data, {
diff --git a/addons/web/static/src/js/views/tree_view.js b/addons/web/static/src/js/views/tree_view.js
index aeb206257fe..604c9204b42 100644
--- a/addons/web/static/src/js/views/tree_view.js
+++ b/addons/web/static/src/js/views/tree_view.js
@@ -49,7 +49,7 @@ var TreeView = View.extend({
},
willStart: function () {
_(this.fields_view.arch.children).each(function (field) {
- if (field.attrs.modifiers) {
+ if (field.attrs.modifiers && typeof field.attrs.modifiers === "string") {
field.attrs.modifiers = JSON.parse(field.attrs.modifiers);
}
});
diff --git a/addons/web_editor/static/src/js/snippets.editor.js b/addons/web_editor/static/src/js/snippets.editor.js
index 4906ab345d0..3fd9bfca2e4 100644
--- a/addons/web_editor/static/src/js/snippets.editor.js
+++ b/addons/web_editor/static/src/js/snippets.editor.js
@@ -287,6 +287,9 @@ data.Class = Widget.extend({
self.make_snippet_draggable(self.$snippets);
this.associate_snippet_names(this.$snippets);
+
+ this.show_blocks();
+ this.$el.on("snippet-dropped snippet-removed", this.show_blocks.bind(this));
},
associate_snippet_names: function ($snippets) {
@@ -1052,6 +1055,8 @@ data.Editor = Class.extend({
$(".note-control-selection").hide();
$('.o_table_handler').remove();
+ this.buildingBlock.$el.trigger("snippet-removed");
+
return false;
},
diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml
index 2b9aaf15b32..d9e8c83a495 100644
--- a/addons/web_editor/static/src/xml/snippets.xml
+++ b/addons/web_editor/static/src/xml/snippets.xml
@@ -60,7 +60,7 @@
<!-- t-field options -->
<t t-name="web_editor.many2one.button">
- <div class="dropdown">
+ <div class="btn-group">
<a href="#" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" title="Search Contact"><i class="fa fa-search"></i></a>
<ul class="dropdown-menu contact_menu" role="menu">
<li><a><input href="#" type="email" placeholder="Search"/></a></li>
diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js
index 5de3c617758..b755c7559dd 100644
--- a/addons/web_kanban/static/src/js/kanban_view.js
+++ b/addons/web_kanban/static/src/js/kanban_view.js
@@ -331,6 +331,7 @@ var KanbanView = View.extend({
// Execute the given action
self.do_action(self.on_create, {
on_close: self.do_reload.bind(self),
+ additional_context: self.search_context,
});
} else {
// Open the form view
diff --git a/addons/website/static/src/js/website.snippets.gallery.js b/addons/website/static/src/js/website.snippets.gallery.js
index ed1293229b8..481ba06e6f3 100644
--- a/addons/website/static/src/js/website.snippets.gallery.js
+++ b/addons/website/static/src/js/website.snippets.gallery.js
@@ -354,18 +354,12 @@ options.registry.gallery_img = options.Class.extend({
editor.reapply();
},0);
},
- remove: function (type) {
- if (type !== "click") return;
-
- var self = this;
+ on_remove: function () {
var $parent = snippet_editor.globalSelector.closest(this.$target.parent());
- this.buildingBlock.make_active(false);
- this.$target.remove();
- setTimeout(function () {
- self.buildingBlock.make_active($parent);
- var gallery = $parent.data('snippet-editor').styles.gallery;
- gallery.reapply();
- }, 0);
+ _.defer((function () {
+ this.buildingBlock.make_active($parent);
+ $parent.data('snippet-editor').styles.gallery.reapply();
+ }).bind(this));
}
});
diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml
index 10de2c6ae18..95ec8613786 100644
--- a/addons/website/views/snippets.xml
+++ b/addons/website/views/snippets.xml
@@ -775,7 +775,6 @@
<li data-position="last"><a href="#">Move to last</a></li>
</ul>
</li>
- <li data-remove="remove"><a href="#"><i class="fa fa-trash-o"/>Remove from gallery</a></li>
</div>
<div data-js='carousel'
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 7eef41bab37..03c00f09d87 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -128,7 +128,7 @@ class WebsiteSale(http.Controller):
(variant id, [visible attribute ids], variant price, variant sale price)
"""
# product attributes with at least two choices
- visible_attrs_ids = product.mapped('attribute_line_ids.attribute_id').filtered(lambda attr: len(attr.value_ids) > 1).ids
+ visible_attrs_ids = product.attribute_line_ids.filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id').ids
to_currency = request.website.get_current_pricelist().currency_id
attribute_value_ids = []
for variant in product.product_variant_ids:
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index c436350c809..17516246a2b 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, tools, _
import odoo.addons.decimal_precision as dp
+from odoo.tools.translate import html_translate
class ProductStyle(models.Model):
@@ -108,7 +109,7 @@ class ProductTemplate(models.Model):
domain=lambda self: ['&', ('model', '=', self._name), ('message_type', '=', 'comment')],
string='Website Comments',
)
- website_description = fields.Html('Description for the website', sanitize_attributes=False, translate=True)
+ website_description = fields.Html('Description for the website', sanitize_attributes=False, translate=html_translate)
alternative_product_ids = fields.Many2many('product.template', 'product_alternative_rel', 'src_id', 'dest_id',
string='Alternative Products', help='Suggest more expensive alternatives to '
'your customers (upsell strategy). Those products show up on the product page.')
diff --git a/odoo/addons/base/ir/ir_http.py b/odoo/addons/base/ir/ir_http.py
index c63bd2f7226..6bca1edee58 100644
--- a/odoo/addons/base/ir/ir_http.py
+++ b/odoo/addons/base/ir/ir_http.py
@@ -324,7 +324,7 @@ class IrHttp(models.AbstractModel):
# cache
etag = hasattr(request, 'httprequest') and request.httprequest.headers.get('If-None-Match')
- retag = hashlib.md5(last_update).hexdigest()
+ retag = '"%s"' % hashlib.md5(last_update).hexdigest()
status = status or (304 if etag == retag else 200)
headers.append(('ETag', retag))
headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))
diff --git a/odoo/addons/base/res/res_partner_view.xml b/odoo/addons/base/res/res_partner_view.xml
index f5d1e819ded..92d4c41db55 100644
--- a/odoo/addons/base/res/res_partner_view.xml
+++ b/odoo/addons/base/res/res_partner_view.xml
@@ -140,6 +140,7 @@
<field name="image" widget='image' class="oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/>
<div class="oe_title">
<field name="is_company" invisible="1"/>
+ <field name="commercial_partner_id" invisible="1"/>
<field name="company_type" widget="radio"
class="oe_edit_only"
options="{'horizontal': true}"/>
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index 513fe06ca8c..5fc58ea976a 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -13,6 +13,7 @@ class Category(models.Model):
name = fields.Char(required=True)
color = fields.Integer('Color Index')
parent = fields.Many2one('test_new_api.category')
+ root_categ = fields.Many2one(_name, compute='_compute_root_categ')
display_name = fields.Char(compute='_compute_display_name', inverse='_inverse_display_name')
dummy = fields.Char(store=False)
discussions = fields.Many2many('test_new_api.discussion', 'test_new_api_discussion_category',
@@ -26,6 +27,14 @@ class Category(models.Model):
else:
self.display_name = self.name
+ @api.depends('parent')
+ def _compute_root_categ(self):
+ for cat in self:
+ current = cat
+ while current.parent:
+ current = current.parent
+ cat.root_categ = current
+
@api.one
def _inverse_display_name(self):
names = self.display_name.split('/')
diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py
index 16d122c712c..87fc8229b7c 100644
--- a/odoo/addons/test_new_api/tests/test_onchange.py
+++ b/odoo/addons/test_new_api/tests/test_onchange.py
@@ -75,6 +75,33 @@ class TestOnChange(common.TransactionCase):
result = self.Message.onchange(values, 'body', field_onchange)
self.assertNotIn('name', result['value'])
+ def test_onchange_many2one(self):
+ Category = self.env['test_new_api.category']
+
+ field_onchange = Category._onchange_spec()
+ self.assertEqual(field_onchange.get('parent'), '1')
+
+ root = Category.create(dict(name='root'))
+
+ values = {
+ 'name': 'test',
+ 'parent': root.id,
+ 'root_categ': False,
+ }
+
+ self.env.invalidate_all()
+ result = Category.onchange(values, 'parent', field_onchange).get('value', {})
+ self.assertIn('root_categ', result)
+ self.assertEqual(result['root_categ'], root.name_get()[0])
+
+ values.update(result)
+ values['parent'] = False
+
+ self.env.invalidate_all()
+ result = Category.onchange(values, 'parent', field_onchange).get('value', {})
+ self.assertIn('root_categ', result)
+ self.assertIs(result['root_categ'], False)
+
def test_onchange_one2many(self):
""" test the effect of onchange() on one2many fields """
BODY = "What a beautiful day!"
diff --git a/odoo/addons/test_new_api/views.xml b/odoo/addons/test_new_api/views.xml
index b8c0f5bfa8b..0b053635319 100644
--- a/odoo/addons/test_new_api/views.xml
+++ b/odoo/addons/test_new_api/views.xml
@@ -205,6 +205,7 @@
<group>
<field name="name"/>
<field name="parent"/>
+ <field name="root_categ"/>
<field name="dummy"/>
</group>
</sheet>
diff --git a/odoo/fields.py b/odoo/fields.py
index 4d3f43f0fa7..d6dfe499f28 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -1898,6 +1898,10 @@ class Many2one(_Relational):
def convert_to_display_name(self, value, record):
return ustr(value.display_name)
+ def convert_to_onchange(self, value, record, fnames=()):
+ if not value.id:
+ return False
+ return super(Many2one, self).convert_to_onchange(value, record, fnames)
class UnionUpdate(SpecialValue):
""" Placeholder for a value update; when this value is taken from the cache,
@@ -1923,10 +1927,10 @@ class _RelationalMulti(_Relational):
""" Update the cached value of ``self`` for ``records`` with ``value``. """
for record in records:
if self in record._cache:
- value = self.convert_to_cache(record[self.name] | value, record, validate=False)
+ val = self.convert_to_cache(record[self.name] | value, record, validate=False)
else:
- value = UnionUpdate(self, record, value)
- record._cache[self] = value
+ val = UnionUpdate(self, record, value)
+ record._cache[self] = val
def convert_to_cache(self, value, record, validate=True):
# cache format: tuple(ids)
diff --git a/odoo/modules/module.py b/odoo/modules/module.py
index 66cbae460f0..1ff0d2d0ed1 100644
--- a/odoo/modules/module.py
+++ b/odoo/modules/module.py
@@ -156,7 +156,7 @@ def get_module_path(module, downloaded=False, display_warning=True):
"""
initialize_sys_path()
for adp in ad_paths:
- if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)):
+ if os.path.exists(opj(adp, module, MANIFEST)) or os.path.exists(opj(adp, '%s.zip' % module)):
return opj(adp, module)
if downloaded:
diff --git a/odoo/tools/convert.py b/odoo/tools/convert.py
index 089210d6f99..3c3eba3bec6 100644
--- a/odoo/tools/convert.py
+++ b/odoo/tools/convert.py
@@ -746,7 +746,8 @@ form: module.record_id""" % (xml_id,)
record.append(Field(name, name='name'))
record.append(Field(full_tpl_id, name='key'))
record.append(Field("qweb", name='type'))
- record.append(Field(el.get('priority', "16"), name='priority'))
+ if 'priority' in el.attrib:
+ record.append(Field(el.get('priority'), name='priority'))
if 'inherit_id' in el.attrib:
record.append(Field(name='inherit_id', ref=el.get('inherit_id')))
if 'website_id' in el.attrib: