Browse Source

[ADD] web_widget_x2many_2d_matrix

pull/96/head
Holger Brunn 10 years ago
parent
commit
75a234f312
  1. 56
      web_widget_x2many_2d_matrix/README.rst
  2. 20
      web_widget_x2many_2d_matrix/__init__.py
  3. 45
      web_widget_x2many_2d_matrix/__openerp__.py
  4. BIN
      web_widget_x2many_2d_matrix/static/description/icon.png
  5. 0
      web_widget_x2many_2d_matrix/static/src/css/web_widget_x2many_2d_matrix.css
  6. 305
      web_widget_x2many_2d_matrix/static/src/js/web_widget_x2many_2d_matrix.js
  7. 33
      web_widget_x2many_2d_matrix/static/src/xml/web_widget_x2many_2d_matrix.xml
  8. 11
      web_widget_x2many_2d_matrix/views/templates.xml

56
web_widget_x2many_2d_matrix/README.rst

@ -0,0 +1,56 @@
2D matrix for x2many fields
===========================
This module allows to show an x2many field with 3-tuples
($x_value, $y_value, $value) in a table
+-----------+-----------+-----------+
| | $x_value1 | $x_value2 |
+===========+===========+===========+
| $y_value1 | $value1/1 | $value2/1 |
+-----------+-----------+-----------+
| $y_value2 | $value1/2 | $value2/2 |
+-----------+-----------+-----------+
where `valuen/n` is editable.
Usage
=====
Use this widget by saying::
<field name="my_field" widget="x2many_2d_matrix" />
This assumes that my_field refers to a model with the fields `x`, `y` and
`value`. If your fields are named differently, pass the correct names as
attributes::
<field name="my_field" widget="x2many_2d_matrix"
field_x_axis="my_field1" field_y_axis="my_field2" field_value="my_field3" />
Known issues / Roadmap
======================
* ...
Credits
=======
Contributors
------------
* Holger Brunn <hbrunn@therp.nl>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

20
web_widget_x2many_2d_matrix/__init__.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV <http://therp.nl>.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

45
web_widget_x2many_2d_matrix/__openerp__.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV <http://therp.nl>.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
"name": "2D matrix for x2many fields",
"version": "1.0",
"author": "Therp BV",
"license": "AGPL-3",
"category": "Hidden/Dependency",
"summary": "Show list fields as a matrix",
"depends": [
'web',
],
"data": [
'views/templates.xml',
],
"qweb": [
'static/src/xml/web_widget_x2many_2d_matrix.xml',
],
"test": [
],
"auto_install": False,
"installable": True,
"application": False,
"external_dependencies": {
'python': [],
},
}

BIN
web_widget_x2many_2d_matrix/static/description/icon.png

After

Width: 80  |  Height: 80  |  Size: 1.1 KiB

0
web_widget_x2many_2d_matrix/static/src/css/web_widget_x2many_2d_matrix.css

305
web_widget_x2many_2d_matrix/static/src/js/web_widget_x2many_2d_matrix.js

@ -0,0 +1,305 @@
//-*- coding: utf-8 -*-
//############################################################################
//
// OpenERP, Open Source Management Solution
// This module copyright (C) 2015 Therp BV <http://therp.nl>.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//############################################################################
openerp.web_widget_x2many_2d_matrix = function(instance)
{
instance.web.form.widgets.add(
'x2many_2d_matrix',
'instance.web_widget_x2many_2d_matrix.FieldX2Many2dMatrix');
instance.web_widget_x2many_2d_matrix.FieldX2Many2dMatrix = instance.web.form.FieldOne2Many.extend({
template: 'FieldX2Many2dMatrix',
widget_class: 'oe_form_field_x2many_2d_matrix',
// those will be filled with rows from the dataset
by_x_axis: {},
by_y_axis: {},
field_x_axis: 'x',
field_label_x_axis: 'x',
field_y_axis: 'y',
field_label_y_axis: 'y',
field_value: 'value',
// information about our datatype
is_numeric: false,
show_row_totals: true,
show_column_totals: true,
// this will be filled with the model's fields_get
fields: {},
// read parameters
init: function(field_manager, node)
{
this.field_x_axis = node.attrs.field_x_axis || this.field_x_axis;
this.field_y_axis = node.attrs.field_y_axis || this.field_y_axis;
this.field_label_x_axis = node.attrs.field_label_x_axis || this.field_x_axis;
this.field_label_y_axis = node.attrs.field_label_y_axis || this.field_y_axis;
this.field_value = node.attrs.field_value || this.field_value;
this.show_row_totals = node.attrs.show_row_totals || this.show_row_totals;
this.show_column_totals = node.attrs.show_column_totals || this.show_column_totals;
return this._super.apply(this, arguments);
},
// return a field's value, id in case it's a one2many field
get_field_value: function(row, field, many2one_as_name)
{
if(this.fields[field].type == 'many2one' && _.isArray(row[field]))
{
if(many2one_as_name)
{
return row[field][1];
}
else
{
return row[field][0];
}
}
return row[field];
},
// setup our datastructure for simple access in the template
set_value: function()
{
var self = this,
result = this._super.apply(this, arguments);
self.by_x_axis = {};
self.by_y_axis = {};
return jQuery.when(result).then(function()
{
return self.dataset._model.call('fields_get').then(function(fields)
{
self.fields = fields;
self.is_numeric = fields[self.field_value].type == 'float';
self.show_row_totals &= self.is_numeric;
self.show_column_totals &= self.is_numeric;
}).then(function()
{
return self.dataset.read_ids(self.dataset.ids).then(function(rows)
{
var read_many2one = {},
many2one_fields = [
self.field_x_axis, self.field_y_axis,
self.field_label_x_axis, self.field_label_y_axis
];
// prepare to read many2one names if necessary (we can get (id, name) or just id as value)
_.each(many2one_fields, function(field)
{
if(self.fields[field].type == 'many2one')
{
read_many2one[field] = {};
}
});
// setup data structure
_.each(rows, function(row)
{
var x = self.get_field_value(row, self.field_x_axis),
y = self.get_field_value(row, self.field_y_axis);
self.by_x_axis[x] = self.by_x_axis[x] || {};
self.by_y_axis[y] = self.by_y_axis[y] || {};
self.by_x_axis[x][y] = row;
self.by_y_axis[y][x] = row;
_.each(read_many2one, function(rows, field)
{
if(!_.isArray(row[field]))
{
rows[row[field]] = rows[row[field]] || []
rows[row[field]].push(row);
}
});
});
// read many2one fields if necessary
var deferrends = [];
_.each(read_many2one, function(rows, field)
{
if(_.isEmpty(rows))
{
return;
}
var model = new instance.web.Model(self.fields[field].relation);
deferrends.push(model.call(
'name_get',
[_.map(_.keys(rows), function(key) {return parseInt(key)})])
.then(function(names)
{
_.each(names, function(name)
{
_.each(rows[name[0]], function(row)
{
row[field] = name;
});
});
}));
})
return jQuery.when.apply(jQuery, deferrends);
});
});
});
},
// get x axis values in the correct order
get_x_axis_values: function()
{
return _.keys(this.by_x_axis);
},
// get y axis values in the correct order
get_y_axis_values: function()
{
return _.keys(this.by_y_axis);
},
// get x axis labels
get_x_axis_labels: function()
{
var self = this;
return _.map(
this.get_x_axis_values(),
function(val)
{
return self.get_field_value(
_.first(_.values(self.by_x_axis[val])),
self.field_label_x_axis, true);
});
},
// get the label for a value on the y axis
get_y_axis_label: function(y)
{
return this.get_field_value(
_.first(_.values(this.by_y_axis[y])),
this.field_label_y_axis, true);
},
// return the class(es) the inputs should have
get_xy_value_class: function()
{
var classes = 'oe_form_field oe_form_required';
if(this.is_numeric)
{
classes += ' oe_form_field_float';
}
return classes;
},
// return row id of a coordinate
get_xy_id: function(x, y)
{
return this.by_x_axis[x][y]['id'];
},
// return the value of a coordinate
get_xy_value: function(x, y)
{
return this.get_field_value(
this.by_x_axis[x][y], this.field_value);
},
// validate a value
validate_xy_value: function(val)
{
return true;
},
// parse a value from user input
parse_xy_value: function(val)
{
if(this.is_numeric)
{
return parseFloat(val);
}
else
{
return val;
}
},
// format a value from the database for display
format_xy_value: function(val)
{
return instance.web.format_value(
val, {'type': this.fields[this.field_value].type});
},
// compute totals
compute_totals: function()
{
var self = this,
totals_x = {},
totals_y = {};
return self.dataset.read_ids(self.dataset.ids).then(function(rows)
{
_.each(rows, function(row)
{
var key_x = self.get_field_value(row, self.field_x_axis),
key_y = self.get_field_value(row, self.field_y_axis);
totals_x[key_x] = (totals_x[key_x] || 0) + self.get_field_value(row, self.field_value);
totals_y[key_y] = (totals_y[key_y] || 0) + self.get_field_value(row, self.field_value);
});
}).then(function()
{
_.each(totals_y, function(total, y)
{
self.$el.find(
_.str.sprintf('td.row_total[data-y="%s"]', y)).text(
self.format_xy_value(total));
});
_.each(totals_x, function(total, x)
{
self.$el.find(
_.str.sprintf('td.column_total[data-x="%s"]', x)).text(
self.format_xy_value(total));
});
});
},
start: function()
{
var self = this;
this.$el.find('input').on(
'change',
function()
{
var $this = jQuery(this),
val = $this.val()
if(self.validate_xy_value(val))
{
data = {}
data[self.field_value] = self.parse_xy_value(val);
self.dataset.write($this.data('id'), data);
$this.parent().removeClass('oe_form_invalid');
self.compute_totals();
}
else
{
$this.parent().addClass('oe_form_invalid');
}
});
this.compute_totals();
return this._super.apply(this, arguments);
},
// deactivate view related functions
load_views: function() {},
reload_current_view: function() {},
get_active_view: function() {},
});
}

33
web_widget_x2many_2d_matrix/static/src/xml/web_widget_x2many_2d_matrix.xml

@ -0,0 +1,33 @@
<templates>
<t t-name="FieldX2Many2dMatrix">
<div>
<table class="oe_list_content">
<thead>
<tr class="oe_list_header_columns">
<th />
<th t-foreach="widget.get_x_axis_labels()" t-as="label">
<t t-esc="label" />
</th>
<th t-if="widget.show_row_totals">Total</th>
</tr>
</thead>
<tbody>
<tr t-foreach="widget.get_y_axis_values()" t-as="y">
<th><t t-esc="widget.get_y_axis_label(y)" /></th>
<td t-foreach="widget.get_x_axis_values()" t-as="x">
<span t-att-class="widget.get_xy_value_class()">
<input t-att-data-x="x" t-att-data-y="y" t-att-data-id="widget.get_xy_id(x, y)" t-att-value="widget.format_xy_value(widget.get_xy_value(x, y))" />
</span>
</td>
<td t-if="widget.show_row_totals" class="row_total" t-att-data-y="y"/>
</tr>
<tr t-if="widget.show_column_totals">
<th>Total</th>
<td t-foreach="widget.get_x_axis_values()" t-as="x" class="column_total" t-att-data-x="x" />
<td />
</tr>
</tbody>
</table>
</div>
</t>
</templates>

11
web_widget_x2many_2d_matrix/views/templates.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<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/web_widget_x2many_2d_matrix.js"></script>
<link rel="stylesheet" href="/web_widget_x2many_2d_matrix/static/src/css/web_widget_x2many_2d_matrix.css"/>
</xpath>
</template>
</data>
</openerp>
Loading…
Cancel
Save