From e343f5f5f95607b91a61fed167a6f11f0eeaeaa8 Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Mon, 3 Sep 2018 16:20:06 +0200 Subject: [IMP] web: x2many adds a new line by tabbing on a not dirty line Before this change, when the focus was on the first field on a x2many field, and the user pressed TAB, the line would be discarded and the focus moved to the next field (at the level of the x2many) in the form. This was an issue because if the first field was required, the focus could not be moved out of the field with TAB so the user had to discard the line manually (with ESC) before moving to the next field This change works with 2 changes: 1. detecting if a line is dirty (has been modified by the user) and at the end of the line, decide to either - discard the line and move the next field, if the line is not dirty - commit the line and add a new line, if the line is dirty 2. allow users to use TAB through required field inside x2many so they can easilly go to the end of the line. Task id : 1872248 --- addons/web/static/src/js/views/basic/basic_model.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index fbb4b0b122c..9dd7c4d1141 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -594,6 +594,7 @@ var BasicModel = AbstractModel.extend({ getDomain: element.getDomain, getFieldNames: element.getFieldNames, id: element.id, + isDirty: element.isDirty, limit: element.limit, model: element.model, offset: element.offset, @@ -648,6 +649,7 @@ var BasicModel = AbstractModel.extend({ getFieldNames: element.getFieldNames, groupedBy: element.groupedBy, id: element.id, + isDirty: element.isDirty, isOpen: element.isOpen, limit: element.limit, model: element.model, @@ -3490,6 +3492,7 @@ var BasicModel = AbstractModel.extend({ dataPoint.getContext = this._getContext.bind(this, dataPoint); dataPoint.getDomain = this._getDomain.bind(this, dataPoint); dataPoint.getFieldNames = this._getFieldNames.bind(this, dataPoint); + dataPoint.isDirty = this.isDirty.bind(this, dataPoint.id); this.localData[dataPoint.id] = dataPoint; -- cgit v1.2.3 From fa4c650b01dc184041c61d9d52299b0b740a83bd Mon Sep 17 00:00:00 2001 From: svs-odoo Date: Fri, 21 Sep 2018 14:13:58 +0200 Subject: [IMP] web: limit number of open columns in kanban view With this commit, we limit the kanban view to 10 open groups at the start. This is useful to prevent the web client to make too many requests at once. This limit is set in the constant 'OPEN_GROUP_LIMIT'. Note: - columns already set as folded by default aren't take into account. - the user can still open any folded column, as usual. Task #1878113 (1/2) --- addons/web/static/src/js/views/basic/basic_model.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 9dd7c4d1141..ac7187c3874 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -134,6 +134,9 @@ var x2ManyCommands = { }; var BasicModel = AbstractModel.extend({ + // constants + OPEN_GROUP_LIMIT: 10, // after this limit, groups are automatically folded + // list of models for which the DataManager's cache should be cleared on // create, update and delete operations noCacheModels: [ @@ -3991,6 +3994,7 @@ var BasicModel = AbstractModel.extend({ list.data = []; list.count = 0; var defs = []; + var openGroupCount = 0; _.each(groups, function (group) { var aggregateValues = {}; @@ -4046,7 +4050,7 @@ var BasicModel = AbstractModel.extend({ oldGroup.limit = oldGroup.limit + oldGroup.loadMoreOffset; _.extend(oldGroup, updatedProps); newGroup = oldGroup; - } else if (!newGroup.openGroupByDefault) { + } else if (!newGroup.openGroupByDefault || openGroupCount >= self.OPEN_GROUP_LIMIT) { newGroup.isOpen = false; } else { newGroup.isOpen = '__fold' in group ? !group.__fold : true; @@ -4054,6 +4058,7 @@ var BasicModel = AbstractModel.extend({ list.data.push(newGroup.id); list.count += newGroup.count; if (newGroup.isOpen && newGroup.count > 0) { + openGroupCount++; defs.push(self._load(newGroup, options)); } }); -- cgit v1.2.3 From 856c2e9008f1af7bc1327d9c2900db1e109ab0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Thu, 30 Aug 2018 11:59:54 +0200 Subject: [IMP] product,(website_)sale: Move product options configurator into sale module Task #1871557 Purpose ======= - Configure a product from a sales order as easily as in the ecommerce The backend uses the same template as the frontend to configure a products and its options - Display optionnal products of optionnal products in the "sale options" section - Allow adding exlusions to some combinations of the product and/or to some combinations of the optionnal and accessory products Specification ============= PHASE I 1. Improve the "Add to cart" with optional products - When you add an option to cart, move the product to the cart & show the options of this option - Don't open the option wizard if no option available (in case this module becomes generic, it's better to skip this step when it's not needed) - Display the options right under their related product in the cart 2. Do not allow to add the product as an optional product for itself Why ? - No functional sense - Creates issues in the add to cart wizard 3. Exclude some attribute combinations within the product or with related options and accessory products - Rename VARIANT PRICES button -> CONFIGURE VARIANTS - When you click this button and select a value, open form view rather than inline edition - Form view of variant values: - Attribute - Value - HTML Color Index - Attribute Price Extra - Not Compatible with: o2m tab with 2 fields: Product AND Attribute Value (of this product) [m2m tag selection] - in any new line, autocomplete the current product by default - Tooltip: A list of product and attribute values that you want to exclude for this product's attribue value. Also applies on optionnal and accessory products. 4. New option to configure a product in a sales order line - This option is only visible if the corresponding option is activated in the sales settings - It opens a wizard to select a product template, configure related attribute values and select optional products (based on frontend view) - Attributes not compatible with other selected attributes should not be selectable - Must work like in the frontend --- addons/web/static/src/js/views/basic/basic_model.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index ac7187c3874..84e900021e8 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1344,6 +1344,7 @@ var BasicModel = AbstractModel.extend({ parentID: list.id, position: position, viewType: list.viewType, + allowWarning: options && options.allowWarning }; return this._makeDefaultRecord(list.model, params).then(function (id) { list._changes.push({operation: 'ADD', id: id, position: position, isNew: true}); @@ -1395,7 +1396,7 @@ var BasicModel = AbstractModel.extend({ for (var fieldName in changes) { field = record.fields[fieldName]; if (field.type === 'one2many' || field.type === 'many2many') { - defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName], options.viewType)); + defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName], options.viewType, options.allowWarning)); } else if (field.type === 'many2one' || field.type === 'reference') { defs.push(this._applyX2OneChange(record, fieldName, changes[fieldName])); } else { @@ -1724,7 +1725,7 @@ var BasicModel = AbstractModel.extend({ * main viewType from the record * @returns {Deferred} */ - _applyX2ManyChange: function (record, fieldName, command, viewType) { + _applyX2ManyChange: function (record, fieldName, command, viewType, allowWarning) { if (command.operation === 'TRIGGER_ONCHANGE') { // the purpose of this operation is to trigger an onchange RPC, so // there is no need to apply any change on the record (the changes @@ -1819,6 +1820,7 @@ var BasicModel = AbstractModel.extend({ var options = { context: command.context, position: command.position, + allowWarning: allowWarning }; def = this._addX2ManyDefaultRecord(list, options).then(function (id) { if (command.position === 'bottom' && list.orderedResIDs && list.orderedResIDs.length >= list.limit) { -- cgit v1.2.3 From d3fbd102694bedaedac7016022a7f08d95a10553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Fri, 31 Aug 2018 15:06:38 +0200 Subject: [IMP] framework js: improve X2Many 'CREATE' operation to handle multiple records Purpose ======= The 'CREATE' operation lacked the possibility to handle multiple records. The 'context' parameter is now an array of dict that contains the context for each new record. This is usefull when you want to create multiple records into a list in a single call. Please note that the 'default_get' is still called once for each record in an async way. --- .../web/static/src/js/views/basic/basic_model.js | 79 +++++++++++++++------- 1 file changed, 53 insertions(+), 26 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 84e900021e8..695e8147c7f 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1328,17 +1328,17 @@ var BasicModel = AbstractModel.extend({ * @param {Object} [options] * @param {string} [options.position=top] if the new record should be added * on top or on bottom of the list - * @param {Object} [options.context] additional context to be merged before - * calling the default_get (eg. to set default values) - * @returns {Deferred} resolves to the new record id + * @param {Object} [options.[context]] additional context to be merged before + * calling the default_get (eg. to set default values). + * If several contexts are found, multiple records are added + * @param {boolean} [params.allowWarning=false] if true, the default record + * operation can complete, even if a warning is raised + * @returns {Deferred<[string]>} resolves to the new records ids */ _addX2ManyDefaultRecord: function (list, options) { var self = this; var position = options && options.position || 'top'; - var additionalContext = options && options.context; - var context = this._getContext(list, {additionalContext: additionalContext}); var params = { - context: context, fields: list.fields, fieldsInfo: list.fieldsInfo, parentID: list.id, @@ -1346,17 +1346,36 @@ var BasicModel = AbstractModel.extend({ viewType: list.viewType, allowWarning: options && options.allowWarning }; - return this._makeDefaultRecord(list.model, params).then(function (id) { - list._changes.push({operation: 'ADD', id: id, position: position, isNew: true}); - var record = self.localData[id]; - list._cache[record.res_id] = id; - if (list.orderedResIDs) { - var index = list.offset + (position !== 'top' ? list.limit : 0); - list.orderedResIDs.splice(index, 0, record.res_id); - // list could be a copy of the original one - self.localData[list.id].orderedResIDs = list.orderedResIDs; - } - return id; + + var additionalContexts = options && options.context; + var makeDefaultRecords = []; + if(additionalContexts){ + _.each(additionalContexts, function (context) { + params.context = self._getContext(list, {additionalContext: context}); + makeDefaultRecords.push(self._makeDefaultRecord(list.model, params)); + }); + } else { + params.context = self._getContext(list); + makeDefaultRecords.push(self._makeDefaultRecord(list.model, params)); + } + + return $.when.apply($, makeDefaultRecords).then(function(){ + var ids = []; + _.each(arguments, function(id){ + ids.push(id); + + list._changes.push({operation: 'ADD', id: id, position: position, isNew: true}); + var record = self.localData[id]; + list._cache[record.res_id] = id; + if (list.orderedResIDs) { + var index = list.offset + (position !== 'top' ? list.limit : 0); + list.orderedResIDs.splice(index, 0, record.res_id); + // list could be a copy of the original one + self.localData[list.id].orderedResIDs = list.orderedResIDs; + } + }); + + return ids; }); }, /** @@ -1375,6 +1394,9 @@ var BasicModel = AbstractModel.extend({ * it was changed. * @param {string} [options.viewType] current viewType. If not set, we will assume * main viewType from the record + * @param {boolean} [params.allowWarning=false] if true, change + * operation can complete, even if a warning is raised + * (only supported by X2ManyChange) * @returns {Deferred} list of changed fields */ _applyChange: function (recordID, changes, options) { @@ -1723,6 +1745,9 @@ var BasicModel = AbstractModel.extend({ * key. For example, it looks like {operation: ADD, id: 'partner_1'} * @param {string} [viewType] current viewType. If not set, we will assume * main viewType from the record + * @param {boolean} [params.allowWarning=false] if true, change + * operation can complete, even if a warning is raised + * (only supported by the 'CREATE' command.operation) * @returns {Deferred} */ _applyX2ManyChange: function (record, fieldName, command, viewType, allowWarning) { @@ -1822,15 +1847,17 @@ var BasicModel = AbstractModel.extend({ position: command.position, allowWarning: allowWarning }; - def = this._addX2ManyDefaultRecord(list, options).then(function (id) { - if (command.position === 'bottom' && list.orderedResIDs && list.orderedResIDs.length >= list.limit) { - list.tempLimitIncrement = (list.tempLimitIncrement || 0) + 1; - list.limit += 1; - } - // FIXME: hack for lunch widget, which does useless default_get and onchange - if (command.data) { - return self._applyChange(id, command.data); - } + def = this._addX2ManyDefaultRecord(list, options).then(function (ids) { + _.each(ids, function(id){ + if (command.position === 'bottom' && list.orderedResIDs && list.orderedResIDs.length >= list.limit) { + list.tempLimitIncrement = (list.tempLimitIncrement || 0) + 1; + list.limit += 1; + } + // FIXME: hack for lunch widget, which does useless default_get and onchange + if (command.data) { + return self._applyChange(id, command.data); + } + }); }); defs.push(def); break; -- cgit v1.2.3 From a4fa2a33f985b00914b40fca5781472319962c87 Mon Sep 17 00:00:00 2001 From: XavierDo Date: Tue, 25 Sep 2018 16:36:13 +0200 Subject: [FIX] web: order read_group by aggregable fields only in list view The read_group method will ignore and display a warning when giving an order on a non-aggregable field. Therefore, when accessing a list of record from a list view group by some field and ordered on another one, a warning will appear in logs (making the nightly clikall test yellow) The solution here is to only order by current group by field and on aggregable fields. We want to keep the user order to know the ASC/DESC information. --- addons/web/static/src/js/views/basic/basic_model.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 695e8147c7f..94d0ea78c0e 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -4006,6 +4006,9 @@ var BasicModel = AbstractModel.extend({ var groupByField = list.groupedBy[0]; var rawGroupBy = groupByField.split(':')[0]; var fields = _.uniq(list.getFieldNames().concat(rawGroupBy)); + var orderedBy = _.filter(list.orderedBy, function(order){ + return order.name === rawGroupBy || list.fields[order.name].group_operator !== undefined; + }); return this._rpc({ model: list.model, method: 'read_group', @@ -4013,7 +4016,7 @@ var BasicModel = AbstractModel.extend({ domain: list.domain, context: list.context, groupBy: list.groupedBy, - orderBy: list.orderedBy, + orderBy: orderedBy, lazy: true, }) .then(function (groups) { -- cgit v1.2.3 From 27a739385249cff8c8779cb0d7be140e0b89226a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Thu, 27 Sep 2018 09:42:17 +0200 Subject: [IMP] product,(website_)sale: Product configurator default_get cleanup Targets commit d3530eb07e24278117ab396ecb86a59deecc312d Purpose ======= [Follow up AL review] Removed the default_get override in the sale_order_line model. - The default values to create records are now specified by pefixing them with "default_" in the JS. That will use the built-in framework method, which is more simple/clean. - The "list_editable_renderer.js" file for the product configurator was removed and its content is now merged within the SectionAndNoteListRenderer. Updated fields: - [sale_order_line in sale/sale.py] "product_no_variant_attribute_values" to "product_no_variant_attribute_value_ids" --- addons/web/static/src/js/views/basic/basic_model.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 94d0ea78c0e..f6bf46aed68 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1328,10 +1328,10 @@ var BasicModel = AbstractModel.extend({ * @param {Object} [options] * @param {string} [options.position=top] if the new record should be added * on top or on bottom of the list - * @param {Object} [options.[context]] additional context to be merged before + * @param {Array} [options.[context]] additional context to be merged before * calling the default_get (eg. to set default values). * If several contexts are found, multiple records are added - * @param {boolean} [params.allowWarning=false] if true, the default record + * @param {boolean} [options.allowWarning=false] if true, the default record * operation can complete, even if a warning is raised * @returns {Deferred<[string]>} resolves to the new records ids */ @@ -1349,7 +1349,7 @@ var BasicModel = AbstractModel.extend({ var additionalContexts = options && options.context; var makeDefaultRecords = []; - if(additionalContexts){ + if (additionalContexts){ _.each(additionalContexts, function (context) { params.context = self._getContext(list, {additionalContext: context}); makeDefaultRecords.push(self._makeDefaultRecord(list.model, params)); @@ -1359,9 +1359,9 @@ var BasicModel = AbstractModel.extend({ makeDefaultRecords.push(self._makeDefaultRecord(list.model, params)); } - return $.when.apply($, makeDefaultRecords).then(function(){ + return $.when.apply($, makeDefaultRecords).then(function (){ var ids = []; - _.each(arguments, function(id){ + _.each(arguments, function (id){ ids.push(id); list._changes.push({operation: 'ADD', id: id, position: position, isNew: true}); @@ -1394,7 +1394,7 @@ var BasicModel = AbstractModel.extend({ * it was changed. * @param {string} [options.viewType] current viewType. If not set, we will assume * main viewType from the record - * @param {boolean} [params.allowWarning=false] if true, change + * @param {boolean} [options.allowWarning=false] if true, change * operation can complete, even if a warning is raised * (only supported by X2ManyChange) * @returns {Deferred} list of changed fields @@ -1745,7 +1745,7 @@ var BasicModel = AbstractModel.extend({ * key. For example, it looks like {operation: ADD, id: 'partner_1'} * @param {string} [viewType] current viewType. If not set, we will assume * main viewType from the record - * @param {boolean} [params.allowWarning=false] if true, change + * @param {boolean} [allowWarning=false] if true, change * operation can complete, even if a warning is raised * (only supported by the 'CREATE' command.operation) * @returns {Deferred} -- cgit v1.2.3