[FIX] web_kanban, web: KanbanSelection widget refactoring
- Change the widget behavior from asynchronous to asynchronous, by adding 3 related fields on the project.task model, which are the custom labels defined for the stage - Instead of making a rpc call, we are taking directly the labels on the parent for the kanban view, or on the view datarecord for the form view - There are now 2 templates for the kanban view or the form view - The development has been backported in the community version - The options attribute has been removed on the project_view.xml file for the kanban_state field
This commit is contained in:
@@ -453,6 +453,9 @@ class task(osv.osv):
|
||||
'attachment_ids': fields.one2many('ir.attachment', 'res_id', domain=lambda self: [('res_model', '=', self._name)], auto_join=True, string='Attachments'),
|
||||
# In the domain of displayed_image_id, we couln't use attachment_ids because a one2many is represented as a list of commands so we used res_model & res_id
|
||||
'displayed_image_id': fields.many2one('ir.attachment', domain="[('res_model', '=', 'project.task'), ('res_id', '=', id), ('mimetype', 'ilike', 'image')]", string='Displayed Image'),
|
||||
'legend_blocked': fields.related("stage_id", "legend_blocked", type="char", string='Kanban Blocked Explanation'),
|
||||
'legend_done': fields.related("stage_id", "legend_done", type="char", string='Kanban Valid Explanation'),
|
||||
'legend_normal': fields.related("stage_id", "legend_normal", type="char", string='Kanban Ongoing Explanation'),
|
||||
}
|
||||
_defaults = {
|
||||
'stage_id': _get_default_stage_id,
|
||||
@@ -637,7 +640,6 @@ class task(osv.osv):
|
||||
# user_id change: update date_assign
|
||||
if vals.get('user_id'):
|
||||
vals['date_assign'] = fields.datetime.now()
|
||||
|
||||
# context: no_log, because subtype already handle this
|
||||
create_context = dict(context, mail_create_nolog=True)
|
||||
task_id = super(task, self).create(cr, uid, vals, context=create_context)
|
||||
|
||||
@@ -254,11 +254,7 @@
|
||||
options='{"terminology": "archive"}'/>
|
||||
</button>
|
||||
</div>
|
||||
<field name="kanban_state" widget="kanban_state_selection"
|
||||
options='{
|
||||
"states_legend_field": "stage_id",
|
||||
"states_legend": {"normal": "legend_normal", "blocked": "legend_blocked", "done": "legend_done"}
|
||||
}'/>
|
||||
<field name="kanban_state" widget="kanban_state_selection"/>
|
||||
<div class="oe_title">
|
||||
<h1 class="o_row">
|
||||
<field name="priority" widget="priority"/>
|
||||
@@ -274,6 +270,9 @@
|
||||
<field name="planned_hours" widget="float_time"
|
||||
groups="project.group_time_work_estimation_tasks"
|
||||
on_change="onchange_planned(planned_hours)"/>
|
||||
<field name="legend_blocked" invisible="1"/>
|
||||
<field name="legend_normal" invisible="1"/>
|
||||
<field name="legend_done" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date_deadline"/>
|
||||
@@ -329,6 +328,9 @@
|
||||
<field name="attachment_ids"/>
|
||||
<field name="displayed_image_id"/>
|
||||
<field name="active"/>
|
||||
<field name="legend_blocked"/>
|
||||
<field name="legend_normal"/>
|
||||
<field name="legend_done"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
|
||||
@@ -365,8 +367,7 @@
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
<div class="pull-left" groups="base.group_user">
|
||||
<field name="kanban_state" widget="kanban_state_selection"
|
||||
options='{"states_legend": {"normal": "legend_normal", "blocked": "legend_blocked", "done": "legend_done"}}'/>
|
||||
<field name="kanban_state" widget="kanban_state_selection"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -165,6 +165,7 @@ var FieldChar = common.AbstractField.extend(common.ReinitializeFieldMixin, {
|
||||
});
|
||||
|
||||
var KanbanSelection = FieldChar.extend({
|
||||
template: "FormSelection",
|
||||
init: function (field_manager, node) {
|
||||
this._super(field_manager, node);
|
||||
},
|
||||
@@ -180,54 +181,58 @@ var KanbanSelection = FieldChar.extend({
|
||||
prepare_dropdown_selection: function() {
|
||||
var self = this;
|
||||
var _data = [];
|
||||
var fetch_stage;
|
||||
var selection = this.field.selection || [];
|
||||
var stage_id = _.isArray(this.view.datarecord.stage_id) ? this.view.datarecord.stage_id[0] : this.view.datarecord.stage_id;
|
||||
var legend_field = this.options && this.options.states_legend_field || false;
|
||||
var fields_to_read = _.map(
|
||||
this.options && this.options.states_legend || {},
|
||||
function (value, key, list) { return value; });
|
||||
if (legend_field && fields_to_read && stage_id) {
|
||||
fetch_stage = new data.DataSet(
|
||||
this,
|
||||
self.view.fields[legend_field].field.relation).read_ids([stage_id],
|
||||
fields_to_read);
|
||||
}
|
||||
else { fetch_stage = $.Deferred().resolve(false); }
|
||||
return $.when(fetch_stage).then(function (stage_data) {
|
||||
_.map(selection, function(res) {
|
||||
var value = {
|
||||
'name': res[0],
|
||||
'tooltip': res[1],
|
||||
'state_name': res[1],
|
||||
};
|
||||
if (stage_data && stage_data[0][self.options.states_legend[res[0]]]) {
|
||||
value['state_name'] = stage_data[0][self.options.states_legend[res[0]]];
|
||||
value['tooltip'] = stage_data[0][self.options.states_legend[res[0]]];
|
||||
}
|
||||
if (res[0] == 'normal') { value['state_class'] = 'oe_kanban_status'; }
|
||||
else if (res[0] == 'done') { value['state_class'] = 'oe_kanban_status oe_kanban_status_green'; }
|
||||
else { value['state_class'] = 'oe_kanban_status oe_kanban_status_red'; }
|
||||
_data.push(value);
|
||||
});
|
||||
return _data;
|
||||
var current_stage_id = self.view.datarecord.stage_id[0];
|
||||
var stage_data = {
|
||||
id: current_stage_id,
|
||||
legend_normal: self.view.datarecord.legend_normal,
|
||||
legend_blocked : self.view.datarecord.legend_blocked,
|
||||
legend_done: self.view.datarecord.legend_done,
|
||||
};
|
||||
_.map(self.field.selection || [], function(selection_item) {
|
||||
var value = {
|
||||
'name': selection_item[0],
|
||||
'tooltip': selection_item[1],
|
||||
};
|
||||
if (selection_item[0] === 'normal') {
|
||||
value.state_name = stage_data.legend_normal ? stage_data.legend_normal : selection_item[1];
|
||||
} else if (selection_item[0] === 'done') {
|
||||
value.state_class = 'oe_kanban_status_green';
|
||||
value.state_name = stage_data.legend_done ? stage_data.legend_done : selection_item[1];
|
||||
} else {
|
||||
value.state_class = 'oe_kanban_status_red';
|
||||
value.state_name = stage_data.legend_blocked ? stage_data.legend_blocked : selection_item[1];
|
||||
}
|
||||
_data.push(value);
|
||||
});
|
||||
return _data;
|
||||
},
|
||||
render_value: function() {
|
||||
this._super();
|
||||
this.states = this.prepare_dropdown_selection();
|
||||
var self = this;
|
||||
var dd_fetched = this.prepare_dropdown_selection();
|
||||
return $.when(dd_fetched).then(function (states) {
|
||||
self.states = states;
|
||||
self.$el.addClass('oe_right oe_inline');
|
||||
self.$el.html(QWeb.render("KanbanSelection", {'widget': self}));
|
||||
self.$el.find('li').on('click', self.set_kanban_selection.bind(self));
|
||||
// Adapt "FormSelection"
|
||||
var current_state = _.find(this.states, function(state) {
|
||||
return state.name === self.get('value');
|
||||
});
|
||||
this.$('.oe_kanban_status')
|
||||
.removeClass('oe_kanban_status_red oe_kanban_status_green')
|
||||
.addClass(current_state.state_class);
|
||||
|
||||
// Render "FormSelection.Items" and move it into "FormSelection"
|
||||
var $items = $(QWeb.render('FormSelection.items', {
|
||||
states: _.without(this.states, current_state)
|
||||
}));
|
||||
var $dropdown = this.$el.find('.dropdown-menu');
|
||||
$dropdown.children().remove(); // remove old items
|
||||
$items.appendTo($dropdown);
|
||||
this.$el.find('a').on('click', this.set_kanban_selection.bind(this));
|
||||
},
|
||||
/* setting the value: in view mode, perform an asynchronous call and reload
|
||||
the form view; in edit mode, use set_value to save the new value that will
|
||||
be written when saving the record. */
|
||||
set_kanban_selection: function (ev) {
|
||||
var self = this;
|
||||
ev.preventDefault();
|
||||
var li = $(ev.target).closest('li');
|
||||
if (li.length) {
|
||||
var value = String(li.data('value'));
|
||||
|
||||
@@ -679,28 +679,21 @@
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
<t t-name="KanbanSelection">
|
||||
<div class="btn-group kanban_state">
|
||||
<t t-foreach="widget.states" t-as="rec">
|
||||
<a t-if="widget.get('value') === rec.name">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown">
|
||||
<span t-att-class="rec.state_class" t-if="widget.get('value') === rec.name" t-att-title="rec.tooltip"/>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</a>
|
||||
</a>
|
||||
</t>
|
||||
<t t-name="FormSelection">
|
||||
<div class="btn-group o_kanban_selection">
|
||||
<a href="#" data-toggle="dropdown"><span class="oe_kanban_status"/></a>
|
||||
<ul class="dropdown-menu state" role="menu">
|
||||
<t t-foreach="widget.states" t-as="rec">
|
||||
<t t-if="widget.get('value') !== rec.name">
|
||||
<li t-att-data-value="rec.name" ><a href="#">
|
||||
<span t-att-class="rec.state_class" t-att-title="rec.tooltip"/>
|
||||
<t t-raw="rec.state_name" /></a>
|
||||
</li>
|
||||
</t>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FormSelection.items">
|
||||
<li t-foreach="states" t-as="rec" t-att-data-value="rec.name">
|
||||
<a href="#">
|
||||
<span t-att-class="'oe_kanban_status ' + (rec.state_class && rec.state_class || '')"/>
|
||||
<t t-raw="rec.state_name"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
<div t-name="Priority" class="o_priority">
|
||||
<a t-if="!widget.readonly" t-foreach="widget.values.slice(1)" t-as="v" href="#" t-att-data-value="v[0]" t-att-title="v[1]" class="o_priority_star fa"/>
|
||||
<span t-if="widget.readonly" t-foreach="widget.values.slice(1)" t-as="v" t-att-data-value="v[0]" t-att-title="v[1]" class="o_priority_star fa"/>
|
||||
|
||||
@@ -9,7 +9,6 @@ var pyeval = require('web.pyeval');
|
||||
var Registry = require('web.Registry');
|
||||
var session = require('web.session');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
/**
|
||||
* Interface to be implemented by kanban fields.
|
||||
@@ -103,53 +102,52 @@ var KanbanSelection = AbstractField.extend({
|
||||
this.name = $node.attr('name');
|
||||
this.parent = parent;
|
||||
},
|
||||
// Instead of making several rpc calls for each kanban card, take the values from the related fields on the tasks
|
||||
prepare_dropdown_selection: function() {
|
||||
var self = this;
|
||||
var data = [];
|
||||
_.map(this.field.selection || [], function(res) {
|
||||
var _data = [];
|
||||
var stage_id = self.parent.values.stage_id.value[0];
|
||||
var stage_data = {
|
||||
id: stage_id,
|
||||
legend_normal: self.parent.values.legend_normal.value,
|
||||
legend_blocked : self.parent.values.legend_blocked.value,
|
||||
legend_done: self.parent.values.legend_done.value,
|
||||
};
|
||||
_.map(self.field.selection || [], function(res) {
|
||||
var value = {
|
||||
'name': res[0],
|
||||
'tooltip': res[1],
|
||||
'state_name': res[1],
|
||||
};
|
||||
var leg_opt = self.options && self.options.states_legend || null;
|
||||
if (leg_opt) {
|
||||
var key = leg_opt[value.name];
|
||||
var legend = self.parent.group_info && self.parent.group_info[key];
|
||||
if (legend) {
|
||||
value.state_name = legend;
|
||||
value.tooltip = legend;
|
||||
}
|
||||
if (res[0] === 'normal') {
|
||||
value.state_name = stage_data.legend_normal ? stage_data.legend_normal : res[1];
|
||||
} else if (res[0] === 'done') {
|
||||
value.state_class = 'oe_kanban_status_green';
|
||||
value.state_name = stage_data.legend_done ? stage_data.legend_done : res[1];
|
||||
} else {
|
||||
value.state_class = 'oe_kanban_status_red';
|
||||
value.state_name = stage_data.legend_blocked ? stage_data.legend_blocked : res[1];
|
||||
}
|
||||
if (res[0] == 'normal') { value.state_class = 'oe_kanban_status'; }
|
||||
else if (res[0] == 'done') { value.state_class = 'oe_kanban_status oe_kanban_status_green'; }
|
||||
else { value.state_class = 'oe_kanban_status oe_kanban_status_red'; }
|
||||
data.push(value);
|
||||
_data.push(value);
|
||||
});
|
||||
return data;
|
||||
return _data;
|
||||
},
|
||||
renderElement: function() {
|
||||
var self = this;
|
||||
this.states = this.prepare_dropdown_selection();
|
||||
|
||||
var state;
|
||||
for(var i = 0 ; i < this.states.length ; i++) {
|
||||
if(this.states[i].name === this.get('value')) {
|
||||
state = this.states[i];
|
||||
}
|
||||
}
|
||||
self.states = this.prepare_dropdown_selection();
|
||||
|
||||
this.$el = $(QWeb.render("KanbanSelection", {'widget': this}));
|
||||
this.$('a').first().find('span').removeClass().addClass(state.state_class);
|
||||
this.$('ul li').show().filter(function() {
|
||||
return ($(this).data('value') === state.name);
|
||||
}).hide();
|
||||
|
||||
this.$('a').click(function (ev) {
|
||||
ev.preventDefault();
|
||||
var current_state = _.find(this.states, function(state) {
|
||||
return state.name === self.get('value');
|
||||
});
|
||||
|
||||
this.$('li').click(this.set_kanban_selection.bind(this));
|
||||
self.$el = $(QWeb.render("KanbanSelection", {
|
||||
current_state_class: current_state.state_class,
|
||||
states: _.without(this.states, current_state)
|
||||
}));
|
||||
self.$('a').click(function (ev) {
|
||||
ev.preventDefault();
|
||||
});
|
||||
self.$('a').click(self.set_kanban_selection.bind(self));
|
||||
},
|
||||
set_kanban_selection: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -84,4 +84,21 @@
|
||||
<img t-att-src="'/web/image/' + widget.field.raw_value[0] + '?unique=1'"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="KanbanSelection">
|
||||
<div class="btn-group o_kanban_selection">
|
||||
<a href="#" data-toggle="dropdown">
|
||||
<span t-att-class="'oe_kanban_status ' + (current_state_class && current_state_class || '')"/>
|
||||
</a>
|
||||
<ul class="dropdown-menu state" role="menu">
|
||||
<li t-foreach="states" t-as="rec" t-att-data-value="rec.name">
|
||||
<a href="#">
|
||||
<span t-att-class="'oe_kanban_status ' + (rec.state_class && rec.state_class || '')"/>
|
||||
<t t-raw="rec.state_name"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user