summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic/basic_model.js
diff options
context:
space:
mode:
authorAurélien Warnon <awa@odoo.com>2018-09-25 18:41:27 +0200
committerAurélien Warnon <awa@odoo.com>2018-09-25 18:42:07 +0200
commitd3530eb07e24278117ab396ecb86a59deecc312d (patch)
treeeb9bd5d52f0a4f93d11318cc6674e7111ea04efe /addons/web/static/src/js/views/basic/basic_model.js
parent96f7bda09e2cec442046b34c80713894fd16bfb6 (diff)
parent90824fc0193c5f83d024660c5237328d89f3ae96 (diff)
[IMP] product,(website_)sale: Allow users to fully configure products
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 - Product attributes can now have custom values. These values are specified by the user during the SO creation. They are also available on the webshop frontend. - Product attributes can be configured to create variants "on demand". This means that product.templates having these attributes will only create product.product variant when that combination is actually selected in a sale order/in the web shop. Product attribute values are also improved to handle extra prices and exclusions in all three modes ("never", "dynamic, "always") Specification ============= 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 5. Create variants on demand - add a new mode to generate variants as they are added to the order/cart (needed for big # of combinations managed in the inventory and/or produced in mrp with specific boms) - replace the boolean by a selection radio tooltip for this new field: Always: Variants are generated straight away when the attribute values are combined in the product configuration. Only when the product is added to a sales order: Variants are generated once the combination is selected in the sales order. This is advised when variants are customized on demand and manufactured as such in Odoo. Never: Advised when variants are not needed in Odoo. The selected values just show up in the description of sales order lines. - ability to define attribute extra prices & exclude combinations in all variant modes - if at least one attribute of the product in set to "on demand" mode, generate all the variants "on demand"
Diffstat (limited to 'addons/web/static/src/js/views/basic/basic_model.js')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js85
1 files changed, 57 insertions, 28 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 ac7187c3874..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,34 +1328,54 @@ 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<string>} 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,
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});
- 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;
});
},
/**
@@ -1374,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) {
@@ -1395,7 +1418,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 {
@@ -1722,9 +1745,12 @@ 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) {
+ _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,16 +1845,19 @@ 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) {
- 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;