summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic
diff options
context:
space:
mode:
authorAurélien Warnon <awa@odoo.com>2019-03-06 07:24:28 +0000
committerAurélien Warnon <awa@odoo.com>2019-03-28 14:55:00 +0000
commit220f5ae53f66400320b697c778039b0585da8fc4 (patch)
treec043173861eeb83ee54688a20ef983bd3c39744c /addons/web/static/src/js/views/basic
parent55ed70f405a7614756f878599ddc7e7d34f6b508 (diff)
[IMP] sale_product_configurator: improve p. configurator
Task #1891970 Original p. configurator commit d3530eb Purpose ======= - The p. configurator now comes with a widget that is "o2m" like in the SO lines view. The widget is only used on the added "product_template_id" field on the SO line. This widget controls the opening of the configuration window and removes the need of a "Configure a product" button. The "SectionAndNoteListRenderer" is now cleaned from p. configurator specific code. The widget is also responsible for handling the configuration provided by the p. configurator form and applying it on the SO line with a 'field_changed' event that updates all the necessary fields. - Added support for 'MULTI' and 'DELETE_ALL' operations on X2Many fields in basic_model.js - 'MULTI' allows to batch multiple operations at once - 'DELETE_ALL' behaves like 'DELETE' with all the current data of the field Spec ======= - remove "configure a product" from sale order lines When the product configurator is active, replace the product_product_id by a product_template_id in the sale order line. When we select a template without variants, it sets the variant automatically, but when we select a product template having variants, it opens the configurator dialog. - add a widget to modify the product configuration in the sale order line (next to product template field) - UX improvements: - Invert image and configuration in the main screen - If the product doesn't have an image, hide it instead of showing the placeholder (only for first screen?) - new independent option in sales settings to activate product configurator. (same in e-commerce) - demo data: Change demo data to set Customizable Desk & Conference Chair as make to order - If only one attribute value that is Custom, don't display radio, selection or even color box
Diffstat (limited to 'addons/web/static/src/js/views/basic')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js53
1 files changed, 40 insertions, 13 deletions
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 e7ce88962ff..79fac0f2247 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -1347,7 +1347,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, options.allowWarning));
+ defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName], options));
} else if (field.type === 'many2one' || field.type === 'reference') {
defs.push(this._applyX2OneChange(record, fieldName, changes[fieldName]));
} else {
@@ -1677,14 +1677,15 @@ var BasicModel = AbstractModel.extend({
* @param {string} fieldName
* @param {Object} command A command object. It should have a 'operation'
* key. For example, it looks like {operation: ADD, id: 'partner_1'}
- * @param {string} [viewType] current viewType. If not set, we will assume
+ * @param {Object} [options]
+ * @param {string} [options.viewType] current viewType. If not set, we will assume
* main viewType from the record
- * @param {boolean} [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 the 'CREATE' command.operation)
* @returns {Promise}
*/
- _applyX2ManyChange: function (record, fieldName, command, viewType, allowWarning) {
+ _applyX2ManyChange: function (record, fieldName, command, options) {
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
@@ -1697,7 +1698,8 @@ var BasicModel = AbstractModel.extend({
var localID = (record._changes && record._changes[fieldName]) || record.data[fieldName];
var list = this.localData[localID];
var field = record.fields[fieldName];
- var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName];
+ var viewType = (options && options.viewType) || record.viewType;
+ var fieldInfo = record.fieldsInfo[viewType][fieldName];
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
var def, rec;
var defs = [];
@@ -1776,12 +1778,12 @@ var BasicModel = AbstractModel.extend({
}
break;
case 'CREATE':
- var options = {
+ var createOptions = _.extend({
context: command.context,
- position: command.position,
- allowWarning: allowWarning
- };
- def = this._addX2ManyDefaultRecord(list, options).then(function (ids) {
+ position: command.position
+ }, options || {});
+
+ def = this._addX2ManyDefaultRecord(list, createOptions).then(function (ids) {
_.each(ids, function(id){
if (command.position === 'bottom' && list.orderedResIDs && list.orderedResIDs.length >= list.limit) {
list.tempLimitIncrement = (list.tempLimitIncrement || 0) + 1;
@@ -1822,6 +1824,17 @@ var BasicModel = AbstractModel.extend({
list._changes.push({operation: operation, id: id});
});
break;
+ case 'DELETE_ALL':
+ // first remove all pending 'ADD' operations
+ list._changes = _.reject(list._changes, function (change) {
+ return change.operation === 'ADD';
+ });
+
+ // then apply 'DELETE' on existing records
+ return this._applyX2ManyChange(record, fieldName, {
+ operation: 'DELETE',
+ ids: list.res_ids
+ }, options);
case 'REPLACE_WITH':
// this is certainly not optimal... and not sure that it is
// correct if some ids are added and some other are removed
@@ -1836,7 +1849,7 @@ var BasicModel = AbstractModel.extend({
addDef = this._applyX2ManyChange(record, fieldName, {
operation: 'ADD_M2M',
ids: values
- }, viewType);
+ }, options);
}
if (removedIds.length) {
var listData = _.map(list.data, function (localId) {
@@ -1850,9 +1863,20 @@ var BasicModel = AbstractModel.extend({
}
return _.findWhere(listData, {res_id: resID}).id;
}),
- }, viewType);
+ }, options);
}
return Promise.all([addDef, removedDef]);
+ case 'MULTI':
+ // allows batching multiple operations
+ _.each(command.commands, function (innerCommand){
+ defs.push(self._applyX2ManyChange(
+ record,
+ fieldName,
+ innerCommand,
+ options
+ ));
+ });
+ break;
}
return Promise.all(defs).then(function () {
@@ -1911,7 +1935,10 @@ var BasicModel = AbstractModel.extend({
case 'FORGET':
case 'DELETE':
list.count--;
- list.res_ids = _.without(list.res_ids, relRecord.res_id);
+ // FIXME awa: there is no "relRecord" for o2m field
+ // seems like using change.id does the trick -> check with framework JS
+ var deletedResID = relRecord ? relRecord.res_id : change.id;
+ list.res_ids = _.without(list.res_ids, deletedResID);
break;
case 'REMOVE_ALL':
list.count = 0;