Merge PR #1746 into 13.0

Signed-off-by pedrobaeza
pull/1753/head
OCA-git-bot 2020-12-01 19:39:40 +00:00
commit 22cd6e24f5
17 changed files with 1578 additions and 0 deletions

View File

@ -0,0 +1 @@
../../../../web_pivot_computed_measure

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@ -0,0 +1,111 @@
==========================
Web Pivot Computed Measure
==========================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/13.0/web_pivot_computed_measure
:alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-13-0/web-13-0-web_pivot_computed_measure
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/162/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
Adds support for computed measures on the pivot view.
**Table of contents**
.. contents::
:local:
Usage
=====
Go to pivot view and click on the "Measures" menu, you will see
a new option called 'Computed Measure'.
You have the follow options to create a 'computed measure':
- Measure 1: Used in 'operation formula' as 'm1'
- Measure 2: Used in 'operation formula' as 'm2'
- Operation: The formula
- Sum: m1 + m2
- Sub: m1 - m2
- Mult: m1 * m2
- Div: m1 / m2 (Format: Float)
- Perc m1 / m2 (Format: Percentage)
- Custom: Special option only visible in debug mode to write a custom formula.
- Name: The name of the new measure (emtpy = auto-generated)
- Format: How will the value be printed
- Integer
- Float
- Percentage (value * 100)
- Formula*: Custom operation formula
These formula is evaluated using 'PY.eval'
These computed measures can be mixed (You can reuse it to make new computed measures) and saved as favorites.
Notice that "measures/computed measures" involved in an active 'computed measure'
can't be deactivated until you have deactivate the 'computed measure'.
Known issues / Roadmap
======================
#. Add support to define a style for a computed measure (ex. colored)
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_pivot_computed_measure%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Tecnativa
Contributors
~~~~~~~~~~~~
* `Tecnativa <https://www.tecnativa.com/>`_:
* Alexandre D. Díaz
* Pedro M. Baeza
* Ernesto Tejeda
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
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.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/13.0/web_pivot_computed_measure>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -0,0 +1,16 @@
# Copyright 2020 Tecnativa - Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
{
"name": "Web Pivot Computed Measure",
"category": "web",
"version": "13.0.1.0.0",
"author": "Tecnativa, Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/web",
"depends": ["web"],
"data": ["view/assets.xml"],
"qweb": ["static/src/xml/web_pivot_computed_measure.xml"],
"auto_install": False,
"installable": True,
}

View File

@ -0,0 +1,152 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_pivot_computed_measure
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2020-09-16 14:00+0000\n"
"Last-Translator: claudiagn <claudia.gargallo@qubiq.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:96
#, python-format
msgid "Add"
msgstr "Añadir"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:87
#, python-format
msgid "Can be empty"
msgstr "Puede estar vacío"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:47
#, python-format
msgid "Computed Measure"
msgstr "Medida computada"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:24
#, python-format
msgid "Custom"
msgstr "Customizado"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:18
#, python-format
msgid "Div (m1 / m2)"
msgstr "Div (m1 / m2)"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:33
#, python-format
msgid "Float"
msgstr "Flotador"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:90
#, python-format
msgid "Format"
msgstr "Formato"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:82
#, python-format
msgid "Formula"
msgstr "Fórmula"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:30
#, python-format
msgid "Integer"
msgstr "Entero"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:56
#, python-format
msgid "Measure 1"
msgstr "Medida 1"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:66
#, python-format
msgid "Measure 2"
msgstr "Medida 2"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:15
#, python-format
msgid "Mult (m1 * m2)"
msgstr "Mult (m1 * m2)"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:86
#, python-format
msgid "Name"
msgstr "Nombre"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:76
#, python-format
msgid "Operation"
msgstr "Operación"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:21
#, python-format
msgid "Perc (m1 * 100 / m2)"
msgstr "Perc (m1 * 100 / m2)"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:36
#, python-format
msgid "Percentage"
msgstr "Porcentaje"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:12
#, python-format
msgid "Sub (m1 - m2)"
msgstr "Sub (m1 - m2)"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:9
#, python-format
msgid "Sum (m1 + m2)"
msgstr "Sum (m1 + m2)"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/js/pivot_model.js:275
#, python-format
msgid "This measure is currently used by a 'computed measure'. Please, disable the computed measure first."
msgstr ""
"Esta medida está utilizada por una 'medida computada'. Por favor, desabilita "
"la medida computada primero."

View File

@ -0,0 +1,148 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_pivot_computed_measure
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:96
#, python-format
msgid "Add"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:87
#, python-format
msgid "Can be empty"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:47
#, python-format
msgid "Computed Measure"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:24
#, python-format
msgid "Custom"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:18
#, python-format
msgid "Div (m1 / m2)"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:33
#, python-format
msgid "Float"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:90
#, python-format
msgid "Format"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:82
#, python-format
msgid "Formula"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:30
#, python-format
msgid "Integer"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:56
#, python-format
msgid "Measure 1"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:66
#, python-format
msgid "Measure 2"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:15
#, python-format
msgid "Mult (m1 * m2)"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:86
#, python-format
msgid "Name"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:76
#, python-format
msgid "Operation"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:21
#, python-format
msgid "Perc (m1 * 100 / m2)"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:36
#, python-format
msgid "Percentage"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:12
#, python-format
msgid "Sub (m1 - m2)"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/xml/web_pivot_computed_measure.xml:9
#, python-format
msgid "Sum (m1 + m2)"
msgstr ""
#. module: web_pivot_computed_measure
#. openerp-web
#: code:addons/web_pivot_computed_measure/static/src/js/pivot_model.js:275
#, python-format
msgid "This measure is currently used by a 'computed measure'. Please, disable the computed measure first."
msgstr ""

View File

@ -0,0 +1,5 @@
* `Tecnativa <https://www.tecnativa.com/>`_:
* Alexandre D. Díaz
* Pedro M. Baeza
* Ernesto Tejeda

View File

@ -0,0 +1 @@
Adds support for computed measures on the pivot view.

View File

@ -0,0 +1 @@
#. Add support to define a style for a computed measure (ex. colored)

View File

@ -0,0 +1,25 @@
Go to pivot view and click on the "Measures" menu, you will see
a new option called 'Computed Measure'.
You have the follow options to create a 'computed measure':
- Measure 1: Used in 'operation formula' as 'm1'
- Measure 2: Used in 'operation formula' as 'm2'
- Operation: The formula
- Sum: m1 + m2
- Sub: m1 - m2
- Mult: m1 * m2
- Div: m1 / m2 (Format: Float)
- Perc m1 / m2 (Format: Percentage)
- Custom: Special option only visible in debug mode to write a custom formula.
- Name: The name of the new measure (emtpy = auto-generated)
- Format: How will the value be printed
- Integer
- Float
- Percentage (value * 100)
- Formula*: Custom operation formula
These formula is evaluated using 'PY.eval'
These computed measures can be mixed (You can reuse it to make new computed measures) and saved as favorites.
Notice that "measures/computed measures" involved in an active 'computed measure'
can't be deactivated until you have deactivate the 'computed measure'.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,477 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Web Pivot Computed Measure</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="web-pivot-computed-measure">
<h1 class="title">Web Pivot Computed Measure</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/13.0/web_pivot_computed_measure"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-13-0/web-13-0-web_pivot_computed_measure"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>Adds support for computed measures on the pivot view.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<p>Go to pivot view and click on the “Measures” menu, you will see
a new option called Computed Measure.</p>
<dl class="docutils">
<dt>You have the follow options to create a computed measure:</dt>
<dd><ul class="first last simple">
<li>Measure 1: Used in operation formula as m1</li>
<li>Measure 2: Used in operation formula as m2</li>
<li><dl class="first docutils">
<dt>Operation: The formula</dt>
<dd><ul class="first last">
<li>Sum: m1 + m2</li>
<li>Sub: m1 - m2</li>
<li>Mult: m1 * m2</li>
<li>Div: m1 / m2 (Format: Float)</li>
<li>Perc m1 / m2 (Format: Percentage)</li>
<li>Custom: Special option only visible in debug mode to write a custom formula.</li>
</ul>
</dd>
</dl>
</li>
<li>Name: The name of the new measure (emtpy = auto-generated)</li>
<li><dl class="first docutils">
<dt>Format: How will the value be printed</dt>
<dd><ul class="first last">
<li>Integer</li>
<li>Float</li>
<li>Percentage (value * 100)</li>
</ul>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>Formula*: Custom operation formula</dt>
<dd>These formula is evaluated using PY.eval</dd>
</dl>
</li>
</ul>
</dd>
</dl>
<p>These computed measures can be mixed (You can reuse it to make new computed measures) and saved as favorites.</p>
<p>Notice that “measures/computed measures” involved in an active computed measure
cant be deactivated until you have deactivate the computed measure.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
<ol class="arabic simple">
<li>Add support to define a style for a computed measure (ex. colored)</li>
</ol>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_pivot_computed_measure%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.tecnativa.com/">Tecnativa</a>:<ul>
<li>Alexandre D. Díaz</li>
<li>Pedro M. Baeza</li>
<li>Ernesto Tejeda</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/13.0/web_pivot_computed_measure">OCA/web</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,193 @@
/* Copyright 2020 Tecnativa - Alexandre Díaz
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */
odoo.define("web_pivot_computed_measure.PivotController", function(require) {
"use strict";
const core = require("web.core");
const config = require("web.config");
const PivotController = require("web.PivotController");
const QWeb = core.qweb;
PivotController.include({
custom_events: _.extend({}, PivotController.prototype.custom_events, {
add_measure: "_onAddMeasure",
remove_measure: "_onRemoveMeasure",
}),
computed_measures_open: false,
/**
* Add the computed measures to the context. This
* will be used when save a filter.
*
* @override
*/
getOwnedQueryParams: function() {
const res = this._super.apply(this, arguments);
const state = this.model.get({raw: true});
res.context.pivot_computed_measures = state.computed_measures;
return res;
},
/**
* @override
*/
renderButtons: function($node) {
this._super.apply(this, arguments);
if ($node) {
this._renderComputedMeasures();
}
},
/**
* Handle click event on measures menu to support computed measures sub-menu
*
* @override
*/
_onButtonClick: function(event) {
const $target = $(event.target);
if ($target.parents("div[data-id='__computed__']").length) {
let hideMenu = false;
event.preventDefault();
if (
$target.hasClass("dropdown-item") ||
$target.hasClass("o_submenu_switcher")
) {
this.computed_measures_open = !this.computed_measures_open;
this._renderComputedMeasures();
} else if ($target.hasClass("o_add_computed_measure")) {
hideMenu = true;
const field1 = this.$buttons_measures_ex
.find("#computed_measure_field_1")
.val();
const field2 = this.$buttons_measures_ex
.find("#computed_measure_field_2")
.val();
let oper = this.$buttons_measures_ex
.find("#computed_measure_operation")
.val();
if (oper === "custom") {
oper = this.$buttons_measures_ex
.find("#computed_measure_operation_custom")
.val();
}
const name = this.$buttons_measures_ex
.find("#computed_measure_name")
.val();
const format = this.$buttons_measures_ex
.find("#computed_measure_format")
.val();
const uniqueId = new Date().getTime();
this.model
.createComputedMeasure(
uniqueId,
field1,
field2,
oper,
name,
format
)
.then(this.update.bind(this, {}, {reload: false}));
}
if (!hideMenu) {
event.stopPropagation();
}
return;
}
this._super.apply(this, arguments);
},
/**
* Render computed measures menu
*
* @private
*/
_renderComputedMeasures: function() {
if (this.$buttons_measures_ex && this.$buttons_measures_ex.length) {
this.$buttons_measures_ex.remove();
}
const measures = _.sortBy(_.pairs(_.omit(this.measures, "__count")), x => {
return x[1].string.toLowerCase();
});
this.$buttons_measures_ex = $(
QWeb.render("web_pivot_computed_measure.ExtendedMenu", {
isOpen: this.computed_measures_open,
debug: config.isDebug(),
measures: measures,
computed_measures: _.map(
_.reject(measures, item => {
return !item[1].__computed_id;
}),
item => {
item[1].active = _.contains(
this.model.data.measures,
item[0]
);
return item;
}
),
})
);
this.$buttons_measures_ex
.find("#computed_measure_operation")
.on("change", this._onChangeComputedMeasureOperation.bind(this));
if (this.$buttons)
this.$buttons
.find(".o_pivot_measures_list")
.append(this.$buttons_measures_ex);
},
/**
* Custom event to add a new measure
*
* @private
* @param {CustomEvent} ev
*/
_onAddMeasure: function(ev) {
this.measures[ev.data.id] = ev.data.def;
this._renderComputedMeasures();
},
/**
* Custom event to remove a measure
*
* @private
* @param {CustomEvent} ev
*/
_onRemoveMeasure: function(ev) {
delete this.measures[ev.data.id];
this._renderComputedMeasures();
},
/**
* Set default values related with the selected operation
*
* @private
* @param {ChangeEvent} ev
*/
_onChangeComputedMeasureOperation: function(ev) {
const $option = $(ev.target.options[ev.target.selectedIndex]);
if ($(ev.target).val() === "custom") {
this.$buttons_measures_ex
.find("#container_computed_measure_operation_custom")
.removeClass("d-none");
} else {
const format = $option.data("format");
if (format) {
this.$buttons_measures_ex
.find("#computed_measure_format")
.val(format);
}
this.$buttons_measures_ex
.find("#container_computed_measure_operation_custom")
.addClass("d-none");
}
},
});
});

View File

@ -0,0 +1,294 @@
/* Copyright 2020 Tecnativa - Alexandre Díaz
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) */
odoo.define("web_pivot_computed_measure.PivotModel", function(require) {
"use strict";
const core = require("web.core");
const PivotModel = require("web.PivotModel");
const _t = core._t;
PivotModel.include({
_computed_measures: [],
/**
* Create a new computed measure
*
* @param {String} id
* @param {String} field1
* @param {String} field2
* @param {String} operation
* @param {String} name
* @param {String} format
* @returns a promise
*/
createComputedMeasure: function(id, field1, field2, operation, name, format) {
const measure = _.find(this._computed_measures, item => {
return (
item.field1 === field1 &&
item.field2 === field2 &&
item.operation === operation
);
});
if (measure) {
return Promise.resolve();
}
const fieldM1 = this.fields[field1];
const fieldM2 = this.fields[field2];
const cmId = "__computed_" + id;
const oper = operation.replace(/m1/g, field1).replace(/m2/g, field2);
const oper_human = operation
.replace(
/m1/g,
fieldM1.__computed_id ? "(" + fieldM1.string + ")" : fieldM1.string
)
.replace(
/m2/g,
fieldM2.__computed_id ? "(" + fieldM2.string + ")" : fieldM2.string
);
const cmTotal = this._computed_measures.push({
field1: field1,
field2: field2,
operation: oper,
name: name || oper_human,
id: cmId,
format: format,
});
return this._createVirtualMeasure(this._computed_measures[cmTotal - 1]);
},
/**
* Create and enable a measure based on a 'fake' field
*
* @private
* @param {Object} cmDef
* @param {List} fields *Optional*
* @returns a promise
*/
_createVirtualMeasure: function(cmDef, fields) {
const arrFields = fields || this.fields;
// This is a minimal 'fake' field info
arrFields[cmDef.id] = {
// Used to format the value
type: cmDef.format,
// Used to print the header name
string: cmDef.name,
// Used to know if is a computed measure field
__computed_id: cmDef.id,
};
this.trigger_up("add_measure", {
id: cmDef.id,
def: arrFields[cmDef.id],
});
return this._activeMeasures([cmDef.field1, cmDef.field2, cmDef.id]);
},
/*
* @private
* @param {List of Strings} fields
*/
_activeMeasures: function(fields) {
let needLoad = false;
for (const field of fields) {
if (!this._isMeasureEnabled(field)) {
this.data.measures.push(field);
needLoad = true;
}
}
if (needLoad) {
return this._loadData();
}
return Promise.resolve();
},
/*
* @private
* @param {String} field
*/
_isMeasureEnabled: function(field) {
return _.contains(this.data.measures, field);
},
/**
* Helper function to add computed measure fields data into a 'subGroupData'
*
* @private
* @param {Object} subGroupData
*/
_fillComputedMeasuresData: function(subGroupData) {
for (const cm of this._computed_measures) {
if (!this._isMeasureEnabled(cm.id)) return;
if (subGroupData.__count === 0) {
subGroupData[cm.id] = false;
} else {
// eslint-disable-next-line no-undef
subGroupData[cm.id] = py.eval(cm.operation, subGroupData);
}
}
},
/**
* Fill the groupSubdivisions with the computed measures and their values
*
* @override
*/
_prepareData: function(group, groupSubdivisions) {
for (const groupSubdivision of groupSubdivisions) {
for (const subGroup of groupSubdivision.subGroups) {
this._fillComputedMeasuresData(subGroup);
}
}
this._super.apply(this, arguments);
},
/**
* _getGroupSubdivision method invokes the read_group method of the
* model via rpc and the passed 'fields' argument is the list of
* measure names that is in this.data.measures, so we remove the
* computed measures form this.data.measures before calling _super
* to prevent an exception
*
* @override
*/
_getGroupSubdivision: function() {
const computed_measures = [];
for (let i = 0; i < this.data.measures.length; i++)
if (this.data.measures[i].startsWith("__computed_")) {
computed_measures.push(this.data.measures[i]);
this.data.measures.splice(i, 1);
i--;
}
const res = this._super.apply(this, arguments);
$.merge(this.data.measures, computed_measures);
return res;
},
/**
* Load the computed measures in context. This is used by filters.
*
* @override
*/
load: function(params) {
this._computed_measures =
params.context.pivot_computed_measures ||
params.computed_measures ||
[];
const toActive = [];
for (const cmDef of this._computed_measures) {
params.fields[cmDef.id] = {
type: cmDef.format,
string: cmDef.name,
__computed_id: cmDef.id,
};
toActive.push(cmDef.field1, cmDef.field2, cmDef.id);
}
return this._super(params).then(() => {
_.defer(() => {
for (const cmDef of this._computed_measures) {
this.trigger_up("add_measure", {
id: cmDef.id,
def: this.fields[cmDef.id],
});
}
});
this._activeMeasures(toActive);
});
},
/**
* Load the computed measures in context. This is used by filters.
*
* @override
*/
reload: function(handle, params) {
if ("context" in params) {
this._computed_measures =
params.context.pivot_computed_measures ||
params.computed_measures ||
[];
}
for (const cmDef of this._computed_measures) {
this._createVirtualMeasure(cmDef);
}
const fieldNames = Object.keys(this.fields);
for (const fieldName of fieldNames) {
const field = this.fields[fieldName];
if (field.__computed_id) {
const cm = _.find(this._computed_measures, {
id: field.__computed_id,
});
if (!cm) {
delete this.fields[fieldName];
this.data.measures = _.without(this.data.measures, fieldName);
this.trigger_up("remove_measure", {
id: fieldName,
});
}
}
}
return this._super.apply(this, arguments);
},
/**
* Add the computed measures to the state. This is used by filters.
*
* @override
*/
get: function() {
const res = this._super.apply(this, arguments);
res.computed_measures = this._computed_measures;
return res;
},
/**
* Adds a rule to deny that measures can be disabled if are being used by a computed measure.
* In the other hand, when enables a measure analyzes it to active all involved measures.
*
* @override
*/
toggleMeasure: function(field) {
if (this._isMeasureEnabled(field)) {
// Measure is disabled
const umeasures = _.filter(this._computed_measures, item => {
return item.field1 === field || item.field2 === field;
});
if (umeasures.length && this._isMeasureEnabled(umeasures[0].id)) {
return Promise.reject(
_t(
"This measure is currently used by a 'computed measure'. Please, disable the computed measure first."
)
);
}
} else {
// Mesaure is enabled
const toEnable = [];
const toAnalize = [field];
while (toAnalize.length) {
const afield = toAnalize.shift();
const fieldDef = this.fields[afield];
if (fieldDef.__computed_id) {
const cm = _.find(this._computed_measures, {
id: fieldDef.__computed_id,
});
toAnalize.push(cm.field1, cm.field2);
const toEnableFields = [];
if (!this.fields[cm.field1].__computed_id) {
toEnableFields.push(cm.field1);
}
if (!this.fields[cm.field2].__computed_id) {
toEnableFields.push(cm.field2);
}
toEnableFields.push(afield);
toEnable.push(toEnableFields);
}
}
if (toEnable.length) {
return this._activeMeasures(_.flatten(toEnable.reverse()));
}
}
return this._super.apply(this, arguments);
},
});
});

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright 2020 Tecnativa - Alexandre Díaz
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<templates>
<t t-name="web_pivot_computed_measure.ComputedMeasureOperations">
<option name="sum" value="m1+m2">
Sum (m1 + m2)
</option>
<option name="sub" value="m1-m2">
Sub (m1 - m2)
</option>
<option name="mult" value="m1*m2">
Mult (m1 * m2)
</option>
<option name="div" data-format="float" value="m1/m2">
Div (m1 / m2)
</option>
<option name="perc" data-format="percentage" value="m1/m2">
Perc (m1 * 100 / m2)
</option>
<option t-if="debug" name="custom" value="custom">
Custom
</option>
</t>
<t t-name="web_pivot_computed_measure.ComputedMeasureFormats">
<option name="int" value="integer">
Integer
</option>
<option name="float" value="float" selected="selected">
Float
</option>
<option name="percentage" value="percentage">
Percentage
</option>
</t>
<t t-name="web_pivot_computed_measure.ExtendedMenu">
<div role="separator" class="dropdown-divider" />
<t t-foreach="computed_measures" t-as="cm">
<a
role="menuitem"
href="#"
t-attf-class="dropdown-item {{cm[1].active and 'selected' or ''}}"
t-data-computed="1"
t-att-data-field="cm[0]"
>
<t t-esc="cm[1].string" />
</a>
</t>
<div class="o_menu_item" data-id="__computed__">
<a href="#" role="menuitem" class="dropdown-item">
Computed Measure
<span class="o_submenu_switcher" data-id="__computed__">
<span
t-att-class="isOpen ? 'fa fa-caret-down' : 'fa fa-caret-right'"
/>
</span>
</a>
<t t-if="isOpen">
<div class="dropdown-item-text">
<label for="computed_measure_field_1">Measure 1</label>
<select
class="o_input o_date_field_selector"
id="computed_measure_field_1"
>
<t t-foreach="measures" t-as="measure">
<option t-att-value="measure[0]">
<t t-esc="measure[1].string" />
</option>
</t>
</select>
</div>
<div class="dropdown-item-text">
<label for="computed_measure_field_2">Measure 2</label>
<select
class="o_input o_time_range_selector"
id="computed_measure_field_2"
>
<t t-foreach="measures" t-as="measure">
<option t-att-value="measure[0]">
<t t-esc="measure[1].string" />
</option>
</t>
</select>
</div>
<div class="dropdown-item-text">
<label for="computed_measure_operation">Operation</label>
<select
class="o_input o_time_range_selector"
id="computed_measure_operation"
>
<t
t-call="web_pivot_computed_measure.ComputedMeasureOperations"
/>
</select>
</div>
<div
t-if="debug"
class="dropdown-item-text d-none"
id="container_computed_measure_operation_custom"
>
<label for="computed_measure_operation_custom">Formula</label>
<input type="text" id="computed_measure_operation_custom" />
</div>
<div class="dropdown-item-text">
<label for="computed_measure_name">Name</label>
<input
placeholder="Can be empty"
type="text"
id="computed_measure_name"
/>
</div>
<div class="dropdown-item-text">
<label for="computed_measure_format">Format</label>
<select
class="o_input o_time_range_selector"
id="computed_measure_format"
>
<t t-call="web_pivot_computed_measure.ComputedMeasureFormats" />
</select>
</div>
<div class="dropdown-item-text">
<button
class="btn btn-primary o_add_computed_measure"
type="button"
>Add</button>
</div>
</t>
</div>
</t>
</templates>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script
type="text/javascript"
src="/web_pivot_computed_measure/static/src/js/pivot_model.js"
/>
<script
type="text/javascript"
src="/web_pivot_computed_measure/static/src/js/pivot_controller.js"
/>
</xpath>
</template>
</odoo>