").append($tr);
- }
- },
-
- /**
- * Renders the total cell (of all rows / columns)
- *
- * @private
- * @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
- ) {
- return;
- }
-
- var $cell = $("", {class: "col-total"});
- this.applyAggregateValue($cell, this.total);
- return $cell;
- },
-
- /**
- * Render the Aggregate cells for the column.
- *
- * @private
- * @returns {List} the rendered cells
- */
- _renderAggregateColCells: function () {
- var self = this;
-
- return _.map(this.columns, function (column) {
- var $cell = $(" | ");
- if (config.isDebug()) {
- $cell.addClass(column.attrs.name);
- }
- if (column.aggregate) {
- self.applyAggregateValue($cell, column);
- }
- return $cell;
- });
- },
-
- /**
- * Compute the column aggregates.
- * This function is called everytime the value is changed.
- *
- * @private
- */
- _computeColumnAggregates: function () {
- if (!this.matrix_data.show_column_totals) {
- return;
- }
- var fname = this.matrix_data.field_value,
- field = this.state.fields[fname];
- if (!field) {
- return;
- }
- var type = field.type;
- if (!~["integer", "float", "monetary"].indexOf(type)) {
- return;
- }
- this.total = {
- attrs: {
- name: fname,
- },
- aggregate: {
- help: _t("Sum Total"),
- value: 0,
- },
- };
- _.each(
- this.columns,
- function (column, index) {
- column.aggregate = {
- help: _t("Sum"),
- value: 0,
- };
- _.each(this.rows, function (row) {
- // TODO Use only one _.propertyOf in underscore 1.9.0+
- try {
- column.aggregate.value += row.data[index].data[fname];
- } catch (error) {
- // Nothing to do
- }
- });
- this.total.aggregate.value += column.aggregate.value;
- }.bind(this)
- );
- },
-
- _getRecord: function (recordId) {
- var record = null;
- utils.traverse_records(this.state, function (r) {
- if (r.id === recordId) {
- record = r;
- }
- });
- return record;
- },
-
- /**
- * @override
- */
- updateState: function (state, params) {
- if (params.matrix_data) {
- this._saveMatrixData(params.matrix_data);
- }
- return this._super.apply(this, arguments);
- },
-
- /**
- * Traverse the fields matrix with the keyboard
- *
- * @override
- * @private
- * @param {OdooEvent} event "navigation_move" event
- */
- _onNavigationMove: function (event) {
- var widgets = this.__parentedChildren,
- index = widgets.indexOf(event.target),
- first = index === 0,
- last = index === widgets.length - 1,
- move = 0;
- // Guess if we have to move the focus
- if (event.data.direction === "next" && !last) {
- move = 1;
- } else if (event.data.direction === "previous" && !first) {
- move = -1;
- }
- // Move focus
- if (move) {
- var target = widgets[index + move];
- index = this.allFieldWidgets[target.record.id].indexOf(target);
- this._activateFieldWidget(target.record, index, {inc: 0});
- event.stopPropagation();
- }
- },
-
- /**
- * Compute the row aggregates.
- *
- * This function is called everytime the value is changed.
- *
- * @private
- */
- _computeRowAggregates: function () {
- if (!this.matrix_data.show_row_totals) {
- return;
- }
- var fname = this.matrix_data.field_value,
- field = this.state.fields[fname];
- if (!field) {
- return;
- }
- var type = field.type;
- if (!~["integer", "float", "monetary"].indexOf(type)) {
- return;
- }
- _.each(this.rows, function (row) {
- row.aggregate = {
- help: _t("Sum"),
- value: 0,
- };
- _.each(row.data, function (col) {
- // TODO Use _.property in underscore 1.9+
- try {
- row.aggregate.value += col.data[fname];
- } catch (error) {
- // Nothing to do
- }
- });
- });
- },
-
- /**
- * Takes the given Value, formats it and adds it to the given cell.
- *
- * @private
- *
- * @param {jQueryElement} $cell
- * The Cell where the aggregate should be added.
- *
- * @param {Object} axis
- * The object which contains the information about the aggregate value axis
- */
- applyAggregateValue: function ($cell, axis) {
- var field = this.state.fields[axis.attrs.name];
- 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 formattedValue = formatFunc(value, field, {escape: true});
- $cell.addClass("o_list_number").attr("title", help).html(formattedValue);
- },
-
- /**
- * Check if the change was successful and then update the grid.
- * This function is required on relational fields.
- *
- * @param {Object} state
- * Contains the current state of the field & all the data
- *
- * @param {String} id
- * the id of the updated object.
- *
- * @param {Array} fields
- * The fields we have in the view.
- *
- * @param {Object} ev
- * The event object.
- *
- * @returns {Deferred}
- * The deferred object thats gonna be resolved when the change is made.
- */
- confirmUpdate: function (state, id, fields, ev) {
- var self = this;
- this.state = state;
- return this.confirmChange(state, id, fields, ev).then(function () {
- self._refresh(id);
- });
- },
-
- /**
- * Refresh our grid.
- *
- * @private
- * @param {String} id Datapoint ID
- */
- _refresh: function (id) {
- this._updateRow(id);
- this._refreshColTotals();
- this._refreshRowTotals();
- },
-
- /**
- *Update row data in our internal rows.
- *
- * @param {String} id: The id of the row that needs to be updated.
- */
- _updateRow: function (id) {
- var record = _.findWhere(this.state.data, {id: id}),
- _id = _.property("id");
- _.each(this.rows, function (row) {
- _.each(row.data, function (col, i) {
- if (_id(col) === id) {
- row.data[i] = record;
- }
- });
- });
- },
-
- /**
- * Update the row total.
- */
- _refreshColTotals: function () {
- this._computeColumnAggregates();
- this.$("tfoot").replaceWith(this._renderFooter());
- },
-
- /**
- * Update the column total.
- */
- _refreshRowTotals: function () {
- var self = this;
- this._computeRowAggregates();
- var $rows = self.$el.find("tr.o_data_row");
- _.each(self.rows, function (row, i) {
- if (row.aggregate) {
- $($rows[i])
- .find(".row-total")
- .replaceWith(self._renderAggregateRowCell(row));
- }
- });
- },
-
- /**
- * X2many fields expect this
- *
- * @returns {null}
- */
- getEditableRecordID: function () {
- return null;
- },
- });
-
- return X2Many2dMatrixRenderer;
-});
diff --git a/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_view.js b/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_view.js
deleted file mode 100644
index a223cf804ade..000000000000
--- a/web_widget_x2many_2d_matrix/static/src/js/2d_matrix_view.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* 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) {
- "use strict";
-
- var BasicView = require("web.BasicView");
-
- BasicView.include({
- _processField: function (viewType, field, attrs) {
- // 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 .
- // 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";
- }
- return this._super(viewType, field, attrs);
- },
- });
-});
diff --git a/web_widget_x2many_2d_matrix/static/src/js/abstract_view_matrix_limit_extend.js b/web_widget_x2many_2d_matrix/static/src/js/abstract_view_matrix_limit_extend.js
deleted file mode 100644
index 3303f86cb4d6..000000000000
--- a/web_widget_x2many_2d_matrix/static/src/js/abstract_view_matrix_limit_extend.js
+++ /dev/null
@@ -1,16 +0,0 @@
-odoo.define("web_widget_x2many_2d_matrix.matrix_limit_extend", function (require) {
- "use strict";
-
- var FormView = require("web.FormView");
-
- FormView.include({
- // We extend this method so that the view is not limited to
- // just 40 cells when the 'x2many_2d_matrix' widget is used.
- _setSubViewLimit: function (attrs) {
- this._super(attrs);
- if (attrs.widget === "x2many_2d_matrix") {
- attrs.limit = Infinity;
- }
- },
- });
-});
diff --git a/web_widget_x2many_2d_matrix/static/src/js/widget_x2many_2d_matrix.js b/web_widget_x2many_2d_matrix/static/src/js/widget_x2many_2d_matrix.js
deleted file mode 100644
index 0f374060333a..000000000000
--- a/web_widget_x2many_2d_matrix/static/src/js/widget_x2many_2d_matrix.js
+++ /dev/null
@@ -1,262 +0,0 @@
-/* Copyright 2015 Holger Brunn
- * Copyright 2016 Pedro M. Baeza
- * Copyright 2018 Simone Orsi
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-
-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 WidgetX2Many2dMatrix = relational_fields.FieldOne2Many.extend({
- widget_class: "o_form_field_x2many_2d_matrix",
-
- /**
- * Initialize the widget & parameters.
- *
- * @param {Object} parent contains the form view.
- * @param {String} name the name of the field.
- * @param {Object} record information about the database records.
- * @param {Object} options view options.
- */
- init: function (parent, name, record, options) {
- this._super(parent, name, record, options);
- this.init_params();
- },
-
- /**
- * Initialize the widget specific parameters.
- * Sets the axis and the values.
- */
- init_params: function () {
- var node = this.attrs;
- this.by_y_axis = {};
- this.x_axis = [];
- 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_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];
- }
- }
- 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",
- this.field_value
- )
- );
- }
- this.show_row_totals = this.parse_boolean(
- node.show_row_totals ||
- 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"
- : ""
- );
- },
-
- /**
- * Initializes the Value matrix.
- *
- * Puts the values in the grid.
- * If we have related items we use the display name.
- */
- init_matrix: function () {
- var records = this.recordData[this.name].data;
- // Wipe the content if something still exists
- this.by_y_axis = {};
- this.x_axis = [];
- this.y_axis = [];
- _.each(
- records,
- function (record) {
- var x = record.data[this.field_label_x_axis],
- y = record.data[this.field_label_y_axis];
- if (x.type === "record") {
- // We have a related record
- x = x.data.display_name;
- }
- if (y.type === "record") {
- // We have a related record
- y = y.data.display_name;
- }
- this.by_y_axis[y] = this.by_y_axis[y] || {};
- this.by_y_axis[y][x] = record;
- if (this.y_axis.indexOf(y) === -1) {
- this.y_axis.push(y);
- }
- if (this.x_axis.indexOf(x) === -1) {
- this.x_axis.push(x);
- }
- }.bind(this)
- );
- // Init columns
- this.columns = [];
- _.each(
- this.x_axis,
- function (x) {
- this.columns.push(this._make_column(x));
- }.bind(this)
- );
- this.rows = [];
- _.each(
- this.y_axis,
- function (y) {
- this.rows.push(this._make_row(y));
- }.bind(this)
- );
- this.matrix_data = {
- field_value: this.field_value,
- field_x_axis: this.field_x_axis,
- field_y_axis: this.field_y_axis,
- field_label_x_axis: this.field_label_x_axis,
- field_label_y_axis: this.field_label_y_axis,
- columns: this.columns,
- rows: this.rows,
- show_row_totals: this.show_row_totals,
- show_column_totals: this.show_column_totals,
- };
- },
-
- /**
- * Create scaffold for a column.
- *
- * @param {String} x The string used as a column title
- * @returns {Object}
- */
- _make_column: function (x) {
- return {
- // Simulate node parsed on xml arch
- tag: "field",
- attrs: {
- name: this.field_x_axis,
- string: x,
- },
- };
- },
-
- /**
- * Create scaffold for a row.
- *
- * @param {String} y The string used as a row title
- * @returns {Object}
- */
- _make_row: function (y) {
- 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,
- },
- data: [],
- };
- _.each(self.x_axis, function (x) {
- row.data.push(self.by_y_axis[y][x]);
- });
- return row;
- },
-
- /**
- * Determine if a field represented by field_def can be aggregated
- */
- is_aggregatable: function (field_def) {
- return field_def.type in {float: 1, monetary: 1, integer: 1};
- },
-
- /**
- * Parse a String containing a bool and convert it to a JS bool.
- *
- * @param {String} val: the string to be parsed.
- * @returns {Boolean} The parsed boolean.
- */
- parse_boolean: function (val) {
- if (val.toLowerCase() === "true" || val === "1") {
- return true;
- }
- return false;
- },
-
- /**
- * Create the matrix renderer and add its output to our element
- *
- * @returns {Deferred}
- * A deferred object to be completed when it finished rendering.
- */
- _render: function () {
- if (!this.view) {
- return this._super();
- }
- // Ensure widget is re initiated when rendering
- this.init_matrix();
- var arch = this.view.arch;
- // Update existing renderer
- if (!_.isUndefined(this.renderer)) {
- return this.renderer.updateState(this.value, {
- matrix_data: this.matrix_data,
- });
- }
- // Create a new matrix renderer
- this.renderer = new X2Many2dMatrixRenderer(this, this.value, {
- arch: arch,
- 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");
- return this.renderer.appendTo(this.$el);
- },
-
- /**
- * Activate the widget.
- *
- * @override
- */
- activate: function (options) {
- // Won't work fine without https://github.com/odoo/odoo/pull/26490
- // TODO Use _.propertyOf in underscore 1.9+
- try {
- this._backwards = options.event.data.direction === "previous";
- } catch (error) {
- this._backwards = false;
- }
- var result = this._super.apply(this, arguments);
- delete this._backwards;
- return result;
- },
-
- /**
- * Get first element to focus.
- *
- * @override
- */
- getFocusableElement: function () {
- return this.$(".o_input:" + (this._backwards ? "last" : "first"));
- },
- });
-
- field_registry.add("x2many_2d_matrix", WidgetX2Many2dMatrix);
-
- return {
- WidgetX2Many2dMatrix: WidgetX2Many2dMatrix,
- };
-});
|