3
0
Fork 0

[IMP] web_widget_one2many_product_picker-response: User experience

12.0
Alexandre D. Díaz 2021-03-24 22:36:52 +01:00
parent 4dbb75abc4
commit 6a10e3703f
6 changed files with 112 additions and 82 deletions

View File

@ -255,6 +255,7 @@ odoo.define("web_widget_one2many_product_picker.ProductPickerQuickCreateFormView
viewType: "form",
}).then(function () {
var record = self.model.get(self.handle);
self.model.updateRecordContext(self.handle, {saving: true});
self.trigger_up("restore_flip_card", {
success_callback: function () {
self.trigger_up("create_quick_record", {

View File

@ -108,11 +108,13 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
if (state) {
this._setState(state);
}
this.$card.removeClass("blocked");
// Avoid recreate active record
if (this.$card.hasClass("active")) {
this._processDynamicFields();
return $.when();
if (this.$card && this.$card.length) {
this.$card.removeClass("blocked");
// Avoid recreate active record
if (this.$card.hasClass("active")) {
this._processDynamicFields();
return $.when();
}
}
this.on_detach_callback();
@ -187,6 +189,17 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
}
var model = this.options.basicFieldParams.model;
this.is_virtual = this.state && model.isPureVirtual(this.state.id) || false;
// Check if has cached qty
if (this.state && this.state.id) {
const record = model.get(this.state.id);
const lazy_qty = record && record.context.lazy_qty || 0;
if (lazy_qty) {
model.updateRecordContext(this.state.id, {lazy_qty: 0});
// Record already has 1
this._incProductQty(lazy_qty - 1);
}
}
},
/**
@ -214,6 +227,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
modified: record && record.context.product_picker_modified,
active_model: '',
auto_save: this.options.autoSave,
is_saving: record && record.context.saving,
};
},
@ -429,6 +443,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
});
},
_updateLazyQty: function () {
var model = this.options.basicFieldParams.model;
var record = model.get(this.state.id);
this.$el.find(".lazy_product_qty").text(record.context.lazy_qty);
},
/**
* This is a special handle for display the non-fields.
* Similar 't-esc' behaviour.
@ -516,6 +536,8 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
_saveRecord: function () {
var self = this;
var model = this.options.basicFieldParams.model;
model.updateRecordContext(this.state.id, {saving: true});
this.recreate();
var record = model.get(this.state.id);
return model.save(record.id, {
stayInEdit: true,
@ -559,6 +581,12 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
var model = this.options.basicFieldParams.model;
model.updateRecordContext(this.state.id, {ignore_warning: this.options.ignoreWarning});
var record = model.get(this.state.id);
// Because we don't hide the 'add' button when the product is added form back form
// we check if the record is in "saving" mode to prevent duplicate it.
if (record.context.saving) {
return Promise.resolve();
}
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;
@ -583,16 +611,23 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
var model = this.options.basicFieldParams.model;
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);
changes[this.options.fieldMap.product_uom_qty] += amount;
return model.notifyChanges(
record.id,
changes
).then(function () {
self._processDynamicFields();
self._lazyUpdateRecord();
});
if (this.options.autoSave && !this.state.data.id) {
let lazy_qty = record.context.lazy_qty || 1;
lazy_qty += amount;
model.updateRecordContext(this.state.id, {lazy_qty: lazy_qty});
self._updateLazyQty();
} else {
var changes = _.pick(record.data, this.options.fieldMap.product_uom_qty);
changes[this.options.fieldMap.product_uom_qty] += amount;
return model.notifyChanges(
record.id,
changes
).then(function () {
self._processDynamicFields();
self._lazyUpdateRecord();
});
}
},
/**
@ -603,11 +638,6 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
var $currentTarget = $(currentTarget);
var $img = $currentTarget.find(".oe_flip_card_front img");
$target.addClass('o_catch_attention');
$img.addClass('oe_product_picker_catch_attention');
$img.on('animationend', function () {
$img.removeClass('oe_product_picker_catch_attention');
$img.off('animationend');
});
},
/**
@ -645,7 +675,7 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
*/
_onClickFlipCard: function (evt) {
// Avoid clicks on form elements
if (['INPUT', 'BUTTON', 'A'].indexOf(evt.target.tagName) !== -1 || this.$card.hasClass('blocked')) {
if (['INPUT', 'BUTTON', 'A'].indexOf(evt.target.tagName) !== -1) {
return;
}
var $target = $(evt.target);
@ -672,6 +702,11 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRecord", fu
return;
}
}
if (this.$card.hasClass('blocked')) {
return;
}
if (!this._clickFlipCardDelayed) {
this._clickFlipCardDelayed = setTimeout(
this._onClickDelayedFlipCard.bind(this, evt),

View File

@ -260,9 +260,14 @@ odoo.define("web_widget_one2many_product_picker.One2ManyProductPickerRenderer",
widget.recordSearch.id === state.data[this.options.field_map.product].data.id
) {
// Is a new record
// Is a new record (a replace for pure virtual)
search_record_index = widget.$el.index();
search_record = widget.recordSearch;
var model = this.getParent().getBasicFieldParams().model;
var record = model.get(widget.state.id);
model.updateRecordContext(state.id, {
lazy_qty: record.context.lazy_qty || 0,
});
}
// Remove "pure virtual" records that have the same product that the new record

View File

@ -604,6 +604,9 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
}
});
});
if (evt.data.callback) {
evt.data.callback();
}
});
} else {
// This is used to know when need use 'yellow' color
@ -619,6 +622,45 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
}
},
_doUpdateQuickRecord: function (id, data, callback) {
if (this.options.auto_save) {
var self = this;
// Dont trigger state update
this._setValue(
{operation: "UPDATE", id: id, data: data},
{notifyChange: false}
).then(function () {
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 (callback) {
callback();
}
});
});
if (callback) {
callback();
}
});
} else {
// This is used to know when need use 'yellow' color
this.parent_controller.model.updateRecordContext(id, {
product_picker_modified: true,
});
// This will trigger an "state" update
this._setValue(
{operation: "UPDATE", id: id, data: data},
).then(function () {
if (callback) {
callback();
}
});
}
},
/**
* Runs the x2many (1,id,values) command.
*
@ -627,44 +669,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
*/
_onUpdateQuickRecord: function (evt) {
evt.stopPropagation();
var self = this;
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,
});
// 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._doUpdateQuickRecord(evt.data.id, evt.data.data, evt.data.callback);
},
/**

View File

@ -61,10 +61,6 @@
transition: top $one2many-product-picker-transition-3d-time, left $one2many-product-picker-transition-3d-time, width $one2many-product-picker-transition-3d-time, height $one2many-product-picker-transition-3d-time;
height: $one2many-product-picker-card-min-height;
&.blocked {
filter: blur(2px);
}
&.disabled {
filter: grayscale(100%);
opacity: 0.5;
@ -239,20 +235,3 @@
text-align: center;
}
}
.oe_product_picker_catch_attention {
position: relative;
animation: productPickerCatchAttention 200ms normal forwards;
}
@keyframes productPickerCatchAttention {
0% {
transform: scale(1.0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1.0);
}
}

View File

@ -70,15 +70,20 @@
<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 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 t-attf-class="oe_flip_card {{!state &amp;&amp; 'disabled' || (auto_save &amp;&amp; (!is_virtual || is_saving) &amp;&amp; !state.data.id &amp;&amp; 'blocked') || ''}}">
<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 || is_saving) &amp;&amp; 'border-warning') || (state &amp;&amp; !is_virtual &amp;&amp; 'border-success') || ''}}">
<t t-if="state">
<t t-if="!is_virtual">
<div class="safezone position-absolute m-0 pb-2 pr-2 text-left">
<span t-att-data-field="field_map.product_uom_qty" t-attf-data-esc="str({{field_map.product_uom_qty}}) + ' x ' + {{field_map.product_uom}}.data.display_name" t-attf-class="badge {{modified &amp;&amp; 'badge-warning' || 'badge-success'}} font-weight-bold rounded-0 mt-0 px-2 py-3 product_qty" />
</div>
</t>
<t t-elif="is_saving">
<div class="safezone position-absolute m-0 pb-2 pr-2 text-left">
<span class="badge record_saving badge-warning font-weight-bold rounded-0 mt-0 px-2 py-3 product_qty"><span class="lazy_product_qty">1</span> x <t t-esc="state.data[field_map.product_uom].data.display_name"/></span>
</div>
</t>
<t t-else="">
<div class="safezone position-absolute m-0 pb-2 pr-2 text-left">
<span class="badge badge-primary font-weight-bold rounded-0 mt-0 px-2 py-3 add_product"><i class="fa fa-plus"></i> 1 <t t-esc="state.data[field_map.product_uom].data.display_name"/></span>