[IMP] web_widget_x2many_2d_matrix: black, isort

pull/2110/head
Adrià Gil Sorribes 2020-02-14 10:02:14 +01:00 committed by Lois Rilo
parent 355e491c2c
commit 80f63ca781
7 changed files with 288 additions and 264 deletions

View File

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

View File

@ -2,26 +2,25 @@
* Copyright 2018 Brainbean Apps
* 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";
var BasicRenderer = require('web.BasicRenderer');
var config = require('web.config');
var core = require('web.core');
var field_utils = require('web.field_utils');
var BasicRenderer = require("web.BasicRenderer");
var config = require("web.config");
var core = require("web.core");
var field_utils = require("web.field_utils");
var _t = core._t;
var FIELD_CLASSES = {
float: 'o_list_number',
integer: 'o_list_number',
monetary: 'o_list_number',
text: 'o_list_text',
float: "o_list_number",
integer: "o_list_number",
monetary: "o_list_number",
text: "o_list_text",
};
// X2Many2dMatrixRenderer is heavily inspired by Odoo's ListRenderer
// and is reusing portions of code from list_renderer.js
var X2Many2dMatrixRenderer = BasicRenderer.extend({
/**
* @override
*/
@ -55,32 +54,26 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
_renderView: function() {
var self = this;
this.$el
.removeClass('table-responsive')
.empty();
this.$el.removeClass("table-responsive").empty();
// Display a nice message if there's no data to display
if (!self.rows.length) {
var $alert = $('<div>', {'class': 'alert alert-info'});
$alert.text(_t('Sorry no matrix data to display.'));
var $alert = $("<div>", {class: "alert alert-info"});
$alert.text(_t("Sorry no matrix data to display."));
this.$el.append($alert);
return this._super();
}
var $table = $('<table>').addClass(
'o_list_view table table-condensed table-striped ' +
'o_x2many_2d_matrix '
var $table = $("<table>").addClass(
"o_list_view table table-condensed table-striped " +
"o_x2many_2d_matrix "
);
this.$el
.addClass('table-responsive')
.append($table);
this.$el.addClass("table-responsive").append($table);
this._computeColumnAggregates();
this._computeRowAggregates();
$table
.append(this._renderHeader())
.append(this._renderBody());
$table.append(this._renderHeader()).append(this._renderBody());
if (self.matrix_data.show_column_totals) {
$table.append(this._renderFooter());
}
@ -97,9 +90,9 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} The table body element just filled.
*/
_renderBody: function() {
var $body = $('<tbody>').append(this._renderRows());
_.each($body.find('input'), function (td, i) {
$(td).attr('tabindex', i);
var $body = $("<tbody>").append(this._renderRows());
_.each($body.find("input"), function(td, i) {
$(td).attr("tabindex", i);
});
return $body;
},
@ -112,15 +105,12 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} The thead element that was inserted into.
*/
_renderHeader: function() {
var $tr = $('<tr>').append('<th/>');
$tr = $tr.append(_.map(
this.columns,
this._renderHeaderCell.bind(this)
));
var $tr = $("<tr>").append("<th/>");
$tr = $tr.append(_.map(this.columns, this._renderHeaderCell.bind(this)));
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);
},
/**
@ -135,27 +125,28 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
_renderHeaderCell: function(node) {
var name = node.attrs.name;
var field = this.state.fields[name];
var $th = $('<th>');
var $th = $("<th>");
if (!field) {
return $th;
}
var description = null;
if (node.attrs.widget) {
description = this.state.fieldsInfo.list[name]
.Widget.prototype.description;
description = this.state.fieldsInfo.list[name].Widget.prototype
.description;
}
if (_.isNull(description)) {
description = node.attrs.string || field.string;
}
$th.text(description).data('name', name);
$th.text(description).data("name", name);
if (
field.type === 'float' || field.type === 'integer' ||
field.type === 'monetary'
field.type === "float" ||
field.type === "integer" ||
field.type === "monetary"
) {
$th.addClass('text-right');
$th.addClass("text-right");
} else {
$th.addClass('text-center');
$th.addClass("text-center");
}
if (config.debug) {
@ -178,10 +169,13 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {String} a string with the generated html.
*/
_renderRows: function() {
return _.map(this.rows, function (row) {
return _.map(
this.rows,
function(row) {
row.attrs.name = this.matrix_data.field_value;
return this._renderRow(row);
}.bind(this));
}.bind(this)
);
},
/**
@ -195,15 +189,18 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} the <tr> element that has been rendered.
*/
_renderRow: function(row) {
var $tr = $('<tr/>', {class: 'o_data_row'}),
var $tr = $("<tr/>", {class: "o_data_row"}),
_data = _.without(row.data, undefined);
$tr = $tr.append(this._renderLabelCell(_data[0]));
var $cells = _.map(this.columns, function (column, index) {
var $cells = _.map(
this.columns,
function(column, index) {
var record = row.data[index];
// Make the widget use our field value for each cell
column.attrs.name = this.matrix_data.field_value;
return this._renderBodyCell(record, column, index, {mode:''});
}.bind(this));
return this._renderBodyCell(record, column, index, {mode: ""});
}.bind(this)
);
$tr = $tr.append($cells);
if (row.aggregate) {
$tr.append(this._renderAggregateRowCell(row));
@ -219,9 +216,9 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} the cell that was rendered.
*/
_renderLabelCell: function(record) {
var $td = $('<td>');
var $td = $("<td>");
var value = record.data[this.matrix_data.field_y_axis];
if (value.type === 'record') {
if (value.type === "record") {
// We have a related record
value = value.data.display_name;
}
@ -238,7 +235,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} The rendered cell.
*/
_renderAggregateRowCell: function(row) {
var $cell = $('<td/>', {class: 'row-total'});
var $cell = $("<td/>", {class: "row-total"});
this.applyAggregateValue($cell, row);
return $cell;
},
@ -256,24 +253,22 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} the rendered cell.
*/
_renderBodyCell: function(record, node, colIndex, options) {
var tdClassName = 'o_data_cell';
if (node.tag === 'field') {
var typeClass = FIELD_CLASSES[
this.state.fields[node.attrs.name].type
];
var tdClassName = "o_data_cell";
if (node.tag === "field") {
var typeClass = FIELD_CLASSES[this.state.fields[node.attrs.name].type];
if (typeClass) {
tdClassName += ' ' + typeClass;
tdClassName += " " + typeClass;
}
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
// the user might want to attach to each single cell.
var $td = $('<td>', {
'class': tdClassName,
var $td = $("<td>", {
class: tdClassName,
});
if (_.isUndefined(record)) {
@ -281,8 +276,8 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
return $td;
}
$td.attr({
'data-form-id': record.id,
'data-id': record.data.id,
"data-form-id": record.id,
"data-id": record.data.id,
});
// 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,
record,
$td,
_.pick(options, 'mode')
_.pick(options, "mode")
);
// If the invisible modifiers is true, the <td> element is
// 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
options.mode = this.getParent().mode;
if (node.tag === 'widget') {
if (node.tag === "widget") {
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);
return $td.append($el);
},
@ -320,12 +315,14 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
_renderFooter: function() {
var $cells = this._renderAggregateColCells();
if ($cells) {
var $tr = $('<tr>').append('<td/>').append($cells);
var $tr = $("<tr>")
.append("<td/>")
.append($cells);
var $total_cell = this._renderTotalCell();
if ($total_cell) {
$tr.append($total_cell);
}
return $('<tfoot>').append($tr);
return $("<tfoot>").append($tr);
}
},
@ -336,12 +333,14 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
* @returns {jQueryElement} The td element with the total in it.
*/
_renderTotalCell: function() {
if (!this.matrix_data.show_column_totals ||
!this.matrix_data.show_row_totals) {
if (
!this.matrix_data.show_column_totals ||
!this.matrix_data.show_row_totals
) {
return;
}
var $cell = $('<td>', {class: 'col-total'});
var $cell = $("<td>", {class: "col-total"});
this.applyAggregateValue($cell, this.total);
return $cell;
},
@ -356,12 +355,12 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
var self = this;
return _.map(this.columns, function(column) {
var $cell = $('<td>');
var $cell = $("<td>");
if (config.debug) {
$cell.addClass(column.attrs.name);
}
if (column.aggregate) {
self.applyAggregateValue($cell, column)
self.applyAggregateValue($cell, column);
}
return $cell;
});
@ -383,7 +382,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
return;
}
var type = field.type;
if (!~['integer', 'float', 'monetary'].indexOf(type)) {
if (!~["integer", "float", "monetary"].indexOf(type)) {
return;
}
this.total = {
@ -391,13 +390,15 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
name: fname,
},
aggregate: {
help: _t('Sum Total'),
help: _t("Sum Total"),
value: 0,
},
};
_.each(this.columns, function (column, index) {
_.each(
this.columns,
function(column, index) {
column.aggregate = {
help: _t('Sum'),
help: _t("Sum"),
value: 0,
};
_.each(this.rows, function(row) {
@ -409,7 +410,8 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
}
});
this.total.aggregate.value += column.aggregate.value;
}.bind(this));
}.bind(this)
);
},
/**
@ -467,12 +469,12 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
return;
}
var type = field.type;
if (!~['integer', 'float', 'monetary'].indexOf(type)) {
if (!~["integer", "float", "monetary"].indexOf(type)) {
return;
}
_.each(this.rows, function(row) {
row.aggregate = {
help: _t('Sum'),
help: _t("Sum"),
value: 0,
};
_.each(row.data, function(col) {
@ -502,11 +504,13 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
var value = axis.aggregate.value;
var help = axis.aggregate.help;
var fieldInfo = this.state.fieldsInfo.list[axis.attrs.name];
var formatFunc = field_utils.format[
fieldInfo.widget ? fieldInfo.widget : field.type
];
var formatFunc =
field_utils.format[fieldInfo.widget ? fieldInfo.widget : field.type];
var formattedValue = formatFunc(value, field, {escape: true});
$cell.addClass('o_list_number').attr('title', help).html(formattedValue);
$cell
.addClass("o_list_number")
.attr("title", help)
.html(formattedValue);
},
/**
@ -570,7 +574,7 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
*/
_refreshColTotals: function() {
this._computeColumnAggregates();
this.$('tfoot').replaceWith(this._renderFooter());
this.$("tfoot").replaceWith(this._renderFooter());
},
/**
@ -579,10 +583,11 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
_refreshRowTotals: function() {
var self = this;
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) {
if (row.aggregate) {
$($rows[i]).find('.row-total')
$($rows[i])
.find(".row-total")
.replaceWith(self._renderAggregateRowCell(row));
}
});
@ -596,7 +601,6 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer', function (requ
getEditableRecordID: function() {
return null;
},
});
return X2Many2dMatrixRenderer;

View File

@ -1,10 +1,10 @@
/* Copyright 2019 Alexandre Díaz
* 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";
var BasicView = require('web.BasicView');
var BasicView = require("web.BasicView");
BasicView.include({
_processField: function(viewType, field, attrs) {
@ -13,11 +13,10 @@ odoo.define('web_widget_x2many_2d_matrix.X2Many2dMatrixView', function (require)
// See https://github.com/OCA/web/pull/1404#pullrequestreview-305813206 .
// In the long term we should a way to handle kanban mode
// better (eg: a specific renderer).
if (attrs.widget === 'x2many_2d_matrix') {
attrs.mode = 'tree';
if (attrs.widget === "x2many_2d_matrix") {
attrs.mode = "tree";
}
return this._super(viewType, field, attrs);
},
});
});

View File

@ -3,17 +3,15 @@
* Copyright 2018 Simone Orsi <simone.orsi@camptocamp.com>
* 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";
var field_registry = require('web.field_registry');
var relational_fields = require('web.relational_fields');
var X2Many2dMatrixRenderer = require(
'web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer'
);
var field_registry = require("web.field_registry");
var relational_fields = require("web.relational_fields");
var X2Many2dMatrixRenderer = require("web_widget_x2many_2d_matrix.X2Many2dMatrixRenderer");
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.
@ -39,40 +37,39 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
this.y_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_label_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.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_label_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.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;
// TODO: is this really needed? Holger?
for (var property in node) {
if (property.startsWith("field_att_")) {
this.fields_att[property.substring(10)] =
node[property];
this.fields_att[property.substring(10)] = node[property];
}
}
var field_defs = this.recordData[this.name].fields;
// TODO: raise when any of the fields above don't exist with a
// helpful error message
if (!field_defs[this.field_value]) {
throw new Error(_.str.sprintf(
'You need to include %s in your view definition',
throw new Error(
_.str.sprintf(
"You need to include %s in your view definition",
this.field_value
));
)
);
}
this.show_row_totals = this.parse_boolean(
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(
node.show_column_totals ||
this.is_aggregatable(field_defs[this.field_value]) ? '1' : ''
this.is_aggregatable(field_defs[this.field_value])
? "1"
: ""
);
},
@ -88,14 +85,16 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
this.by_y_axis = {};
this.x_axis = [];
this.y_axis = [];
_.each(records, function (record) {
_.each(
records,
function(record) {
var x = record.data[this.field_x_axis],
y = record.data[this.field_y_axis];
if (x.type === 'record') {
if (x.type === "record") {
// We have a related record
x = x.data.display_name;
}
if (y.type === 'record') {
if (y.type === "record") {
// We have a related record
y = y.data.display_name;
}
@ -107,24 +106,31 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
if (this.x_axis.indexOf(x) === -1) {
this.x_axis.push(x);
}
}.bind(this));
}.bind(this)
);
// Init columns
this.columns = [];
_.each(this.x_axis, function (x) {
_.each(
this.x_axis,
function(x) {
this.columns.push(this._make_column(x));
}.bind(this));
}.bind(this)
);
this.rows = [];
_.each(this.y_axis, function (y) {
_.each(
this.y_axis,
function(y) {
this.rows.push(this._make_row(y));
}.bind(this));
}.bind(this)
);
this.matrix_data = {
'field_value': this.field_value,
'field_x_axis': this.field_x_axis,
'field_y_axis': this.field_y_axis,
'columns': this.columns,
'rows': this.rows,
'show_row_totals': this.show_row_totals,
'show_column_totals': this.show_column_totals,
field_value: this.field_value,
field_x_axis: this.field_x_axis,
field_y_axis: this.field_y_axis,
columns: this.columns,
rows: this.rows,
show_row_totals: this.show_row_totals,
show_column_totals: this.show_column_totals,
};
},
@ -137,10 +143,10 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
_make_column: function(x) {
return {
// Simulate node parsed on xml arch
'tag': 'field',
'attrs': {
'name': this.field_x_axis,
'string': x,
tag: "field",
attrs: {
name: this.field_x_axis,
string: x,
},
};
},
@ -155,12 +161,12 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
var self = this;
// Use object so that we can attach more data if needed
var row = {
'tag': 'field',
'attrs': {
'name': this.field_y_axis,
'string': y,
tag: "field",
attrs: {
name: this.field_y_axis,
string: y,
},
'data': [],
data: [],
};
_.each(self.x_axis, function(x) {
row.data.push(self.by_y_axis[y][x]);
@ -182,7 +188,7 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
* @returns {Boolean} The parsed boolean.
*/
parse_boolean: function(val) {
if (val.toLowerCase() === 'true' || val === '1') {
if (val.toLowerCase() === "true" || val === "1") {
return true;
}
return false;
@ -210,11 +216,11 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
// Create a new matrix renderer
this.renderer = new X2Many2dMatrixRenderer(this, this.value, {
arch: arch,
editable: this.mode === 'edit' && arch.attrs.editable,
editable: this.mode === "edit" && arch.attrs.editable,
viewType: "list",
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);
},
@ -246,7 +252,7 @@ odoo.define('web_widget_x2many_2d_matrix.widget', function (require) {
},
});
field_registry.add('x2many_2d_matrix', WidgetX2Many2dMatrix);
field_registry.add("x2many_2d_matrix", WidgetX2Many2dMatrix);
return {
WidgetX2Many2dMatrix: WidgetX2Many2dMatrix,

View File

@ -21,10 +21,12 @@ $x2many_2d_matrix_max_height: 450px;
> tbody {
> 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%);
}
&: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;
}

View File

@ -3,15 +3,32 @@
<!--
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->
<template id="assets_backend" name="web_widget_x2many_2d_matrix assets" inherit_id="web.assets_backend">
<template
id="assets_backend"
name="web_widget_x2many_2d_matrix assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script type="text/javascript" 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/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"/>
<script
type="text/javascript"
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/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>
</template>
</odoo>