Skip to content

Commit 8bf2d58

Browse files
[ADD] report_qweb_field_options
1 parent ca02b2d commit 8bf2d58

21 files changed

+1020
-0
lines changed

report_qweb_field_options/README.rst

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
=========================
2+
Report Qweb Field Options
3+
=========================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:57f77e238517a4774ef6dd76a5487221564468ed0f58e18caa48f32632ac56a7
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github
20+
:target: https://github.com/OCA/reporting-engine/tree/16.0/report_qweb_field_options
21+
:alt: OCA/reporting-engine
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/reporting-engine-16-0/reporting-engine-16-0-report_qweb_field_options
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module allows administrators to define the decimal precision of float fields and
32+
add option values to fields (e.g., adding a date widget option to datetime fields) for
33+
QWeb report and view presentation.
34+
35+
**Table of contents**
36+
37+
.. contents::
38+
:local:
39+
40+
Configuration
41+
=============
42+
43+
Go to *Settings > Technical > Reporting > Qweb Field Options*, and create records
44+
according to your needs.
45+
46+
For each record:
47+
48+
- Set **Model** and **Field** (required)
49+
- Set **UoM** and **UoM Field**, or **Currency** and **Currency Field** only for fields
50+
of float type (optional)
51+
- Set **Company** (optional)
52+
- Set **Options** as a string representation of a dictionary. E.g., ``{"widget": "date"}``,
53+
``{"widget": "monetary"}``, or ``{'widget': 'contact', 'fields': ['name', 'phone']}``
54+
- Set **Digits** (only for float-type fields). The value is ignored if Options is set
55+
56+
Usage
57+
=====
58+
59+
Print a QWeb report (quotation, invoice, purchase order, etc.), and the value
60+
presentation for fields like line quantity, price unit and date order are adjusted
61+
according to the Qweb Field Options configuration.
62+
63+
Note that among matching configuration records, the one with the strictest condition will be
64+
applied.
65+
66+
Known issues / Roadmap
67+
======================
68+
69+
Assigning Options in a QWeb Field Options record can cause UI issues if a field is
70+
defined twice with different widgets in a view.
71+
72+
For example, adding ``{"widget": "date"}`` to the date_approve field in a purchase order
73+
can result in two dates appearing under the Confirmation Date column in the portal view.
74+
This occurs because the field is defined twice with different widgets.
75+
76+
Reference: https://github.com/odoo/odoo/blob/5eec379/addons/purchase/views/portal_templates.xml#L101-L102
77+
78+
Bug Tracker
79+
===========
80+
81+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
82+
In case of trouble, please check there if your issue has already been reported.
83+
If you spotted it first, help us to smash it by providing a detailed and welcomed
84+
`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**>`_.
85+
86+
Do not contact contributors directly about support or help with technical issues.
87+
88+
Credits
89+
=======
90+
91+
Authors
92+
~~~~~~~
93+
94+
* Quartile
95+
96+
Contributors
97+
~~~~~~~~~~~~
98+
99+
* `Quartile <https://www.quartile.co>`_:
100+
101+
* Yoshi Tashiro
102+
* Aung Ko Ko Lin
103+
104+
Maintainers
105+
~~~~~~~~~~~
106+
107+
This module is maintained by the OCA.
108+
109+
.. image:: https://odoo-community.org/logo.png
110+
:alt: Odoo Community Association
111+
:target: https://odoo-community.org
112+
113+
OCA, or the Odoo Community Association, is a nonprofit organization whose
114+
mission is to support the collaborative development of Odoo features and
115+
promote its widespread use.
116+
117+
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.
118+
119+
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
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
"external_dependencies": {
12+
"python": ["odoo_test_helper"],
13+
},
14+
"data": [
15+
"security/ir.model.access.csv",
16+
"security/qweb_field_options_security.xml",
17+
"views/qweb_field_options_views.xml",
18+
],
19+
"installable": True,
20+
}
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,32 @@
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+
company = getattr(record, "company_id", False) or self.env.company
16+
options_recs = self.env["qweb.field.options"].search(
17+
[
18+
("res_model_name", "=", record._name),
19+
("field_name", "=", field_name),
20+
"|",
21+
("company_id", "=", company.id),
22+
("company_id", "=", False),
23+
]
24+
)
25+
if options_recs:
26+
options_rec = max(
27+
options_recs, default=None, key=lambda r: r._get_score(record)
28+
)
29+
field_options = options_rec._update_field_options(record, field_options)
30+
return super()._get_field(
31+
record, field_name, expression, tagName, field_options, values
32+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
# Just increment the score as the record already matches the company_id,
81+
# which has been filtered prior to calling this method.
82+
if self.company_id:
83+
score += 1
84+
if self.uom_id:
85+
if record[self.uom_field_id.sudo().name] == self.uom_id:
86+
score += 1
87+
else:
88+
return -1
89+
if self.currency_id:
90+
if record[self.currency_field_id.sudo().name] == self.currency_id:
91+
score += 1
92+
else:
93+
return -1
94+
return score
95+
96+
def _update_field_options(self, record, field_options):
97+
self.ensure_one()
98+
if self.field_options:
99+
try:
100+
extra_options = ast.literal_eval(self.field_options)
101+
if extra_options.get("widget") == "monetary":
102+
extra_options["display_currency"] = (
103+
self.currency_id
104+
or hasattr(record, "company_id")
105+
and record.company_id.currency_id
106+
or self.env.company.currency_id
107+
)
108+
field_options.update(extra_options)
109+
except Exception as e:
110+
_logger.error(
111+
"Failed to parse field options as a dictionary: "
112+
f"{self.field_options}. Error: {e}"
113+
)
114+
elif self.field_type == "float":
115+
field_options["precision"] = self.digits
116+
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)