3
0
Fork 0

[IMP] web_widget_x2many_2d_matrix: black, isort

13.0
Adrià Gil Sorribes 2020-02-14 10:02:14 +01:00
parent 333a67ca13
commit 9bd27d63b0
9 changed files with 428 additions and 264 deletions

View File

@ -3,24 +3,20 @@
# Copyright 2018 Simone Orsi <simone.orsi@camptocamp.com> # Copyright 2018 Simone Orsi <simone.orsi@camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': '2D matrix for x2many fields', "name": "2D matrix for x2many fields",
'version': '12.0.2.2.0', "version": "12.0.2.2.0",
'author': ( "author": (
'Therp BV, ' "Therp BV, "
'Tecnativa, ' "Tecnativa, "
'Camptocamp, ' "Camptocamp, "
'Brainbean Apps, ' "Brainbean Apps, "
'Odoo Community Association (OCA)' "Odoo Community Association (OCA)"
), ),
'website': 'https://github.com/OCA/web', "website": "https://github.com/OCA/web",
'license': 'AGPL-3', "license": "AGPL-3",
'category': 'Hidden/Dependency', "category": "Hidden/Dependency",
'summary': 'Show list fields as a matrix', "summary": "Show list fields as a matrix",
'depends': [ "depends": ["web",],
'web', "data": ["views/assets.xml",],
], "installable": True,
'data': [
'views/assets.xml',
],
'installable': True,
} }

View File

@ -2,30 +2,29 @@
* Copyright 2018 Brainbean Apps * Copyright 2018 Brainbean Apps
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (require) { odoo.define("web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer", function(require) {
"use strict"; "use strict";
var BasicRenderer = require('web.BasicRenderer'); var BasicRenderer = require("web.BasicRenderer");
var config = require('web.config'); var config = require("web.config");
var core = require('web.core'); var core = require("web.core");
var field_utils = require('web.field_utils'); var field_utils = require("web.field_utils");
var _t = core._t; var _t = core._t;
var FIELD_CLASSES = { var FIELD_CLASSES = {
float: 'o_list_number', float: "o_list_number",
integer: 'o_list_number', integer: "o_list_number",
monetary: 'o_list_number', monetary: "o_list_number",
text: 'o_list_text', text: "o_list_text",
}; };
// X2Many2dMatrixRenderer is heavily inspired by Odoo's ListRenderer // X2Many2dMatrixRenderer is heavily inspired by Odoo's ListRenderer
// and is reusing portions of code from list_renderer.js // and is reusing portions of code from list_renderer.js
var X2Many2dMatrixRenderer = BasicRenderer.extend({ var X2Many2dMatrixRenderer = BasicRenderer.extend({
/** /**
* @override * @override
*/ */
init: function (parent, state, params) { init: function(parent, state, params) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.editable = params.editable; this.editable = params.editable;
this._saveMatrixData(params.matrix_data); this._saveMatrixData(params.matrix_data);
@ -36,7 +35,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* *
* @param {Object} matrixData Contains the matrix data * @param {Object} matrixData Contains the matrix data
*/ */
_saveMatrixData: function (matrixData) { _saveMatrixData: function(matrixData) {
this.columns = matrixData.columns; this.columns = matrixData.columns;
this.rows = matrixData.rows; this.rows = matrixData.rows;
this.matrix_data = matrixData; this.matrix_data = matrixData;
@ -52,35 +51,29 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {Deferred} this deferred is resolved immediately * @returns {Deferred} this deferred is resolved immediately
*/ */
_renderView: function () { _renderView: function() {
var self = this; var self = this;
this.$el this.$el.removeClass("table-responsive").empty();
.removeClass('table-responsive')
.empty();
// Display a nice message if there's no data to display // Display a nice message if there's no data to display
if (!self.rows.length) { if (!self.rows.length) {
var $alert = $('<div>', {'class': 'alert alert-info'}); var $alert = $("<div>", {class: "alert alert-info"});
$alert.text(_t('Sorry no matrix data to display.')); $alert.text(_t("Sorry no matrix data to display."));
this.$el.append($alert); this.$el.append($alert);
return this._super(); return this._super();
} }
var $table = $('<table>').addClass( var $table = $("<table>").addClass(
'o_list_view table table-condensed table-striped ' + "o_list_view table table-condensed table-striped " +
'o_x2many_2d_matrix ' "o_x2many_2d_matrix "
); );
this.$el this.$el.addClass("table-responsive").append($table);
.addClass('table-responsive')
.append($table);
this._computeColumnAggregates(); this._computeColumnAggregates();
this._computeRowAggregates(); this._computeRowAggregates();
$table $table.append(this._renderHeader()).append(this._renderBody());
.append(this._renderHeader())
.append(this._renderBody());
if (self.matrix_data.show_column_totals) { if (self.matrix_data.show_column_totals) {
$table.append(this._renderFooter()); $table.append(this._renderFooter());
} }
@ -96,10 +89,10 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {jQueryElement} The table body element just filled. * @returns {jQueryElement} The table body element just filled.
*/ */
_renderBody: function () { _renderBody: function() {
var $body = $('<tbody>').append(this._renderRows()); var $body = $("<tbody>").append(this._renderRows());
_.each($body.find('input'), function (td, i) { _.each($body.find("input"), function(td, i) {
$(td).attr('tabindex', i); $(td).attr("tabindex", i);
}); });
return $body; return $body;
}, },
@ -111,16 +104,13 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {jQueryElement} The thead element that was inserted into. * @returns {jQueryElement} The thead element that was inserted into.
*/ */
_renderHeader: function () { _renderHeader: function() {
var $tr = $('<tr>').append('<th/>'); var $tr = $("<tr>").append("<th/>");
$tr = $tr.append(_.map( $tr = $tr.append(_.map(this.columns, this._renderHeaderCell.bind(this)));
this.columns,
this._renderHeaderCell.bind(this)
));
if (this.matrix_data.show_row_totals) { if (this.matrix_data.show_row_totals) {
$tr.append($('<th/>', {class: 'total'})); $tr.append($("<th/>", {class: "total"}));
} }
return $('<thead>').append($tr); return $("<thead>").append($tr);
}, },
/** /**
@ -132,30 +122,31 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @param {jQueryElement} node * @param {jQueryElement} node
* @returns {jQueryElement} the created <th> node. * @returns {jQueryElement} the created <th> node.
*/ */
_renderHeaderCell: function (node) { _renderHeaderCell: function(node) {
var name = node.attrs.name; var name = node.attrs.name;
var field = this.state.fields[name]; var field = this.state.fields[name];
var $th = $('<th>'); var $th = $("<th>");
if (!field) { if (!field) {
return $th; return $th;
} }
var description = null; var description = null;
if (node.attrs.widget) { if (node.attrs.widget) {
description = this.state.fieldsInfo.list[name] description = this.state.fieldsInfo.list[name].Widget.prototype
.Widget.prototype.description; .description;
} }
if (_.isNull(description)) { if (_.isNull(description)) {
description = node.attrs.string || field.string; description = node.attrs.string || field.string;
} }
$th.text(description).data('name', name); $th.text(description).data("name", name);
if ( if (
field.type === 'float' || field.type === 'integer' || field.type === "float" ||
field.type === 'monetary' field.type === "integer" ||
field.type === "monetary"
) { ) {
$th.addClass('text-right'); $th.addClass("text-right");
} else { } else {
$th.addClass('text-center'); $th.addClass("text-center");
} }
if (config.debug) { if (config.debug) {
@ -177,11 +168,14 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {String} a string with the generated html. * @returns {String} a string with the generated html.
*/ */
_renderRows: function () { _renderRows: function() {
return _.map(this.rows, function (row) { return _.map(
row.attrs.name = this.matrix_data.field_value; this.rows,
return this._renderRow(row); function(row) {
}.bind(this)); row.attrs.name = this.matrix_data.field_value;
return this._renderRow(row);
}.bind(this)
);
}, },
/** /**
@ -194,16 +188,19 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @param {Object} row The row that will be rendered. * @param {Object} row The row that will be rendered.
* @returns {jQueryElement} the <tr> element that has been rendered. * @returns {jQueryElement} the <tr> element that has been rendered.
*/ */
_renderRow: function (row) { _renderRow: function(row) {
var $tr = $('<tr/>', {class: 'o_data_row'}), var $tr = $("<tr/>", {class: "o_data_row"}),
_data = _.without(row.data, undefined); _data = _.without(row.data, undefined);
$tr = $tr.append(this._renderLabelCell(_data[0])); $tr = $tr.append(this._renderLabelCell(_data[0]));
var $cells = _.map(this.columns, function (column, index) { var $cells = _.map(
var record = row.data[index]; this.columns,
// Make the widget use our field value for each cell function(column, index) {
column.attrs.name = this.matrix_data.field_value; var record = row.data[index];
return this._renderBodyCell(record, column, index, {mode:''}); // Make the widget use our field value for each cell
}.bind(this)); column.attrs.name = this.matrix_data.field_value;
return this._renderBodyCell(record, column, index, {mode: ""});
}.bind(this)
);
$tr = $tr.append($cells); $tr = $tr.append($cells);
if (row.aggregate) { if (row.aggregate) {
$tr.append(this._renderAggregateRowCell(row)); $tr.append(this._renderAggregateRowCell(row));
@ -218,10 +215,10 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @param {Object} record Contains the information about the record. * @param {Object} record Contains the information about the record.
* @returns {jQueryElement} the cell that was rendered. * @returns {jQueryElement} the cell that was rendered.
*/ */
_renderLabelCell: function (record) { _renderLabelCell: function(record) {
var $td = $('<td>'); var $td = $("<td>");
var value = record.data[this.matrix_data.field_y_axis]; var value = record.data[this.matrix_data.field_y_axis];
if (value.type === 'record') { if (value.type === "record") {
// We have a related record // We have a related record
value = value.data.display_name; value = value.data.display_name;
} }
@ -237,8 +234,8 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @param {Object} row the row object to aggregate. * @param {Object} row the row object to aggregate.
* @returns {jQueryElement} The rendered cell. * @returns {jQueryElement} The rendered cell.
*/ */
_renderAggregateRowCell: function (row) { _renderAggregateRowCell: function(row) {
var $cell = $('<td/>', {class: 'row-total'}); var $cell = $("<td/>", {class: "row-total"});
this.applyAggregateValue($cell, row); this.applyAggregateValue($cell, row);
return $cell; return $cell;
}, },
@ -255,25 +252,23 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @param {Object} options The obtions used for the widget * @param {Object} options The obtions used for the widget
* @returns {jQueryElement} the rendered cell. * @returns {jQueryElement} the rendered cell.
*/ */
_renderBodyCell: function (record, node, colIndex, options) { _renderBodyCell: function(record, node, colIndex, options) {
var tdClassName = 'o_data_cell'; var tdClassName = "o_data_cell";
if (node.tag === 'field') { if (node.tag === "field") {
var typeClass = FIELD_CLASSES[ var typeClass = FIELD_CLASSES[this.state.fields[node.attrs.name].type];
this.state.fields[node.attrs.name].type
];
if (typeClass) { if (typeClass) {
tdClassName += ' ' + typeClass; tdClassName += " " + typeClass;
} }
if (node.attrs.widget) { if (node.attrs.widget) {
tdClassName += ' o_' + node.attrs.widget + '_cell'; tdClassName += " o_" + node.attrs.widget + "_cell";
} }
} }
// TODO roadmap: here we should collect possible extra params // TODO roadmap: here we should collect possible extra params
// the user might want to attach to each single cell. // the user might want to attach to each single cell.
var $td = $('<td>', { var $td = $("<td>", {
'class': tdClassName, class: tdClassName,
}); });
if (_.isUndefined(record)) { if (_.isUndefined(record)) {
@ -281,8 +276,8 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
return $td; return $td;
} }
$td.attr({ $td.attr({
'data-form-id': record.id, "data-form-id": record.id,
'data-id': record.data.id, "data-id": record.data.id,
}); });
// We register modifiers on the <td> element so that it gets // We register modifiers on the <td> element so that it gets
@ -291,7 +286,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
node, node,
record, record,
$td, $td,
_.pick(options, 'mode') _.pick(options, "mode")
); );
// If the invisible modifiers is true, the <td> element is // If the invisible modifiers is true, the <td> element is
// left empty. Indeed, if the modifiers was to change the // left empty. Indeed, if the modifiers was to change the
@ -303,10 +298,10 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
// Enforce mode of the parent // Enforce mode of the parent
options.mode = this.getParent().mode; options.mode = this.getParent().mode;
if (node.tag === 'widget') { if (node.tag === "widget") {
return $td.append(this._renderWidget(record, node)); return $td.append(this._renderWidget(record, node));
} }
var $el = this._renderFieldWidget(node, record, _.pick(options, 'mode')); var $el = this._renderFieldWidget(node, record, _.pick(options, "mode"));
this._handleAttributes($el, node); this._handleAttributes($el, node);
return $td.append($el); return $td.append($el);
}, },
@ -317,15 +312,17 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {jQueryElement} The footer element with the cells in it. * @returns {jQueryElement} The footer element with the cells in it.
*/ */
_renderFooter: function () { _renderFooter: function() {
var $cells = this._renderAggregateColCells(); var $cells = this._renderAggregateColCells();
if ($cells) { if ($cells) {
var $tr = $('<tr>').append('<td/>').append($cells); var $tr = $("<tr>")
.append("<td/>")
.append($cells);
var $total_cell = this._renderTotalCell(); var $total_cell = this._renderTotalCell();
if ($total_cell) { if ($total_cell) {
$tr.append($total_cell); $tr.append($total_cell);
} }
return $('<tfoot>').append($tr); return $("<tfoot>").append($tr);
} }
}, },
@ -335,13 +332,15 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {jQueryElement} The td element with the total in it. * @returns {jQueryElement} The td element with the total in it.
*/ */
_renderTotalCell: function () { _renderTotalCell: function() {
if (!this.matrix_data.show_column_totals || if (
!this.matrix_data.show_row_totals) { !this.matrix_data.show_column_totals ||
!this.matrix_data.show_row_totals
) {
return; return;
} }
var $cell = $('<td>', {class: 'col-total'}); var $cell = $("<td>", {class: "col-total"});
this.applyAggregateValue($cell, this.total); this.applyAggregateValue($cell, this.total);
return $cell; return $cell;
}, },
@ -352,16 +351,16 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @returns {List} the rendered cells * @returns {List} the rendered cells
*/ */
_renderAggregateColCells: function () { _renderAggregateColCells: function() {
var self = this; var self = this;
return _.map(this.columns, function (column) { return _.map(this.columns, function(column) {
var $cell = $('<td>'); var $cell = $("<td>");
if (config.debug) { if (config.debug) {
$cell.addClass(column.attrs.name); $cell.addClass(column.attrs.name);
} }
if (column.aggregate) { if (column.aggregate) {
self.applyAggregateValue($cell, column) self.applyAggregateValue($cell, column);
} }
return $cell; return $cell;
}); });
@ -373,7 +372,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* *
* @private * @private
*/ */
_computeColumnAggregates: function () { _computeColumnAggregates: function() {
if (!this.matrix_data.show_column_totals) { if (!this.matrix_data.show_column_totals) {
return; return;
} }
@ -383,7 +382,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
return; return;
} }
var type = field.type; var type = field.type;
if (!~['integer', 'float', 'monetary'].indexOf(type)) { if (!~["integer", "float", "monetary"].indexOf(type)) {
return; return;
} }
this.total = { this.total = {
@ -391,31 +390,34 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
name: fname, name: fname,
}, },
aggregate: { aggregate: {
help: _t('Sum Total'), help: _t("Sum Total"),
value: 0, value: 0,
}, },
}; };
_.each(this.columns, function (column, index) { _.each(
column.aggregate = { this.columns,
help: _t('Sum'), function(column, index) {
value: 0, column.aggregate = {
}; help: _t("Sum"),
_.each(this.rows, function (row) { value: 0,
// TODO Use only one _.propertyOf in underscore 1.9.0+ };
try { _.each(this.rows, function(row) {
column.aggregate.value += row.data[index].data[fname]; // TODO Use only one _.propertyOf in underscore 1.9.0+
} catch (error) { try {
// Nothing to do column.aggregate.value += row.data[index].data[fname];
} } catch (error) {
}); // Nothing to do
this.total.aggregate.value += column.aggregate.value; }
}.bind(this)); });
this.total.aggregate.value += column.aggregate.value;
}.bind(this)
);
}, },
/** /**
* @override * @override
*/ */
updateState: function (state, params) { updateState: function(state, params) {
if (params.matrix_data) { if (params.matrix_data) {
this._saveMatrixData(params.matrix_data); this._saveMatrixData(params.matrix_data);
} }
@ -423,13 +425,13 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
}, },
/** /**
* Traverse the fields matrix with the keyboard * Traverse the fields matrix with the keyboard
* *
* @override * @override
* @private * @private
* @param {OdooEvent} event "navigation_move" event * @param {OdooEvent} event "navigation_move" event
*/ */
_onNavigationMove: function (event) { _onNavigationMove: function(event) {
var widgets = this.__parentedChildren, var widgets = this.__parentedChildren,
index = widgets.indexOf(event.target), index = widgets.indexOf(event.target),
first = index === 0, first = index === 0,
@ -457,7 +459,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* *
* @private * @private
*/ */
_computeRowAggregates: function () { _computeRowAggregates: function() {
if (!this.matrix_data.show_row_totals) { if (!this.matrix_data.show_row_totals) {
return; return;
} }
@ -467,15 +469,15 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
return; return;
} }
var type = field.type; var type = field.type;
if (!~['integer', 'float', 'monetary'].indexOf(type)) { if (!~["integer", "float", "monetary"].indexOf(type)) {
return; return;
} }
_.each(this.rows, function (row) { _.each(this.rows, function(row) {
row.aggregate = { row.aggregate = {
help: _t('Sum'), help: _t("Sum"),
value: 0, value: 0,
}; };
_.each(row.data, function (col) { _.each(row.data, function(col) {
// TODO Use _.property in underscore 1.9+ // TODO Use _.property in underscore 1.9+
try { try {
row.aggregate.value += col.data[fname]; row.aggregate.value += col.data[fname];
@ -497,16 +499,18 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @param {Object} axis * @param {Object} axis
* The object which contains the information about the aggregate value axis * The object which contains the information about the aggregate value axis
*/ */
applyAggregateValue: function ($cell, axis) { applyAggregateValue: function($cell, axis) {
var field = this.state.fields[axis.attrs.name]; var field = this.state.fields[axis.attrs.name];
var value = axis.aggregate.value; var value = axis.aggregate.value;
var help = axis.aggregate.help; var help = axis.aggregate.help;
var fieldInfo = this.state.fieldsInfo.list[axis.attrs.name]; var fieldInfo = this.state.fieldsInfo.list[axis.attrs.name];
var formatFunc = field_utils.format[ var formatFunc =
fieldInfo.widget ? fieldInfo.widget : field.type field_utils.format[fieldInfo.widget ? fieldInfo.widget : field.type];
]; var formattedValue = formatFunc(value, field, {escape: true});
var formattedValue = formatFunc(value, field, { escape: true }); $cell
$cell.addClass('o_list_number').attr('title', help).html(formattedValue); .addClass("o_list_number")
.attr("title", help)
.html(formattedValue);
}, },
/** /**
@ -528,10 +532,10 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {Deferred} * @returns {Deferred}
* The deferred object thats gonna be resolved when the change is made. * The deferred object thats gonna be resolved when the change is made.
*/ */
confirmUpdate: function (state, id, fields, ev) { confirmUpdate: function(state, id, fields, ev) {
var self = this; var self = this;
this.state = state; this.state = state;
return this.confirmChange(state, id, fields, ev).then(function () { return this.confirmChange(state, id, fields, ev).then(function() {
self._refresh(id); self._refresh(id);
}); });
}, },
@ -542,7 +546,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @private * @private
* @param {String} id Datapoint ID * @param {String} id Datapoint ID
*/ */
_refresh: function (id) { _refresh: function(id) {
this._updateRow(id); this._updateRow(id);
this._refreshColTotals(); this._refreshColTotals();
this._refreshRowTotals(); this._refreshRowTotals();
@ -553,11 +557,11 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* *
* @param {String} id: The id of the row that needs to be updated. * @param {String} id: The id of the row that needs to be updated.
*/ */
_updateRow: function (id) { _updateRow: function(id) {
var record = _.findWhere(this.state.data, {id: id}), var record = _.findWhere(this.state.data, {id: id}),
_id = _.property("id"); _id = _.property("id");
_.each(this.rows, function (row) { _.each(this.rows, function(row) {
_.each(row.data, function (col, i) { _.each(row.data, function(col, i) {
if (_id(col) === id) { if (_id(col) === id) {
row.data[i] = record; row.data[i] = record;
} }
@ -568,21 +572,22 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
/** /**
* Update the row total. * Update the row total.
*/ */
_refreshColTotals: function () { _refreshColTotals: function() {
this._computeColumnAggregates(); this._computeColumnAggregates();
this.$('tfoot').replaceWith(this._renderFooter()); this.$("tfoot").replaceWith(this._renderFooter());
}, },
/** /**
* Update the column total. * Update the column total.
*/ */
_refreshRowTotals: function () { _refreshRowTotals: function() {
var self = this; var self = this;
this._computeRowAggregates(); this._computeRowAggregates();
var $rows = self.$el.find('tr.o_data_row'); var $rows = self.$el.find("tr.o_data_row");
_.each(self.rows, function (row, i) { _.each(self.rows, function(row, i) {
if (row.aggregate) { if (row.aggregate) {
$($rows[i]).find('.row-total') $($rows[i])
.find(".row-total")
.replaceWith(self._renderAggregateRowCell(row)); .replaceWith(self._renderAggregateRowCell(row));
} }
}); });
@ -593,10 +598,9 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* *
* @returns {null} * @returns {null}
*/ */
getEditableRecordID: function () { getEditableRecordID: function() {
return null; return null;
}, },
}); });
return X2Many2dMatrixRenderer; return X2Many2dMatrixRenderer;

View File

@ -1,23 +1,22 @@
/* Copyright 2019 Alexandre Díaz /* Copyright 2019 Alexandre Díaz
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixView', function (require) { odoo.define("web_widget_x2many_2d_matrix.X2Many2dMatrixView", function(require) {
"use strict"; "use strict";
var BasicView = require('web.BasicView'); var BasicView = require("web.BasicView");
BasicView.include({ BasicView.include({
_processField: function (viewType, field, attrs) { _processField: function(viewType, field, attrs) {
// Workaround for kanban mode rendering. // Workaround for kanban mode rendering.
// Source of the issue: https://github.com/OCA/OCB/blob/12.0/addons/web/static/src/js/views/basic/basic_view.js#L303 . // Source of the issue: https://github.com/OCA/OCB/blob/12.0/addons/web/static/src/js/views/basic/basic_view.js#L303 .
// See https://github.com/OCA/web/pull/1404#pullrequestreview-305813206 . // See https://github.com/OCA/web/pull/1404#pullrequestreview-305813206 .
// In the long term we should a way to handle kanban mode // In the long term we should a way to handle kanban mode
// better (eg: a specific renderer). // better (eg: a specific renderer).
if (attrs.widget === 'x2many_2d_matrix') { if (attrs.widget === "x2many_2d_matrix") {
attrs.mode = 'tree'; attrs.mode = "tree";
} }
return this._super(viewType, field, attrs); return this._super(viewType, field, attrs);
}, },
}); });
}); });

View File

@ -1,4 +1,4 @@
odoo.define( "web_widget_x2many_2d_matrix.matrix_limit_extend", function (require) { odoo.define("web_widget_x2many_2d_matrix.matrix_limit_extend", function(require) {
"use strict"; "use strict";
var FormView = require("web.FormView"); var FormView = require("web.FormView");
@ -6,7 +6,7 @@ odoo.define( "web_widget_x2many_2d_matrix.matrix_limit_extend", function (requir
FormView.include({ FormView.include({
// We extend this method so that the view is not limited to // We extend this method so that the view is not limited to
// just 40 cells when the 'x2many_2d_matrix' widget is used. // just 40 cells when the 'x2many_2d_matrix' widget is used.
_setSubViewLimit: function (attrs) { _setSubViewLimit: function(attrs) {
this._super(attrs); this._super(attrs);
if (attrs.widget === "x2many_2d_matrix") { if (attrs.widget === "x2many_2d_matrix") {
attrs.limit = Infinity; attrs.limit = Infinity;

View File

@ -3,17 +3,15 @@
* Copyright 2018 Simone Orsi <simone.orsi@camptocamp.com> * Copyright 2018 Simone Orsi <simone.orsi@camptocamp.com>
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('web_widget_x2many_2d_matrix.widget', function (require) { odoo.define("web_widget_x2many_2d_matrix.widget", function(require) {
"use strict"; "use strict";
var field_registry = require('web.field_registry'); var field_registry = require("web.field_registry");
var relational_fields = require('web.relational_fields'); var relational_fields = require("web.relational_fields");
var X2Many2dMatrixRenderer = require( var X2Many2dMatrixRenderer = require("web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer");
'web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer'
);
var WidgetX2Many2dMatrix = relational_fields.FieldOne2Many.extend({ var WidgetX2Many2dMatrix = relational_fields.FieldOne2Many.extend({
widget_class: 'o_form_field_x2many_2d_matrix', widget_class: "o_form_field_x2many_2d_matrix",
/** /**
* Initialize the widget & parameters. * Initialize the widget & parameters.
@ -23,7 +21,7 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* @param {Object} record information about the database records. * @param {Object} record information about the database records.
* @param {Object} options view options. * @param {Object} options view options.
*/ */
init: function (parent, name, record, options) { init: function(parent, name, record, options) {
this._super(parent, name, record, options); this._super(parent, name, record, options);
this.init_params(); this.init_params();
}, },
@ -32,47 +30,46 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* Initialize the widget specific parameters. * Initialize the widget specific parameters.
* Sets the axis and the values. * Sets the axis and the values.
*/ */
init_params: function () { init_params: function() {
var node = this.attrs; var node = this.attrs;
this.by_y_axis = {}; this.by_y_axis = {};
this.x_axis = []; this.x_axis = [];
this.y_axis = []; this.y_axis = [];
this.field_x_axis = node.field_x_axis || this.field_x_axis; this.field_x_axis = node.field_x_axis || this.field_x_axis;
this.field_y_axis = node.field_y_axis || this.field_y_axis; this.field_y_axis = node.field_y_axis || this.field_y_axis;
this.field_label_x_axis = this.field_label_x_axis = node.field_label_x_axis || this.field_x_axis;
node.field_label_x_axis || this.field_x_axis; this.field_label_y_axis = node.field_label_y_axis || this.field_y_axis;
this.field_label_y_axis = this.x_axis_clickable = this.parse_boolean(node.x_axis_clickable || "1");
node.field_label_y_axis || this.field_y_axis; this.y_axis_clickable = this.parse_boolean(node.y_axis_clickable || "1");
this.x_axis_clickable = this.parse_boolean(
node.x_axis_clickable || '1'
);
this.y_axis_clickable = this.parse_boolean(
node.y_axis_clickable || '1'
);
this.field_value = node.field_value || this.field_value; this.field_value = node.field_value || this.field_value;
// TODO: is this really needed? Holger? // TODO: is this really needed? Holger?
for (var property in node) { for (var property in node) {
if (property.startsWith("field_att_")) { if (property.startsWith("field_att_")) {
this.fields_att[property.substring(10)] = this.fields_att[property.substring(10)] = node[property];
node[property];
} }
} }
var field_defs = this.recordData[this.name].fields; var field_defs = this.recordData[this.name].fields;
// TODO: raise when any of the fields above don't exist with a // TODO: raise when any of the fields above don't exist with a
// helpful error message // helpful error message
if (!field_defs[this.field_value]) { if (!field_defs[this.field_value]) {
throw new Error(_.str.sprintf( throw new Error(
'You need to include %s in your view definition', _.str.sprintf(
this.field_value "You need to include %s in your view definition",
)); this.field_value
)
);
} }
this.show_row_totals = this.parse_boolean( this.show_row_totals = this.parse_boolean(
node.show_row_totals || node.show_row_totals ||
this.is_aggregatable(field_defs[this.field_value]) ? '1' : '' this.is_aggregatable(field_defs[this.field_value])
? "1"
: ""
); );
this.show_column_totals = this.parse_boolean( this.show_column_totals = this.parse_boolean(
node.show_column_totals || node.show_column_totals ||
this.is_aggregatable(field_defs[this.field_value]) ? '1' : '' this.is_aggregatable(field_defs[this.field_value])
? "1"
: ""
); );
}, },
@ -82,49 +79,58 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* Puts the values in the grid. * Puts the values in the grid.
* If we have related items we use the display name. * If we have related items we use the display name.
*/ */
init_matrix: function () { init_matrix: function() {
var records = this.recordData[this.name].data; var records = this.recordData[this.name].data;
// Wipe the content if something still exists // Wipe the content if something still exists
this.by_y_axis = {}; this.by_y_axis = {};
this.x_axis = []; this.x_axis = [];
this.y_axis = []; this.y_axis = [];
_.each(records, function (record) { _.each(
var x = record.data[this.field_x_axis], records,
y = record.data[this.field_y_axis]; function(record) {
if (x.type === 'record') { var x = record.data[this.field_x_axis],
// We have a related record y = record.data[this.field_y_axis];
x = x.data.display_name; if (x.type === "record") {
} // We have a related record
if (y.type === 'record') { x = x.data.display_name;
// We have a related record }
y = y.data.display_name; if (y.type === "record") {
} // We have a related record
this.by_y_axis[y] = this.by_y_axis[y] || {}; y = y.data.display_name;
this.by_y_axis[y][x] = record; }
if (this.y_axis.indexOf(y) === -1) { this.by_y_axis[y] = this.by_y_axis[y] || {};
this.y_axis.push(y); this.by_y_axis[y][x] = record;
} if (this.y_axis.indexOf(y) === -1) {
if (this.x_axis.indexOf(x) === -1) { this.y_axis.push(y);
this.x_axis.push(x); }
} if (this.x_axis.indexOf(x) === -1) {
}.bind(this)); this.x_axis.push(x);
}
}.bind(this)
);
// Init columns // Init columns
this.columns = []; this.columns = [];
_.each(this.x_axis, function (x) { _.each(
this.columns.push(this._make_column(x)); this.x_axis,
}.bind(this)); function(x) {
this.columns.push(this._make_column(x));
}.bind(this)
);
this.rows = []; this.rows = [];
_.each(this.y_axis, function (y) { _.each(
this.rows.push(this._make_row(y)); this.y_axis,
}.bind(this)); function(y) {
this.rows.push(this._make_row(y));
}.bind(this)
);
this.matrix_data = { this.matrix_data = {
'field_value': this.field_value, field_value: this.field_value,
'field_x_axis': this.field_x_axis, field_x_axis: this.field_x_axis,
'field_y_axis': this.field_y_axis, field_y_axis: this.field_y_axis,
'columns': this.columns, columns: this.columns,
'rows': this.rows, rows: this.rows,
'show_row_totals': this.show_row_totals, show_row_totals: this.show_row_totals,
'show_column_totals': this.show_column_totals, show_column_totals: this.show_column_totals,
}; };
}, },
@ -134,13 +140,13 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* @param {String} x The string used as a column title * @param {String} x The string used as a column title
* @returns {Object} * @returns {Object}
*/ */
_make_column: function (x) { _make_column: function(x) {
return { return {
// Simulate node parsed on xml arch // Simulate node parsed on xml arch
'tag': 'field', tag: "field",
'attrs': { attrs: {
'name': this.field_x_axis, name: this.field_x_axis,
'string': x, string: x,
}, },
}; };
}, },
@ -151,18 +157,18 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* @param {String} y The string used as a row title * @param {String} y The string used as a row title
* @returns {Object} * @returns {Object}
*/ */
_make_row: function (y) { _make_row: function(y) {
var self = this; var self = this;
// Use object so that we can attach more data if needed // Use object so that we can attach more data if needed
var row = { var row = {
'tag': 'field', tag: "field",
'attrs': { attrs: {
'name': this.field_y_axis, name: this.field_y_axis,
'string': y, string: y,
}, },
'data': [], data: [],
}; };
_.each(self.x_axis, function (x) { _.each(self.x_axis, function(x) {
row.data.push(self.by_y_axis[y][x]); row.data.push(self.by_y_axis[y][x]);
}); });
return row; return row;
@ -171,7 +177,7 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
/** /**
* Determine if a field represented by field_def can be aggregated * Determine if a field represented by field_def can be aggregated
*/ */
is_aggregatable: function (field_def) { is_aggregatable: function(field_def) {
return field_def.type in {float: 1, monetary: 1, integer: 1}; return field_def.type in {float: 1, monetary: 1, integer: 1};
}, },
@ -181,8 +187,8 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* @param {String} val: the string to be parsed. * @param {String} val: the string to be parsed.
* @returns {Boolean} The parsed boolean. * @returns {Boolean} The parsed boolean.
*/ */
parse_boolean: function (val) { parse_boolean: function(val) {
if (val.toLowerCase() === 'true' || val === '1') { if (val.toLowerCase() === "true" || val === "1") {
return true; return true;
} }
return false; return false;
@ -194,7 +200,7 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* @returns {Deferred} * @returns {Deferred}
* A deferred object to be completed when it finished rendering. * A deferred object to be completed when it finished rendering.
*/ */
_render: function () { _render: function() {
if (!this.view) { if (!this.view) {
return this._super(); return this._super();
} }
@ -210,11 +216,11 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
// Create a new matrix renderer // Create a new matrix renderer
this.renderer = new X2Many2dMatrixRenderer(this, this.value, { this.renderer = new X2Many2dMatrixRenderer(this, this.value, {
arch: arch, arch: arch,
editable: this.mode === 'edit' && arch.attrs.editable, editable: this.mode === "edit" && arch.attrs.editable,
viewType: "list", viewType: "list",
matrix_data: this.matrix_data, matrix_data: this.matrix_data,
}); });
this.$el.addClass('o_field_x2many o_field_x2many_2d_matrix'); this.$el.addClass("o_field_x2many o_field_x2many_2d_matrix");
return this.renderer.appendTo(this.$el); return this.renderer.appendTo(this.$el);
}, },
@ -223,7 +229,7 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* *
* @override * @override
*/ */
activate: function (options) { activate: function(options) {
// Won't work fine without https://github.com/odoo/odoo/pull/26490 // Won't work fine without https://github.com/odoo/odoo/pull/26490
// TODO Use _.propertyOf in underscore 1.9+ // TODO Use _.propertyOf in underscore 1.9+
try { try {
@ -241,12 +247,12 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* *
* @override * @override
*/ */
getFocusableElement: function () { getFocusableElement: function() {
return this.$(".o_input:" + (this._backwards ? "last" : "first")); return this.$(".o_input:" + (this._backwards ? "last" : "first"));
}, },
}); });
field_registry.add('x2many_2d_matrix', WidgetX2Many2dMatrix); field_registry.add("x2many_2d_matrix", WidgetX2Many2dMatrix);
return { return {
WidgetX2Many2dMatrix: WidgetX2Many2dMatrix, WidgetX2Many2dMatrix: WidgetX2Many2dMatrix,

View File

@ -21,10 +21,12 @@ $x2many_2d_matrix_max_height: 450px;
> tbody { > tbody {
> tr { > tr {
&:nth-of-type(2n+1) td.row-total, &:nth-of-type(2n+1) td:first-child { &:nth-of-type(2n + 1) td.row-total,
&:nth-of-type(2n + 1) td:first-child {
background-color: mix(#000, #fff, 1%); background-color: mix(#000, #fff, 1%);
} }
&:nth-of-type(2n) td.row-total, &:nth-of-type(2n) td:first-child { &:nth-of-type(2n) td.row-total,
&:nth-of-type(2n) td:first-child {
background-color: white; background-color: white;
} }

View File

@ -1,17 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- <!--
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
--> -->
<template
<template id="assets_backend" name="web_widget_x2many_2d_matrix assets" inherit_id="web.assets_backend"> id="assets_backend"
name="web_widget_x2many_2d_matrix assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<script type="text/javascript" src="/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_view.js" /> <script
<script type="text/javascript" src="/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_renderer.js" /> type="text/javascript"
<script type="text/javascript" src="/web_widget_x2many_2d_matrix/static/src/js/widget_x2many_2d_matrix.js" /> src="/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_view.js"
<script type="text/javascript" src="/web_widget_x2many_2d_matrix/static/src/js/abstract_view_matrix_limit_extend.js" /> />
<link rel="stylesheet" href="/web_widget_x2many_2d_matrix/static/src/scss/web_widget_x2many_2d_matrix.scss"/> <script
type="text/javascript"
src="/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_renderer.js"
/>
<script
type="text/javascript"
src="/web_widget_x2many_2d_matrix/static/src/js/widget_x2many_2d_matrix.js"
/>
<script
type="text/javascript"
src="/web_widget_x2many_2d_matrix/static/src/js/abstract_view_matrix_limit_extend.js"
/>
<link
rel="stylesheet"
href="/web_widget_x2many_2d_matrix/static/src/scss/web_widget_x2many_2d_matrix.scss"
/>
</xpath> </xpath>
</template> </template>
</odoo> </odoo>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_x2m_demo_form" model="ir.ui.view">
<field name="name">x2m.demo.form</field>
<field name="model">x2m.demo</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" />
<field name="line_ids">
<tree>
<field name="name" />
<field name="value" />
<field name="value_selection" />
<field name="value_many2one" />
<field name="user_id" />
</tree>
</field>
</group>
<footer>
<button
name="open_x2m_matrix"
type="object"
string="Try x2m 2d matrix"
class="oe_link"
icon="fa-edit"
/>
<button
name="open_x2m_matrix_selection"
type="object"
string="Try x2m 2d matrix (selection)"
class="oe_link"
icon="fa-edit"
/>
<button
name="open_x2m_matrix_many2one"
type="object"
string="Try x2m 2d matrix (many2one)"
class="oe_link"
icon="fa-edit"
/>
</footer>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="view_x2m_demo_tree">
<field name="name">Demo - Tree</field>
<field name="model">x2m.demo</field>
<field name="arch" type="xml">
<tree>
<field name="name" />
</tree>
</field>
</record>
<record id="action_x2m_demo" model="ir.actions.act_window">
<field name="name">Demo</field>
<field name="res_model">x2m.demo</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_x2m_demo_tree" />
</record>
<menuitem
id="base_matrix_widget_menu"
name="Demo x2m matrix widget"
action="action_x2m_demo"
/>
</odoo>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="x2many_2d_matrix_demo" model="ir.ui.view">
<field name="name">x2m.matrix.demo.wiz</field>
<field name="model">x2m.matrix.demo.wiz</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<field
name="line_ids"
widget="x2many_2d_matrix"
field_x_axis="demo_id"
field_y_axis="user_id"
field_value="value"
>
<tree>
<field name="demo_id" />
<field name="user_id" />
<field name="value" />
</tree>
</field>
</form>
</field>
</record>
<record id="x2many_2d_matrix_demo_selection" model="ir.ui.view">
<field name="name">x2m.matrix.demo.wiz</field>
<field name="model">x2m.matrix.demo.wiz</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<field
name="line_ids"
widget="x2many_2d_matrix"
field_x_axis="demo_id"
field_y_axis="user_id"
field_value="value_selection"
>
<tree>
<field name="demo_id" />
<field name="user_id" />
<field name="value_selection" />
</tree>
</field>
</form>
</field>
</record>
<record id="x2many_2d_matrix_demo_many2one" model="ir.ui.view">
<field name="name">x2m.matrix.demo.wiz</field>
<field name="model">x2m.matrix.demo.wiz</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<field
name="line_ids"
widget="x2many_2d_matrix"
field_x_axis="demo_id"
field_y_axis="user_id"
field_value="value_many2one"
>
<tree>
<field name="demo_id" />
<field name="user_id" />
<field
name="value_many2one"
domain="[('users', '=', user_id)]"
/>
</tree>
</field>
</form>
</field>
</record>
</odoo>