Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] web_widget_x2many_2d_matrix: support One2many field as value field #13

Open
wants to merge 2 commits into
base: 16-mig-matrix-widget
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions web_widget_x2many_2d_matrix/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "2D matrix for x2many fields",
"version": "15.0.1.0.2",
"version": "16.0.1.0.0",
"maintainers": ["ChrisOForgeFlow"],
"development_status": "Production/Stable",
"author": (
"Therp BV, "
"Tecnativa, "
"Camptocamp, "
"CorporateHub, "
"Onestein, "
"Odoo Community Association (OCA)"
),
"website": "https://github.com/OCA/web",
Expand All @@ -24,11 +25,16 @@
"installable": True,
"assets": {
"web.assets_backend": [
"web_widget_x2many_2d_matrix/static/src/scss/web_widget_x2many_2d_matrix.scss",
"web_widget_x2many_2d_matrix/static/src/js/2d_matrix_renderer.js",
"web_widget_x2many_2d_matrix/static/src/js/2d_matrix_view.js",
"web_widget_x2many_2d_matrix/static/src/js/abstract_view_matrix_limit_extend.js",
"web_widget_x2many_2d_matrix/static/src/js/widget_x2many_2d_matrix.js",
"web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_renderer/"
"x2many_2d_matrix_renderer.esm.js",
"web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_renderer/"
"x2many_2d_matrix_renderer.xml",
"web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/"
"x2many_2d_matrix_field.esm.js",
"web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/"
"x2many_2d_matrix_field.xml",
"web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/"
"x2many_2d_matrix_field.scss",
],
},
}
4 changes: 0 additions & 4 deletions web_widget_x2many_2d_matrix/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ field_x_axis
The field that indicates the x value of a point
field_y_axis
The field that indicates the y value of a point
field_label_x_axis
Use another field to display in the table header
field_label_y_axis
Use another field to display in the table header
field_value
Show this field as value
show_row_totals
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/** @odoo-module **/

import {Component} from "@odoo/owl";
import {standardFieldProps} from "@web/views/fields/standard_field_props";
import {registry} from "@web/core/registry";
import {archParseBoolean} from "@web/views/utils";
import {X2Many2DMatrixRenderer} from "@web_widget_x2many_2d_matrix/components/x2many_2d_matrix_renderer/x2many_2d_matrix_renderer.esm";
import { _lt } from "@web/core/l10n/translation";

export class X2Many2DMatrixField extends Component {

setup() {
this.activeField = this.props.record.activeFields[this.props.name];
}

_getDefaultRecordValues() {
return {};
}

async commitChange(x, y, value) {
const fields = this.props.matrixFields;

const values = this._getDefaultRecordValues();
values[fields.x] = x;
values[fields.y] = y;

const matchingRecords = this.props.value.records.filter(
(record) => record.data[fields.x] === x && record.data[fields.y] === y
);
if (matchingRecords.length === 1) {
values[fields.value] = value;
await matchingRecords[0].update(values);
} else {
let total = 0;
if (matchingRecords.length) {
total = matchingRecords
.map((r) => r.data[fields.value])
.reduce((aggr, v) => aggr + v);
}
const diff = value - total;
values[fields.value] = diff;
const record = await this.list.addNew({
mode: "edit",
});
await record.update(values);
}
this.props.setDirty(false);
}
}

X2Many2DMatrixField.components = {X2Many2DMatrixRenderer};
X2Many2DMatrixField.displayName = _lt("X2Many2DMatrixField Table");
X2Many2DMatrixField.template = "web_widget_x2many_2d_matrix.X2Many2DMatrixField";
X2Many2DMatrixField.props = {
...standardFieldProps,
matrixFields: Object,
isXClickable: Boolean,
isYClickable: Boolean,
showRowTotals: Boolean,
showColumnTotals: Boolean,
};
X2Many2DMatrixField.extractProps = ({attrs}) => {
return {
matrixFields: {
value: attrs.field_value,
x: attrs.field_x_axis,
y: attrs.field_y_axis,
},
isXClickable: archParseBoolean(attrs.x_axis_clickable),
isYClickable: archParseBoolean(attrs.y_axis_clickable),
showRowTotals:
"show_row_totals" in attrs ? archParseBoolean(attrs.show_row_totals) : false,
showColumnTotals:
"show_column_totals" in attrs
? archParseBoolean(attrs.show_column_totals)
: false,
};
};

registry.category("fields").add("x2many_2d_matrix", X2Many2DMatrixField);
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ $x2many_2d_matrix_max_height: 450px;
overflow-y: auto;
}

.o_x2many_2d_matrix.o_list_view {
.table {
> thead > tr > th {
white-space: pre-line;
position: sticky;
Expand Down Expand Up @@ -42,6 +42,7 @@ $x2many_2d_matrix_max_height: 450px;
box-shadow: -1px 5px 10px $gray-300;
}
&.row-total {
padding: 0.75rem;
font-weight: bold;
position: sticky;
right: 0;
Expand All @@ -54,7 +55,7 @@ $x2many_2d_matrix_max_height: 450px;
}
}

> tfoot > tr > td {
> tfoot > tr > th {
padding: 0.75rem;
text-align: left;
background-color: $o-list-footer-bg-color;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-name="web_widget_x2many_2d_matrix.X2Many2DMatrixField" owl="1">
<div class="table-responsive">
<X2Many2DMatrixRenderer
matrixRows="props.value"
matrixFields="props.matrixFields"
showRowTotals="props.showRowTotals"
showColumnTotals="props.showColumnTotals"
setDirty="(isDirty) => this.setDirty(isDirty)"
onUpdate="(x, y, value) => this.commitChange(x, y, value)"
readonly="props.readonly"
/>
</div>
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/** @odoo-module **/

import {Component, onWillUpdateProps} from "@odoo/owl";
import {registry} from "@web/core/registry";

export class X2Many2DMatrixRenderer extends Component {

setup() {
this.ValueFieldComponent = this._getValueFieldComponent();
this.AggregateFieldComponent = this._getAggregateFieldComponent();
this.columns = this._getColumns();
this.rows = this._getRows();
this.matrix = this._getMatrix();

onWillUpdateProps((newProps) => {
this.columns = this._getColumns(newProps.matrixRows.records);
this.rows = this._getRows(newProps.matrixRows.records);
this.matrix = this._getMatrix(newProps.matrixRows.records);
});
}

_getColumns(records = this.matrixRows.records) {
const columns = [];
records.forEach((record) => {
const column = {
value: record.data[this.matrixFields.x],
text: record.data[this.matrixFields.x],
};
if (record.fields[this.matrixFields.x].type === "many2one") {
column.value = column.value[0];
column.text = column.value[1];
}
if (columns.findIndex((c) => c.value === column.value) !== -1) return;
columns.push(column);
});
return columns;
}

_getRows(records = this.matrixRows.records) {
const rows = [];
records.forEach((record) => {
const row = {
value: record.data[this.matrixFields.y],
text: record.data[this.matrixFields.y],
};
if (record.fields[this.matrixFields.y].type === "many2one") {
row.value = row.value[0];
row.text = row.value[1];
}
if (rows.findIndex((r) => r.value === row.value) !== -1) return;
rows.push(row);
});
return rows;
}

_getPointOfRecord(record) {
let xValue = record.data[this.matrixFields.x];
if (record.fields[this.matrixFields.x].type === "many2one") {
xValue = xValue[0];
}
let yValue = record.data[this.matrixFields.y];
if (record.fields[this.matrixFields.y].type === "many2one") {
yValue = yValue[0];
}

const x = this.columns.findIndex((c) => c.value === xValue);
const y = this.rows.findIndex((r) => r.value === yValue);
return {x, y};
}

_getMatrix(records = this.matrixRows.records) {
const matrix = this.rows.map(() =>
new Array(this.columns.length).fill(null).map(() => {
return {value: 0, records: []};
})
);
records.forEach((record) => {
const value = record.data[this.matrixFields.value];
const {x, y} = this._getPointOfRecord(record);
matrix[y][x].value += value;
matrix[y][x].records.push(record);
});
return matrix;
}

get matrixRows() {
return this.props.matrixRows;
}

get matrixFields() {
return this.props.matrixFields;
}

_getValueFieldComponent() {
return this.matrixRows.activeFields[this.matrixFields.value].FieldComponent;
}

_getAggregateFieldComponent() {
return registry.category("fields").get("char")
}

_aggregateRow(row) {
const y = this.rows.findIndex((r) => r.value === row);
return this.matrix[y].map((r) => r.value).reduce((aggr, x) => aggr + x);
}

_aggregateColumn(column) {
const x = this.columns.findIndex((c) => c.value === column);
return this.matrix
.map((r) => r[x])
.map((r) => r.value)
.reduce((aggr, y) => aggr + y);
}

_aggregateAll() {
return this.matrix
.map((r) => r.map((x) => x.value).reduce((aggr, x) => aggr + x))
.reduce((aggr, y) => aggr + y);
}

update(x, y, value) {
this.matrix[y][x].value = value;
const xFieldValue = this.columns[x].value;
const yFieldValue = this.rows[y].value;

this.props.onUpdate(xFieldValue, yFieldValue, value);
}

getValueFieldProps(column, row) {
const x = this.columns.findIndex((c) => c.value === column);
const y = this.rows.findIndex((r) => r.value === row);
const props = this.matrixRows.activeFields[this.matrixFields.value].props;
const propsFromAttrs = this.matrixRows.activeFields[this.matrixFields.value].propsFromAttrs;
const record = this.matrix[y][x].records[0];
return {
...props,
...propsFromAttrs,
value: record.data[this.matrixFields.value],
update: (value) => this.update(x, y, value),
readonly: this.props.readonly,
record: record,
};
}
}

X2Many2DMatrixRenderer.template = "web_widget_x2many_2d_matrix.X2Many2DMatrixRenderer";
X2Many2DMatrixRenderer.props = {
matrixRows: Object,
matrixFields: Object,
setDirty: Function,
onUpdate: Function,
readonly: Boolean,
showRowTotals: Boolean,
showColumnTotals: Boolean,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-name="web_widget_x2many_2d_matrix.X2Many2DMatrixRenderer" owl="1">
<table
class="o_list_table table table-responsive table-sm table-hover position-relative mb-0 o_list_table_ungrouped table-striped"
t-if="rows.length > 0"
>
<thead>
<tr>
<th />
<th
t-foreach="columns"
t-as="column"
t-key="column.value"
class="text-center"
>
<t t-esc="column.text" />
</th>
<th t-if="props.showRowTotals" />
</tr>
</thead>
<tbody>
<tr t-foreach="rows" t-as="row" t-key="row.value">
<td>
<t t-esc="row.text" />
</td>
<td t-foreach="columns" t-as="column" t-key="column.value">
<t
t-component="ValueFieldComponent"
t-props="getValueFieldProps(column.value, row.value)"
/>
</td>
<td t-if="props.showRowTotals" class="row-total">
<t
t-component="AggregateFieldComponent"
readonly="true"
value="_aggregateRow(row.value)"
/>
</td>
</tr>
</tbody>
<tfoot>
<tr t-if="props.showColumnTotals">
<th />
<th t-foreach="columns" t-as="column" t-key="column.value">
<t
t-component="AggregateFieldComponent"
readonly="true"
value="_aggregateColumn(column.value)"
/>
</th>
<t t-if="props.showRowTotals">
<th class="col-total">
<t
t-component="AggregateFieldComponent"
readonly="true"
value="_aggregateAll()"
/>
</th>
</t>
</tr>
</tfoot>
</table>
<div t-else="" class="alert alert-info">
Nothing to display.
</div>
</t>
</templates>
Loading