diff options
| author | Aurélien Warnon <awa@odoo.com> | 2018-09-25 18:41:27 +0200 |
|---|---|---|
| committer | Aurélien Warnon <awa@odoo.com> | 2018-09-25 18:42:07 +0200 |
| commit | d3530eb07e24278117ab396ecb86a59deecc312d (patch) | |
| tree | eb9bd5d52f0a4f93d11318cc6674e7111ea04efe /addons/web/static/src/js/views/basic | |
| parent | 96f7bda09e2cec442046b34c80713894fd16bfb6 (diff) | |
| parent | 90824fc0193c5f83d024660c5237328d89f3ae96 (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')
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 85 |
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; |
