3
0
Fork 0

Merge PR #1825 into 12.0

Signed-off-by pedrobaeza
12.0
OCA-git-bot 2021-03-15 19:45:27 +00:00
commit 7af341936f
9 changed files with 200 additions and 190 deletions

View File

@ -86,6 +86,8 @@ Widget options:
will lose part of its functionality as the document will be saved every time you will lose part of its functionality as the document will be saved every time you
modify/create a record with the widget. modify/create a record with the widget.
* ignore_warning > Enable/Disable display onchange warnings (False by default)
All widget options are optional. All widget options are optional.
Notice that you can call '_' method to use translations. This only can be used with this widget. Notice that you can call '_' method to use translations. This only can be used with this widget.

View File

@ -44,6 +44,8 @@ Widget options:
will lose part of its functionality as the document will be saved every time you will lose part of its functionality as the document will be saved every time you
modify/create a record with the widget. modify/create a record with the widget.
* ignore_warning > Enable/Disable display onchange warnings (False by default)
All widget options are optional. All widget options are optional.
Notice that you can call '_' method to use translations. This only can be used with this widget. Notice that you can call '_' method to use translations. This only can be used with this widget.

View File

@ -471,6 +471,8 @@ You need to define the view fields. The view must be of <tt class="docutils lite
will lose part of its functionality as the document will be saved every time you will lose part of its functionality as the document will be saved every time you
modify/create a record with the widget.</p> modify/create a record with the widget.</p>
</li> </li>
<li><p class="first">ignore_warning &gt; Enable/Disable display onchange warnings (False by default)</p>
</li>
</ul> </ul>
<p>All widget options are optional. <p>All widget options are optional.
Notice that you can call _ method to use translations. This only can be used with this widget.</p> Notice that you can call _ method to use translations. This only can be used with this widget.</p>

View File

@ -179,6 +179,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickModifPriceForm
this.trigger_up("update_quick_record", { this.trigger_up("update_quick_record", {
id: record.id, id: record.id,
}); });
self.model.unsetDirty(self.handle);
this.getParent().destroy(); this.getParent().destroy();
} }
}, },

View File

@ -213,6 +213,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
is_virtual: this.is_virtual, is_virtual: this.is_virtual,
modified: record && record.context.product_picker_modified, modified: record && record.context.product_picker_modified,
active_model: '', active_model: '',
auto_save: this.options.autoSave,
}; };
}, },
@ -493,11 +494,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
_calcPriceReduced: function () { _calcPriceReduced: function () {
var price_reduce = 0; var price_reduce = 0;
var field_map = this.options.fieldMap; var field_map = this.options.fieldMap;
var state_data = this.state.data; var model = this.options.basicFieldParams.model;
if (state_data && state_data[field_map.discount]) { var record = model.get(this.state.id);
if (record && record.data[field_map.discount]) {
price_reduce = tools.priceReduce( price_reduce = tools.priceReduce(
state_data[field_map.price_unit], record.data[field_map.price_unit],
state_data[field_map.discount]); record.data[field_map.discount]);
} }
return price_reduce && tools.monetary( return price_reduce && tools.monetary(
price_reduce, price_reduce,
@ -516,28 +518,36 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
var model = this.options.basicFieldParams.model; var model = this.options.basicFieldParams.model;
var record = model.get(this.state.id); var record = model.get(this.state.id);
return model.save(record.id, { return model.save(record.id, {
stayInEdit: true,
reload: true,
savePoint: true, savePoint: true,
viewType: "form",
}).then(function () { }).then(function () {
var record = model.get(self.state.id); var record = model.get(self.state.id);
self.trigger_up("create_quick_record", { self.trigger_up("create_quick_record", {
id: record.id, id: record.id,
callback: function () {
self.$card.find('.o_catch_attention').removeClass('o_catch_attention');
}
}); });
model.unsetDirty(self.state.id); model.unsetDirty(self.state.id);
self.$card.find('.o_catch_attention').removeClass('o_catch_attention');
}); });
}, },
/** /**
* @private * @private
*/ */
_updateRecord: function (changes) { _updateRecord: function () {
var self = this;
var model = this.options.basicFieldParams.model; var model = this.options.basicFieldParams.model;
var record = model.get(this.state.id); var record = model.get(this.state.id);
this.trigger_up("update_quick_record", { this.trigger_up("update_quick_record", {
id: record.id, id: record.id,
callback: function () {
self.$card.find('.o_catch_attention').removeClass('o_catch_attention');
}
}); });
model.unsetDirty(this.state.id); model.unsetDirty(this.state.id);
this.$card.find('.o_catch_attention').removeClass('o_catch_attention');
}, },
/** /**
@ -546,13 +556,19 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
*/ */
_addProduct: function () { _addProduct: function () {
var self = this; var self = this;
var changes = {}; var model = this.options.basicFieldParams.model;
if (this.state.data[this.options.fieldMap.product_uom_qty] === 0) { model.updateRecordContext(this.state.id, {ignore_warning: this.options.ignoreWarning});
var record = model.get(this.state.id);
var changes = _.pick(record.data, this.options.fieldMap.product_uom_qty);
if (changes[this.options.fieldMap.product_uom_qty] === 0) {
changes[this.options.fieldMap.product_uom_qty] = 1; changes[this.options.fieldMap.product_uom_qty] = 1;
} }
var model = this.options.basicFieldParams.model; var model = this.options.basicFieldParams.model;
this.$card.addClass("blocked"); this.$card.addClass("blocked");
return model.notifyChanges(this.state.id, changes).then(function () { return model.notifyChanges(
record.id,
changes
).then(function () {
self._saveRecord(); self._saveRecord();
}); });
}, },
@ -564,14 +580,16 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
*/ */
_incProductQty: function (amount) { _incProductQty: function (amount) {
var self = this; var self = this;
this.state.data[this.options.fieldMap.product_uom_qty] += amount;
var model = this.options.basicFieldParams.model; var model = this.options.basicFieldParams.model;
model.updateRecordContext(this.state.id, {ignore_warning: this.options.ignoreWarning});
var record = model.get(this.state.id); var record = model.get(this.state.id);
var state_data = record.data; var changes = _.pick(record.data, this.options.fieldMap.product_uom_qty);
state_data[this.options.fieldMap.product_uom_qty] += amount; changes[this.options.fieldMap.product_uom_qty] += amount;
var changes = _.pick(state_data, this.options.fieldMap.product_uom_qty);
return model.notifyChanges(record.id, changes).then(function () { return model.notifyChanges(
record.id,
changes
).then(function () {
self._processDynamicFields(); self._processDynamicFields();
self._lazyUpdateRecord(); self._lazyUpdateRecord();
}); });

View File

@ -104,8 +104,10 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
*/ */
updateState: function (state, params) { updateState: function (state, params) {
var self = this; var self = this;
var force_update = params.force;
delete params.force;
var sparams = _.extend({}, params, {noRender: true}); var sparams = _.extend({}, params, {noRender: true});
if (_.isEqual(this.state.data, state.data)) { if (!force_update && _.isEqual(this.state.data, state.data)) {
return this._super(state, sparams); return this._super(state, sparams);
} }
var old_state = _.clone(this.state.data); var old_state = _.clone(this.state.data);
@ -114,6 +116,22 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
}); });
}, },
/**
* Recreate the given widget by the state id
*
* @param {String} state_id
* @param {Object} new_state
*/
updateRecord: function (state_id, new_state) {
for (var eb = this.widgets.length-1; eb>=0; --eb) {
var widget = this.widgets[eb];
if (widget.state.id === state_id) {
widget.recreate(new_state);
break;
}
}
},
/** /**
* @private * @private
* @param {Array[Object]} states * @param {Array[Object]} states
@ -206,7 +224,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
var found = false; var found = false;
for (var e in this.state.data) { for (var e in this.state.data) {
var current_state = this.state.data[e]; var current_state = this.state.data[e];
if (current_state.id === old_state.id || (typeof current_state.data.id !== 'undefined' && current_state.data.id === old_state.data.id)) { if (current_state.id === old_state.id) {
found = true; found = true;
break; break;
} }
@ -215,6 +233,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
states_to_destroy.push(old_state); states_to_destroy.push(old_state);
} }
} }
this.state.data = _.compact(this.state.data);
this._removeRecords(states_to_destroy, this.state.data); this._removeRecords(states_to_destroy, this.state.data);
// Records to Update or Create // Records to Update or Create
@ -232,7 +252,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
// Already processed widget (deleted) // Already processed widget (deleted)
continue; continue;
} }
if (widget.state.id === state.id || (typeof state.data.id !== 'undefined' && widget.state.data.id === state.data.id)) { if (widget.state.id === state.id) {
widget.recreate(state); widget.recreate(state);
exists = true; exists = true;
break; break;
@ -255,6 +275,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
} }
} }
this.state.data = _.compact(this.state.data);
// Need add a new one? // Need add a new one?
if (!exists && search_record_index !== -1) { if (!exists && search_record_index !== -1) {
var new_search_record = _.extend({}, search_record, {__id: state.id}); var new_search_record = _.extend({}, search_record, {__id: state.id});
@ -278,6 +300,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
}); });
this.$extraButtonsContainer = $(qweb.render("One2ManyProductPicker.ExtraButtons")); this.$extraButtonsContainer = $(qweb.render("One2ManyProductPicker.ExtraButtons"));
this.$btnLoadMore = this.$extraButtonsContainer.find("#productPickerLoadMore"); this.$btnLoadMore = this.$extraButtonsContainer.find("#productPickerLoadMore");
this.search_data = this._sort_search_data(this.search_data);
return $.Deferred(function (d) { return $.Deferred(function (d) {
var defs = self.appendSearchRecords(self.search_data, true); var defs = self.appendSearchRecords(self.search_data, true);
defs[0].then(function () { defs[0].then(function () {
@ -294,6 +317,31 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
}); });
}, },
/**
* @param {Array} datas
* @returns {Array}
*/
_sort_search_data: function (datas) {
if (this.search_group.name === "main_lines") {
var field_name = this.options.field_map.product;
for (var index_datas in datas) {
var data = datas[index_datas];
for (var index_state in this.state.data) {
var state_data = this.state.data[index_state];
if (state_data.data[field_name].res_id === data.id) {
data._order_value = state_data.res_id;
}
}
}
var sorted_datas = _.chain(datas).sortBy('_order_value').map(function(item) {
return _.omit(item, '_order_value');
}).value().reverse();
return sorted_datas;
}
return datas;
},
/** /**
* Compare search results with current lines. * Compare search results with current lines.
* Link a current state with the 'search record'. * Link a current state with the 'search record'.
@ -362,6 +410,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
showDiscount: this.options.show_discount, showDiscount: this.options.show_discount,
editDiscount: this.options.edit_discount, editDiscount: this.options.edit_discount,
editPrice: this.options.edit_price, editPrice: this.options.edit_price,
autoSave: this.options.auto_save,
ignoreWarning: this.options.ignore_warning,
}); });
}, },

View File

@ -79,8 +79,9 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require)
self._makeDefaultRecord(list.model, params) self._makeDefaultRecord(list.model, params)
.then(function (recordID) { .then(function (recordID) {
self.setPureVirtual(recordID, true); self.setPureVirtual(recordID, true);
self.updateRecordContext(recordID, {ignore_warning: true});
if (options.data) { if (options.data) {
self._applyChangeNoWarnings( self._applyChange(
recordID, recordID,
options.data, options.data,
params params
@ -95,165 +96,26 @@ odoo.define("web_widget_one2many_product_picker.BasicModel", function (require)
}, },
/** /**
* Cloned '_applyChange' but without warning messages * Adds support to avoid show onchange warnings.
* The implementation is a pure hack that clone
* the context and do a monkey path the
* 'trigger_up' method.
* *
* @private * @override
* @param {Number} recordID
* @param {Object} changes
* @param {Object} options
* @returns {Deferred}
*/ */
_applyChangeNoWarnings: function (recordID, changes, options) { _performOnChange: function (record, fields, viewType) {
var self = this; if (record.context && record.context.ignore_warning) {
var record = this.localData[recordID]; var this_mp = _.clone(this);
var field; var super_call = this.trigger_up;
var defs = []; this_mp.trigger_up = function (event_name, data) {
options = options || {}; if (event_name === 'warning' && data.type === "dialog") {
record._changes = record._changes || {}; return; // Do nothing
if (!options.doNotSetDirty) {
record._isDirty = true;
}
var initialData = {};
this._visitChildren(record, function (elem) {
initialData[elem.id] = $.extend(true, {}, _.pick(elem, 'data', '_changes'));
});
// Apply changes to local data
for (var fieldName in changes) {
field = record.fields[fieldName];
if (field && (field.type === 'one2many' || field.type === 'many2many')) {
defs.push(this._applyX2ManyChange(
record,
fieldName,
changes[fieldName],
options.viewType,
options.allowWarning));
} else if (field && (field.type === 'many2one' || field.type === 'reference')) {
defs.push(this._applyX2OneChange(record, fieldName, changes[fieldName]));
} else {
record._changes[fieldName] = changes[fieldName];
}
}
if (options.notifyChange === false) {
return $.Deferred().resolve(_.keys(changes));
}
return $.when.apply($, defs).then(function () {
// The fields that have changed and that have an on_change
var onChangeFields = [];
for (var fieldName in changes) {
field = record.fields[fieldName];
if (field && field.onChange) {
var isX2Many = (
field.type === 'one2many' ||
field.type === 'many2many'
);
if (
!isX2Many ||
(
self._isX2ManyValid(
record._changes[fieldName] ||
record.data[fieldName]
)
)
) {
onChangeFields.push(fieldName);
}
} }
} return super_call.apply(this, arguments);
var onchangeDef = $.Deferred(); }.bind(this);
if (onChangeFields.length) { return this._super.apply(this_mp, arguments);
self._performOnChangeNoWarnings(record, onChangeFields, options.viewType)
.then(function (result) {
delete record._warning;
onchangeDef.resolve(
_.keys(changes).concat(
Object.keys((result && result.value) || {})));
}).fail(function () {
self._visitChildren(record, function (elem) {
_.extend(elem, initialData[elem.id]);
});
// Safe fix for stable version, for opw-2267444
if (!options.force_fail) {
onchangeDef.resolve({});
} else {
onchangeDef.reject({});
}
});
} else {
onchangeDef = $.Deferred().resolve(_.keys(changes));
}
return onchangeDef.then(function (fieldNames) {
_.each(fieldNames, function (name) {
if (
record._changes &&
record._changes[name] === record.data[name]
) {
delete record._changes[name];
record._isDirty = !_.isEmpty(record._changes);
}
});
return self._fetchSpecialData(record).then(function (fieldNames2) {
// Return the names of the fields that changed (onchange or
// associated special data change)
return _.union(fieldNames, fieldNames2);
});
});
});
},
/**
* Cloned '_performOnChange' but without warning messages
*
* @private
* @param {Object} record
* @param {Object} fields
* @param {String} viewType
* @returns {Deferred}
*/
_performOnChangeNoWarnings: function (record, fields, viewType) {
var self = this;
var onchangeSpec = this._buildOnchangeSpecs(record, viewType);
if (!onchangeSpec) {
return $.when();
} }
var idList = record.data.id ? [record.data.id] : []; return this._super.apply(this, arguments);
var options = {
full: true,
};
if (fields.length === 1) {
fields = fields[0];
// If only one field changed, add its context to the RPC context
options.fieldName = fields;
}
var context = this._getContext(record, options);
var currentData = this._generateOnChangeData(record, {changesOnly: false});
return self._rpc({
model: record.model,
method: 'onchange',
args: [idList, currentData, fields, onchangeSpec, context],
}).then(function (result) {
if (!record._changes) {
// If the _changes key does not exist anymore, it means that
// it was removed by discarding the changes after the rpc
// to onchange. So, in that case, the proper response is to
// ignore the onchange.
return;
}
if (result.domain) {
record._domains = _.extend(record._domains, result.domain);
}
return self._applyOnChange(result.value, record).then(function () {
return result;
});
});
}, },
}); });

View File

@ -37,6 +37,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
update_subtotal: "_onUpdateSubtotal", update_subtotal: "_onUpdateSubtotal",
load_more: "_onLoadMore", load_more: "_onLoadMore",
loading_records: "_onLoadingRecords", loading_records: "_onLoadingRecords",
list_record_remove: "_onListRecordRemove",
}), }),
_auto_search_delay: 450, _auto_search_delay: 450,
@ -438,6 +439,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
discount: "discount", discount: "discount",
}, },
auto_save: false, auto_save: false,
ignore_warning: false,
}; };
}, },
@ -525,7 +527,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
'name': 'main_lines', 'name': 'main_lines',
}; };
this._searchContext.domain = this._getLinesDomain(); this._searchContext.domain = this._getLinesDomain();
this._searchContext.order = false; this._searchContext.order = [{'name': 'sequence'}, {'name': 'id'}];
this._searchContext.activeTest = false; this._searchContext.activeTest = false;
this.doRenderSearchRecords(); this.doRenderSearchRecords();
}, },
@ -580,20 +582,45 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
* @param {CustomEvent} evt * @param {CustomEvent} evt
*/ */
_onCreateQuickRecord: function (evt) { _onCreateQuickRecord: function (evt) {
evt.stopPropagation();
var self = this; var self = this;
this.parent_controller.model.setPureVirtual(evt.data.id, false); this.parent_controller.model.setPureVirtual(evt.data.id, false);
if (!self.options.auto_save) {
self.parent_controller.model.updateRecordContext(evt.data.id, { if (this.options.auto_save) {
// Dont trigger state update
this._setValue(
{operation: "ADD", id: evt.data.id},
{notifyChange: false}
).then(function () {
if (self.options.auto_save) {
self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function (rrr) {
// Because 'create' generates a new state and we can't know these new id we
// need force update the all the current states.
self._setValue(
{operation: "UPDATE", id: evt.data.id},
{doNotSetDirty: true}
).then(function () {
if (evt.data.callback) {
evt.data.callback();
}
});
});
} else if (evt.data.callback) {
evt.data.callback();
}
});
} else {
// This is used to know when need use 'yellow' color
this.parent_controller.model.updateRecordContext(evt.data.id, {
product_picker_modified: true, product_picker_modified: true,
}); });
// This will trigger an "state" update
this._setValue({operation: "ADD", id: evt.data.id}).then(function () {
if (evt.data.callback) {
evt.data.callback();
}
});
} }
this._setValue({operation: "ADD", id: evt.data.id}).then(function () {
if (self.options.auto_save) {
self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function () {
self.renderer.updateState(self.value);
});
}
});
}, },
/** /**
@ -603,17 +630,62 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
* @param {CustomEevent} evt * @param {CustomEevent} evt
*/ */
_onUpdateQuickRecord: function (evt) { _onUpdateQuickRecord: function (evt) {
evt.stopPropagation();
var self = this; var self = this;
if (!self.options.auto_save) {
self.parent_controller.model.updateRecordContext(evt.data.id, { if (this.options.auto_save) {
// Dont trigger state update
this._setValue(
{operation: "UPDATE", id: evt.data.id, data: evt.data.data},
{notifyChange: false}
).then(function () {
if (self.options.auto_save) {
self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function () {
// Workaround to get updated values
self.parent_controller.model.reload(self.value.id).then(function (result) {
var new_data = self.parent_controller.model.get(result);
self.value.data = new_data.data;
self.renderer.updateState(self.value, {force: true});
if (evt.data.callback) {
evt.data.callback();
}
});
});
} else if (evt.data.callback) {
evt.data.callback();
}
});
} else {
// This is used to know when need use 'yellow' color
this.parent_controller.model.updateRecordContext(evt.data.id, {
product_picker_modified: true, product_picker_modified: true,
}); });
// This will trigger an "state" update
this._setValue(
{operation: "UPDATE", id: evt.data.id, data: evt.data.data},
).then(function () {
if (evt.data.callback) {
evt.data.callback();
}
});
} }
this._setValue({operation: "UPDATE", id: evt.data.id, data: evt.data.data}).then(function () { },
/**
* Handle auto_save when remove a record
*/
_onListRecordRemove: function (evt) {
evt.stopPropagation();
var self = this;
this._setValue({operation: "DELETE", ids: [evt.data.id]}).then(function () {
if (self.options.auto_save) { if (self.options.auto_save) {
self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function () { self.parent_controller.saveRecord(undefined, {stayInEdit: true}).then(function () {
self.renderer.updateState(self.value); if (evt.data.callback) {
evt.data.callback();
}
}); });
} else if (evt.data.callback) {
evt.data.callback();
} }
}); });
}, },
@ -674,7 +746,8 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
* *
* @override * @override
*/ */
_setValue: function () {
_setValue: function (value, options) {
var self = this; var self = this;
return this._super.apply(this, arguments).then(function () { return this._super.apply(this, arguments).then(function () {
self.updateBadgeLines(); self.updateBadgeLines();

View File

@ -70,7 +70,7 @@
<t t-name="One2ManyProductPicker.FlipCard"> <t t-name="One2ManyProductPicker.FlipCard">
<div class="oe_flip_container p-1 col-12 col-sm-8 col-md-6 col-lg-4 col-xl-3 col-xxl-2"> <div class="oe_flip_container p-1 col-12 col-sm-8 col-md-6 col-lg-4 col-xl-3 col-xxl-2">
<div t-attf-class="oe_flip_card {{!state &amp;&amp; 'disabled' || ''}}"> <div t-attf-class="oe_flip_card {{!state &amp;&amp; 'disabled' || (auto_save &amp;&amp; !is_virtual &amp;&amp; !state.data.id &amp;&amp; 'blocked') || ''}}">
<div class="oe_flip_card_inner text-center"> <div class="oe_flip_card_inner text-center">
<div t-attf-class="oe_flip_card_front p-0 {{(modified &amp;&amp; 'border-warning') || (state &amp;&amp; !is_virtual &amp;&amp; 'border-success') || ''}}"> <div t-attf-class="oe_flip_card_front p-0 {{(modified &amp;&amp; 'border-warning') || (state &amp;&amp; !is_virtual &amp;&amp; 'border-success') || ''}}">
<t t-if="state"> <t t-if="state">