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