summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-04-09 20:48:01 +0200
committerChristophe Simonis <chs@odoo.com>2019-04-09 20:48:01 +0200
commitc9e1deb496e97fca22991b788ad36f753ecb1fc5 (patch)
tree78689bb9e5e029c99a2f1690e7e9d8e425734a26 /addons
parent6c8e30cae6ca5e4fe12e287ebd29d76544be57a3 (diff)
parentaf313858b4bfcaa726c4d08057a1ff1db1ade480 (diff)
[MERGE] forward port branch saas-12.1 up to af313858b4b
Diffstat (limited to 'addons')
-rw-r--r--addons/account/security/account_security.xml22
-rw-r--r--addons/account/views/report_invoice.xml4
-rw-r--r--addons/board/static/src/scss/dashboard.scss4
-rw-r--r--addons/calendar/models/res_partner.py2
-rw-r--r--addons/mail/models/ir_attachment.py20
-rw-r--r--addons/mail/static/src/js/chatter.js4
-rw-r--r--addons/product/models/product_template.py9
-rw-r--r--addons/product/tests/test_product_attribute_value_config.py43
-rw-r--r--addons/sale/report/sale_report_templates.xml4
-rw-r--r--addons/sale/views/sale_portal_templates.xml4
-rw-r--r--addons/sale_management/models/sale_order.py3
-rw-r--r--addons/sale_management/models/sale_order_template.py2
-rw-r--r--addons/sale_management/views/sale_portal_templates.xml1
-rw-r--r--addons/stock/report/report_stockinventory.xml1
-rw-r--r--addons/stock_account/views/product_views.xml2
-rw-r--r--addons/survey/static/src/js/survey.js3
-rw-r--r--addons/web/static/src/js/views/list/list_editable_renderer.js21
-rw-r--r--addons/web/static/tests/views/list_tests.js122
18 files changed, 244 insertions, 27 deletions
diff --git a/addons/account/security/account_security.xml b/addons/account/security/account_security.xml
index 69f3fca8bc8..b7ec99e56c0 100644
--- a/addons/account/security/account_security.xml
+++ b/addons/account/security/account_security.xml
@@ -20,18 +20,6 @@
<field name="category_id" ref="base.module_category_hidden"/>
</record>
- <record id="base.group_public" model="res.groups">
- <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/>
- </record>
-
- <record id="base.group_portal" model="res.groups">
- <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/>
- </record>
-
- <record id="base.group_user" model="res.groups">
- <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/>
- </record>
-
<record id="group_account_user" model="res.groups">
<field name="name">Show Full Accounting Features</field>
<field name="category_id" ref="base.module_category_hidden"/>
@@ -75,6 +63,16 @@
<data noupdate="1">
+ <record id="base.group_public" model="res.groups">
+ <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/>
+ </record>
+ <record id="base.group_portal" model="res.groups">
+ <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/>
+ </record>
+ <record id="base.group_user" model="res.groups">
+ <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/>
+ </record>
+
<record id="base.default_user" model="res.users">
<field name="groups_id" eval="[(4,ref('account.group_account_manager'))]"/>
</record>
diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml
index 043c5a3cc47..8c3da3ae101 100644
--- a/addons/account/views/report_invoice.xml
+++ b/addons/account/views/report_invoice.xml
@@ -51,12 +51,16 @@
<table class="table table-sm o_main_table" name="invoice_line_table">
<thead>
<tr>
+ <!-- TODO: remove in master -->
+ <t t-set="colspan" t-value="6"/>
<th class="text-left"><span>Description</span></th>
<th class="d-none text-left"><span>Source Document</span></th>
<th class="text-right"><span>Quantity</span></th>
<th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Unit Price</span></th>
<th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
<span>Disc.(%)</span>
+ <!-- TODO: remove in master -->
+ <t t-set="colspan" t-value="colspan+1"/>
</th>
<th t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Taxes</span></th>
<th class="text-right">
diff --git a/addons/board/static/src/scss/dashboard.scss b/addons/board/static/src/scss/dashboard.scss
index d5d95fa9f59..518849cde0a 100644
--- a/addons/board/static/src/scss/dashboard.scss
+++ b/addons/board/static/src/scss/dashboard.scss
@@ -123,8 +123,8 @@
}
// Override height of graph. min-height doesn't do the trick
- .o_graph_controller {
- height: 500px;
+ .o_graph_svg_container {
+ height: 250px;
}
}
}
diff --git a/addons/calendar/models/res_partner.py b/addons/calendar/models/res_partner.py
index 403d70b18fc..261b955fc8f 100644
--- a/addons/calendar/models/res_partner.py
+++ b/addons/calendar/models/res_partner.py
@@ -11,7 +11,7 @@ from odoo.addons.calendar.models.calendar import get_real_ids
class Partner(models.Model):
_inherit = 'res.partner'
- calendar_last_notif_ack = fields.Datetime('Last notification marked as read from base Calendar')
+ calendar_last_notif_ack = fields.Datetime('Last notification marked as read from base Calendar', default=fields.Datetime.now)
@api.multi
def get_attendee_detail(self, meeting_id):
diff --git a/addons/mail/models/ir_attachment.py b/addons/mail/models/ir_attachment.py
index 4330059c495..527b22409d6 100644
--- a/addons/mail/models/ir_attachment.py
+++ b/addons/mail/models/ir_attachment.py
@@ -13,9 +13,22 @@ class IrAttachment(models.Model):
"""
super(IrAttachment, self)._post_add_create()
for record in self:
- record.register_as_main_attachment()
+ record.register_as_main_attachment(force=False)
- def register_as_main_attachment(self):
+ @api.multi
+ def unlink(self):
+ self.remove_as_main_attachment()
+ super(IrAttachment, self).unlink()
+
+ @api.multi
+ def remove_as_main_attachment(self):
+ for attachment in self:
+ related_record = self.env[attachment.res_model].browse(attachment.res_id)
+ if related_record and hasattr(related_record, 'message_main_attachment_id'):
+ if related_record.message_main_attachment_id == attachment:
+ related_record.message_main_attachment_id = False
+
+ def register_as_main_attachment(self, force=True):
""" Registers this attachment as the main one of the model it is
attached to.
"""
@@ -24,4 +37,5 @@ class IrAttachment(models.Model):
# message_main_attachment_id field can be empty, that's why we compare to False;
# we are just checking that it exists on the model before writing it
if related_record and hasattr(related_record, 'message_main_attachment_id'):
- related_record.message_main_attachment_id = self
+ if force or not related_record.message_main_attachment_id:
+ related_record.message_main_attachment_id = self
diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js
index 72971fbeee9..5f9b11198e3 100644
--- a/addons/mail/static/src/js/chatter.js
+++ b/addons/mail/static/src/js/chatter.js
@@ -81,6 +81,7 @@ var Chatter = Widget.extend({
var nodeOptions = fieldsInfo[mailFields.mail_thread].options || {};
this.hasLogButton = options.display_log_button || nodeOptions.display_log_button;
this.postRefresh = nodeOptions.post_refresh || 'never';
+ this.reloadOnUploadAttachment = this.postRefresh === 'always';
}
},
/**
@@ -572,6 +573,9 @@ var Chatter = Widget.extend({
* @private
*/
_onReloadAttachmentBox: function () {
+ if (this.reloadOnUploadAttachment) {
+ this.trigger_up('reload');
+ }
this._reloadAttachmentBox();
},
/**
diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py
index fbdf5f37b33..5fc9bed772a 100644
--- a/addons/product/models/product_template.py
+++ b/addons/product/models/product_template.py
@@ -518,6 +518,7 @@ class ProductTemplate(models.Model):
variants_to_create.append({
'product_tmpl_id': tmpl_id.id,
'attribute_value_ids': [(6, 0, list(value_ids))],
+ 'active': tmpl_id.active,
})
if len(variants_to_create) > 1000:
raise UserError(_(
@@ -957,8 +958,10 @@ class ProductTemplate(models.Model):
if not self.active:
return _("The product template is archived so no combination is possible.")
- ptal_stack = [self._get_valid_product_template_attribute_lines()]
- combination_stack = [self.env['product.template.attribute.value']]
+ necessary_values = necessary_values or self.env['product.template.attribute.value']
+ necessary_attributes = necessary_values.mapped('attribute_id')
+ ptal_stack = [self.valid_product_template_attribute_line_ids.filtered(lambda ptal: ptal.attribute_id not in necessary_attributes)]
+ combination_stack = [necessary_values]
# keep going while we have attribute lines to test
while len(ptal_stack):
@@ -967,7 +970,7 @@ class ProductTemplate(models.Model):
if not attribute_lines:
# full combination, if it's possible return it, otherwise skip it
- if self._is_combination_possible(combination, parent_combination) and all(v in combination for v in (necessary_values or [])):
+ if self._is_combination_possible(combination, parent_combination):
yield(combination)
else:
# we have remaining attribute lines to consider
diff --git a/addons/product/tests/test_product_attribute_value_config.py b/addons/product/tests/test_product_attribute_value_config.py
index aa1de67d16b..e966adbdbdc 100644
--- a/addons/product/tests/test_product_attribute_value_config.py
+++ b/addons/product/tests/test_product_attribute_value_config.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import time
+
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
@@ -423,6 +425,47 @@ class TestProductAttributeValueConfig(TestProductAttributeValueSetup):
self.assertEqual(self.computer._get_closest_possible_combination(computer_ssd_256 + computer_ram_8 + computer_hdd_4 + computer_hdd_2),
computer_ssd_256 + computer_ram_8 + computer_hdd_4)
+ # Make sure this is not extremely slow:
+ product_template = self.env['product.template'].create({
+ 'name': 'many combinations',
+ })
+
+ for i in range(10):
+ # create the attributes
+ product_attribute = self.env['product.attribute'].create({
+ 'name': "att %s" % i,
+ 'create_variant': 'dynamic',
+ 'sequence': i,
+ })
+
+ for j in range(10):
+ # create the attribute values
+ self.env['product.attribute.value'].create([{
+ 'name': "val %s/%s" % (i, j),
+ 'attribute_id': product_attribute.id,
+ 'sequence': j,
+ }])
+
+ # set attribute and attribute values on the template
+ self.env['product.template.attribute.line'].create([{
+ 'attribute_id': product_attribute.id,
+ 'product_tmpl_id': product_template.id,
+ 'value_ids': [(6, 0, product_attribute.value_ids.ids)]
+ }])
+
+ # Get a value in the middle for each attribute to make sure it would
+ # take time to reach it (if looping one by one like before the fix).
+ combination = self.env['product.template.attribute.value']
+ for ptal in product_template.attribute_line_ids:
+ combination += ptal.product_template_value_ids[5]
+
+ started_at = time.time()
+ self.assertEqual(product_template._get_closest_possible_combination(combination), combination)
+ elapsed = time.time() - started_at
+ # It should take around 10ms, but to avoid false positives we check an
+ # higher value. Before the fix it would take hours.
+ self.assertLess(elapsed, 0.5)
+
def test_clear_caches(self):
"""The goal of this test is to make sure the cache is invalidated when
it should be."""
diff --git a/addons/sale/report/sale_report_templates.xml b/addons/sale/report/sale_report_templates.xml
index 380596eb181..c8c9612326a 100644
--- a/addons/sale/report/sale_report_templates.xml
+++ b/addons/sale/report/sale_report_templates.xml
@@ -70,11 +70,15 @@
<table class="table table-sm o_main_table">
<thead>
<tr>
+ <!-- TODO: remove in master -->
+ <t t-set="colspan" t-value="5"/>
<th class="text-left">Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
<span>Disc.(%)</span>
+ <!-- TODO: remove in master -->
+ <t t-set="colspan" t-value="colspan+1"/>
</th>
<th class="text-right">Taxes</th>
<th class="text-right">
diff --git a/addons/sale/views/sale_portal_templates.xml b/addons/sale/views/sale_portal_templates.xml
index c2a12f037d2..1f85b2e7e32 100644
--- a/addons/sale/views/sale_portal_templates.xml
+++ b/addons/sale/views/sale_portal_templates.xml
@@ -423,11 +423,15 @@
<table class="table table-sm" id="sales_order_table">
<thead class="bg-100">
<tr>
+ <!-- TODO: remove in master -->
+ <t t-set="colspan" t-value="6"/>
<th class="text-left">Products</th>
<th class="text-right">Quantity</th>
<th t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">Unit Price</th>
<th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">
<span>Discount</span>
+ <!-- TODO: remove in master -->
+ <t t-set="colspan" t-value="colspan+1"/>
</th>
<th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Taxes</th>
<th class="text-right" >
diff --git a/addons/sale_management/models/sale_order.py b/addons/sale_management/models/sale_order.py
index 6ebed05a2bc..6a95dfeaeeb 100644
--- a/addons/sale_management/models/sale_order.py
+++ b/addons/sale_management/models/sale_order.py
@@ -31,7 +31,8 @@ class SaleOrder(models.Model):
@api.onchange('partner_id')
def onchange_partner_id(self):
super(SaleOrder, self).onchange_partner_id()
- self.note = self.sale_order_template_id.note or self.note
+ template = self.sale_order_template_id.with_context(lang=self.partner_id.lang)
+ self.note = template.note or self.note
def _compute_line_data_for_template_change(self, line):
return {
diff --git a/addons/sale_management/models/sale_order_template.py b/addons/sale_management/models/sale_order_template.py
index 8eef6efd405..493386f33c7 100644
--- a/addons/sale_management/models/sale_order_template.py
+++ b/addons/sale_management/models/sale_order_template.py
@@ -18,7 +18,7 @@ class SaleOrderTemplate(models.Model):
name = fields.Char('Quotation Template', required=True)
sale_order_template_line_ids = fields.One2many('sale.order.template.line', 'sale_order_template_id', 'Lines', copy=True)
- note = fields.Text('Terms and conditions')
+ note = fields.Text('Terms and conditions', translate=True)
sale_order_template_option_ids = fields.One2many('sale.order.template.option', 'sale_order_template_id', 'Optional Products', copy=True)
number_of_days = fields.Integer('Quotation Duration',
help='Number of days for the validity date computation of the quotation')
diff --git a/addons/sale_management/views/sale_portal_templates.xml b/addons/sale_management/views/sale_portal_templates.xml
index 90d2100ba4a..f29a538dd0e 100644
--- a/addons/sale_management/views/sale_portal_templates.xml
+++ b/addons/sale_management/views/sale_portal_templates.xml
@@ -15,6 +15,7 @@
<xpath expr="//section[@id='details']//table[@id='sales_order_table']/thead/tr" position="inside">
<th t-if="display_remove">
+ <!-- TODO: remove in master -->
<t t-set="colspan" t-value="colspan+1"/>
</th>
</xpath>
diff --git a/addons/stock/report/report_stockinventory.xml b/addons/stock/report/report_stockinventory.xml
index 90abd6b4109..19a988dfe59 100644
--- a/addons/stock/report/report_stockinventory.xml
+++ b/addons/stock/report/report_stockinventory.xml
@@ -6,6 +6,7 @@
<t t-foreach="docs" t-as="o">
<t t-call="web.external_layout">
<div class="page">
+ <br/>
<h2>Stock Inventory</h2>
<table class="table table-sm">
diff --git a/addons/stock_account/views/product_views.xml b/addons/stock_account/views/product_views.xml
index d4920effef1..1a42d5fcb58 100644
--- a/addons/stock_account/views/product_views.xml
+++ b/addons/stock_account/views/product_views.xml
@@ -67,7 +67,7 @@
<div name="standard_price_div" class="row ml-0">
<field name="standard_price"
attrs="{'readonly':[('valuation','=','real_time')]}"
- widget='monetary' options="{'currency_field': 'currency_id', 'field_digits': True}" class="float-left"/>
+ widget='monetary' options="{'currency_field': 'cost_currency_id', 'field_digits': True}" class="float-left"/>
<div name="update_cost_price">
<button string="Update Cost" type="action"
name="%(action_view_change_standard_price)d"
diff --git a/addons/survey/static/src/js/survey.js b/addons/survey/static/src/js/survey.js
index b4b47cb3d2f..0bcea263df4 100644
--- a/addons/survey/static/src/js/survey.js
+++ b/addons/survey/static/src/js/survey.js
@@ -98,6 +98,9 @@ if(!the_form.length) {
}
// checkboxes and radios
+ the_form.find("input[name^='" + key + "_'][type='checkbox']").each(function(){
+ $(this).val(value);
+ });
the_form.find("input[name=" + key + "][type!='text']").each(function(){
$(this).val(value);
});
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 185a385a812..04da1bc4983 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -428,6 +428,18 @@ ListRenderer.include({
});
}
},
+ /**
+ * @override
+ */
+ updateState: function (state, params) {
+ if (params.noRender) {
+ // the state changed, but we won't do a re-rendering right now, so
+ // remove computed modifiers data (as they are obsolete) to force
+ // them to be recomputed at next (sub-)rendering
+ this.allModifiersData = [];
+ }
+ return this._super.apply(this, arguments);
+ },
//--------------------------------------------------------------------------
// Private
@@ -478,7 +490,8 @@ ListRenderer.include({
*/
_moveToPreviousLine: function () {
if (this.currentRow > 0) {
- this._selectCell(this.currentRow - 1, this.columns.length - 1);
+ // FORWARD-PORT THIS COMMIT UP TO SAAS-12.2
+ this._selectCell(this.currentRow - 1, this.columns.length - 1, {inc: -1});
} else {
this.unselectRow().then(this.trigger_up.bind(this, 'add_record'));
}
@@ -692,6 +705,8 @@ ListRenderer.include({
* @param {boolean} [options.force=false] if true, force selecting the cell
* even if seems to be already the selected one (useful after a re-
* rendering, to reset the focus on the correct field)
+ * @param {integer} [options.inc=1] the increment to use when searching for
+ * the "next" possible cell (if the cell to select can't be selected)
* @return {Deferred} fails if no cell could be selected
*/
_selectCell: function (rowIndex, fieldIndex, options) {
@@ -715,7 +730,7 @@ ListRenderer.include({
var oldFieldIndex = self.currentFieldIndex;
self.currentFieldIndex = fieldIndex;
fieldIndex = self._activateFieldWidget(record, fieldIndex, {
- inc: 1,
+ inc: options.inc || 1,
wrap: wrap,
event: options && options.event,
});
@@ -868,7 +883,7 @@ ListRenderer.include({
switch (ev.data.direction) {
case 'previous':
if (this.currentFieldIndex > 0) {
- this._selectCell(this.currentRow, this.currentFieldIndex - 1, {wrap: false})
+ this._selectCell(this.currentRow, this.currentFieldIndex - 1, {inc: -1, wrap: false})
.fail(this._moveToPreviousLine.bind(this));
} else {
this._moveToPreviousLine();
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 347d0c23a53..9be3e427d98 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -2879,6 +2879,87 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('pressing SHIFT-TAB in editable list with a readonly field [REQUIRE FOCUS]', function (assert) {
+ assert.expect(4);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree editable="bottom">' +
+ '<field name="foo"/>' +
+ '<field name="int_field" readonly="1"/>' +
+ '<field name="qux"/>' +
+ '</tree>',
+ });
+
+ // start on 'qux', line 3
+ testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:nth(2)'));
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=qux]')[0]);
+
+ // Press 'shift-Tab' -> should go to first cell (same line)
+ $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true});
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=foo]')[0]);
+
+ list.destroy();
+ });
+
+ QUnit.test('pressing SHIFT-TAB in editable list with a readonly field in first column [REQUIRE FOCUS]', function (assert) {
+ assert.expect(4);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree editable="bottom">' +
+ '<field name="int_field" readonly="1"/>' +
+ '<field name="foo"/>' +
+ '<field name="qux"/>' +
+ '</tree>',
+ });
+
+ // start on 'foo', line 3
+ testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:nth(1)'));
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=foo]')[0]);
+
+ // Press 'shift-Tab' -> should go to previous line (last cell)
+ $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true});
+ assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(1) .o_data_cell input[name=qux]')[0]);
+
+ list.destroy();
+ });
+
+ QUnit.test('pressing SHIFT-TAB in editable list with a readonly field in last column [REQUIRE FOCUS]', function (assert) {
+ assert.expect(4);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree editable="bottom">' +
+ '<field name="int_field"/>' +
+ '<field name="foo"/>' +
+ '<field name="qux" readonly="1"/>' +
+ '</tree>',
+ });
+
+ // start on 'int_field', line 3
+ testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:first'));
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=int_field]')[0]);
+
+ // Press 'shift-Tab' -> should go to previous line ('foo' field)
+ $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true});
+ assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(1) .o_data_cell input[name=foo]')[0]);
+
+ list.destroy();
+ });
+
QUnit.test('skip invisible fields when navigating list view with TAB', function (assert) {
assert.expect(2);
@@ -4097,6 +4178,47 @@ QUnit.module('Views', {
list.destroy();
});
+
+ QUnit.test('create record on list with modifiers depending on id', function (assert) {
+ assert.expect(8);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree editable="top">' +
+ '<field name="id" invisible="1"/>' +
+ '<field name="foo" attrs="{\'readonly\': [[\'id\',\'!=\',False]]}"/>' +
+ '<field name="int_field" attrs="{\'invisible\': [[\'id\',\'!=\',False]]}"/>' +
+ '</tree>',
+ });
+
+ // add a new record
+ testUtils.dom.click(list.$buttons.find('.o_list_button_add'));
+
+ // modifiers should be evaluted to false
+ assert.containsOnce(list, '.o_selected_row');
+ assert.doesNotHaveClass(list.$('.o_selected_row .o_data_cell:first'), 'o_readonly_modifier');
+ assert.doesNotHaveClass(list.$('.o_selected_row .o_data_cell:nth(1)'), 'o_invisible_modifier');
+
+ // set a value and save
+ testUtils.fields.editInput(list.$('.o_selected_row input[name=foo]'), 'some value');
+ testUtils.dom.click(list.$buttons.find('.o_list_button_save'));
+
+ // modifiers should be evaluted to true
+ assert.hasClass(list.$('.o_data_row:first .o_data_cell:first'), 'o_readonly_modifier');
+ assert.hasClass(list.$('.o_data_row:first .o_data_cell:nth(1)'), 'o_invisible_modifier');
+
+ // edit again the just created record
+ testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:first'));
+
+ // modifiers should be evaluted to true
+ assert.containsOnce(list, '.o_selected_row');
+ assert.hasClass(list.$('.o_selected_row .o_data_cell:first'), 'o_readonly_modifier');
+ assert.hasClass(list.$('.o_selected_row .o_data_cell:nth(1)'), 'o_invisible_modifier');
+
+ list.destroy();
+ });
});
});