Skip to content

Commit be6bb3e

Browse files
[ADD] report_qweb_field_options
1 parent 7b085ad commit be6bb3e

19 files changed

+930
-0
lines changed

report_qweb_field_options/README.rst

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
=========================
2+
Report Qweb Field Options
3+
=========================
4+
5+
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6+
!! This file is generated by oca-gen-addon-readme !!
7+
!! changes will be overwritten. !!
8+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
9+
10+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
11+
:target: https://odoo-community.org/page/development-status
12+
:alt: Beta
13+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
14+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
15+
:alt: License: AGPL-3
16+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github
17+
:target: https://github.com/OCA/reporting-engine/tree/16.0/report_qweb_field_options
18+
:alt: OCA/reporting-engine
19+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
20+
:target: https://translation.odoo-community.org/projects/reporting-engine-16-0/reporting-engine-16-0-report_qweb_field_options
21+
:alt: Translate me on Weblate
22+
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
23+
:target: https://runbot.odoo-community.org/runbot/143/16.0
24+
:alt: Try me on Runbot
25+
26+
|badge1| |badge2| |badge3| |badge4| |badge5|
27+
28+
This module allows administrators to define the decimal precision of float fields and
29+
add option values to fields (e.g., adding a date widget option to datetime fields) for
30+
QWeb report and view presentation.
31+
32+
**Table of contents**
33+
34+
.. contents::
35+
:local:
36+
37+
Configuration
38+
=============
39+
40+
Go to *Settings > Technical > Reporting > Qweb Field Options*, and create records
41+
according to your needs.
42+
43+
For each record:
44+
45+
- Set **Model** and **Field** (required)
46+
- Set **UoM** and **UoM Field**, or **Currency** and **Currency Field** only for fields
47+
of float type (optional)
48+
- Set **Company** (optional)
49+
- Set **Options** as a string representation of a dictionary. E.g., ``{"widget": "date"}``,
50+
``{"widget": "monetary"}``, or ``{'widget': 'contact', 'fields': ['name', 'phone']}``
51+
- Set **Digits** (only for float-type fields). The value is ignored if Options is set
52+
53+
Usage
54+
=====
55+
56+
Print a QWeb report (quotation, invoice, purchase order, etc.), and the value
57+
presentation for fields like line quantity, price unit and date order are adjusted
58+
according to the Qweb Field Options configuration.
59+
60+
Note that among matching configuration records, the one with the strictest condition will be
61+
applied.
62+
63+
Known issues / Roadmap
64+
======================
65+
66+
Assigning Options in a QWeb Field Options record can cause UI issues if a field is
67+
defined twice with different widgets in a view.
68+
69+
For example, adding ``{"widget": "date"}`` to the date_approve field in a purchase order
70+
can result in two dates appearing under the Confirmation Date column in the portal view.
71+
This occurs because the field is defined twice with different widgets.
72+
73+
Reference: https://github.com/odoo/odoo/blob/5eec379/addons/purchase/views/portal_templates.xml#L101-L102
74+
75+
Bug Tracker
76+
===========
77+
78+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
79+
In case of trouble, please check there if your issue has already been reported.
80+
If you spotted it first, help us smashing it by providing a detailed and welcomed
81+
`feedback <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_qweb_field_options%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
82+
83+
Do not contact contributors directly about support or help with technical issues.
84+
85+
Credits
86+
=======
87+
88+
Authors
89+
~~~~~~~
90+
91+
* Quartile
92+
93+
Contributors
94+
~~~~~~~~~~~~
95+
96+
* `Quartile <https://www.quartile.co>`_:
97+
98+
* Yoshi Tashiro
99+
* Aung Ko Ko Lin
100+
101+
Maintainers
102+
~~~~~~~~~~~
103+
104+
This module is maintained by the OCA.
105+
106+
.. image:: https://odoo-community.org/logo.png
107+
:alt: Odoo Community Association
108+
:target: https://odoo-community.org
109+
110+
OCA, or the Odoo Community Association, is a nonprofit organization whose
111+
mission is to support the collaborative development of Odoo features and
112+
promote its widespread use.
113+
114+
This module is part of the `OCA/reporting-engine <https://github.com/OCA/reporting-engine/tree/16.0/report_qweb_field_options>`_ project on GitHub.
115+
116+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

report_qweb_field_options/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2024-2025 Quartile (https://www.quartile.com)
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Report Qweb Field Options",
5+
"version": "16.0.1.0.0",
6+
"category": "Technical Settings",
7+
"license": "AGPL-3",
8+
"author": "Quartile, Odoo Community Association (OCA)",
9+
"website": "https://github.com/OCA/reporting-engine",
10+
"depends": ["uom"],
11+
"data": [
12+
"security/ir.model.access.csv",
13+
"security/qweb_field_options_security.xml",
14+
"views/qweb_field_options_views.xml",
15+
],
16+
"installable": True,
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import ir_qweb
2+
from . import qweb_field_options
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright 2024-2025 Quartile (https://www.quartile.com)
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import api, models
5+
6+
7+
class IrQweb(models.AbstractModel):
8+
_inherit = "ir.qweb"
9+
10+
@api.model
11+
def _get_field(
12+
self, record, field_name, expression, tagName, field_options, values
13+
):
14+
if values.get("report_type") == "pdf":
15+
options_recs = self.env["qweb.field.options"].search(
16+
[("res_model_name", "=", record._name), ("field_name", "=", field_name)]
17+
)
18+
if options_recs:
19+
options_rec = max(
20+
options_recs, default=None, key=lambda r: r._get_score(record)
21+
)
22+
field_options = options_rec._update_field_options(record, field_options)
23+
return super()._get_field(
24+
record, field_name, expression, tagName, field_options, values
25+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright 2024-2025 Quartile (https://www.quartile.com)
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
import ast
5+
import logging
6+
7+
from odoo import _, api, fields, models
8+
from odoo.exceptions import ValidationError
9+
10+
_logger = logging.getLogger(__name__)
11+
12+
13+
class QwebFieldOptions(models.Model):
14+
_name = "qweb.field.options"
15+
_description = "Qweb Field Options"
16+
_order = "res_model_id, field_id"
17+
18+
res_model_id = fields.Many2one(
19+
"ir.model", string="Model", ondelete="cascade", required=True
20+
)
21+
res_model_name = fields.Char("Model Name", related="res_model_id.model", store=True)
22+
field_id = fields.Many2one(
23+
"ir.model.fields",
24+
domain="[('model_id', '=', res_model_id)]",
25+
string="Field",
26+
ondelete="cascade",
27+
required=True,
28+
)
29+
field_type = fields.Selection(related="field_id.ttype")
30+
field_name = fields.Char("Field Name", related="field_id.name", store=True)
31+
uom_id = fields.Many2one("uom.uom", string="UoM", ondelete="cascade")
32+
uom_field_id = fields.Many2one(
33+
"ir.model.fields",
34+
domain="[('model_id', '=', res_model_id), ('relation', '=', 'uom.uom')]",
35+
string="UoM Field",
36+
ondelete="cascade",
37+
)
38+
currency_id = fields.Many2one("res.currency", string="Currency", ondelete="cascade")
39+
currency_field_id = fields.Many2one(
40+
"ir.model.fields",
41+
domain="[('model_id', '=', res_model_id), ('relation', '=', 'res.currency')]",
42+
string="Currency Field",
43+
ondelete="cascade",
44+
)
45+
field_options = fields.Char(
46+
"Options",
47+
help="A string representation of a dictionary to specify field formatting "
48+
"options. Examples:\n"
49+
"{'widget': 'date'}\n"
50+
"{'widget': 'monetary'}\n"
51+
"{'widget': 'contact', 'fields': ['name', 'phone']}.",
52+
)
53+
digits = fields.Integer()
54+
company_id = fields.Many2one("res.company", string="Company")
55+
56+
@api.constrains("field_options")
57+
def _check_field_options_format(self):
58+
for rec in self:
59+
if not rec.field_options:
60+
continue
61+
field_options = False
62+
try:
63+
field_options = ast.literal_eval(rec.field_options)
64+
except Exception as e:
65+
raise ValidationError(
66+
_(
67+
"Invalid string for the Options field: %(field_options)s.\n"
68+
"Error: %(error)s"
69+
)
70+
% {"field_options": rec.field_options, "error": e}
71+
) from e
72+
if not isinstance(field_options, dict):
73+
raise ValidationError(
74+
_("Options must be a dictionary, but got %s") % type(field_options)
75+
)
76+
77+
def _get_score(self, record):
78+
self.ensure_one()
79+
score = 1
80+
if self.company_id:
81+
if record.company_id == self.company_id:
82+
score += 1
83+
else:
84+
return -1
85+
if self.uom_id:
86+
if record[self.uom_field_id.sudo().name] == self.uom_id:
87+
score += 1
88+
else:
89+
return -1
90+
if self.currency_id:
91+
if record[self.currency_field_id.sudo().name] == self.currency_id:
92+
score += 1
93+
else:
94+
return -1
95+
return score
96+
97+
def _update_field_options(self, record, field_options):
98+
self.ensure_one()
99+
if self.field_options:
100+
try:
101+
extra_options = ast.literal_eval(self.field_options)
102+
if extra_options.get("widget") == "monetary":
103+
extra_options["display_currency"] = (
104+
self.currency_id
105+
or hasattr(record, "company_id")
106+
and record.company_id.currency_id
107+
or self.env.company.currency_id
108+
)
109+
field_options.update(extra_options)
110+
except Exception as e:
111+
_logger.error(
112+
"Failed to parse field options as a dictionary: "
113+
f"{self.field_options}. Error: {e}"
114+
)
115+
elif self.field_type == "float":
116+
field_options["precision"] = self.digits
117+
return field_options
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Go to *Settings > Technical > Reporting > Qweb Field Options*, and create records
2+
according to your needs.
3+
4+
For each record:
5+
6+
- Set **Model** and **Field** (required)
7+
- Set **UoM** and **UoM Field**, or **Currency** and **Currency Field** only for fields
8+
of float type (optional)
9+
- Set **Company** (optional)
10+
- Set **Options** as a string representation of a dictionary. E.g., ``{"widget": "date"}``,
11+
``{"widget": "monetary"}``, or ``{'widget': 'contact', 'fields': ['name', 'phone']}``
12+
- Set **Digits** (only for float-type fields). The value is ignored if Options is set
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
* `Quartile <https://www.quartile.co>`_:
2+
3+
* Yoshi Tashiro
4+
* Aung Ko Ko Lin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This module allows administrators to define the decimal precision of float fields and
2+
add option values to fields (e.g., adding a date widget option to datetime fields) for
3+
QWeb report and view presentation.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Assigning Options in a QWeb Field Options record can cause UI issues if a field is
2+
defined twice with different widgets in a view.
3+
4+
For example, adding ``{"widget": "date"}`` to the date_approve field in a purchase order
5+
can result in two dates appearing under the Confirmation Date column in the portal view.
6+
This occurs because the field is defined twice with different widgets.
7+
8+
Reference: https://github.com/odoo/odoo/blob/5eec379/addons/purchase/views/portal_templates.xml#L101-L102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Print a QWeb report (quotation, invoice, purchase order, etc.), and the value
2+
presentation for fields like line quantity, price unit and date order are adjusted
3+
according to the Qweb Field Options configuration.
4+
5+
Note that among matching configuration records, the one with the strictest condition will be
6+
applied.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
access_qweb_field_options_all,access.qweb.field.options.all,model_qweb_field_options,,1,0,0,0
3+
access_qweb_field_options_admin,access.qweb.field.options.admin,model_qweb_field_options,base.group_system,1,1,1,1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<record id="qweb_field_options_company_rule" model="ir.rule">
4+
<field name="name">Qweb Field Options Multi-company</field>
5+
<field name="model_id" ref="model_qweb_field_options" />
6+
<field
7+
name="domain_force"
8+
>['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
9+
</record>
10+
</odoo>

0 commit comments

Comments
 (0)