From 741b22c989974ef6a991b76e066f6368fdc99436 Mon Sep 17 00:00:00 2001
From: Florian da Costa
Date: Fri, 3 Feb 2023 11:36:59 +0100
Subject: [PATCH 1/3] [IMP] Add back the variable feature in sql_export
Refactore to use the new properties field
---
sql_export/__manifest__.py | 7 +-
sql_export/demo/ir_model_fields.xml | 37 ---------
sql_export/demo/sql_export.xml | 10 +--
sql_export/models/sql_export.py | 50 ++++++------
sql_export/readme/DESCRIPTION.rst | 2 +-
sql_export/readme/ROADMAP.rst | 2 -
sql_export/readme/USAGE.rst | 3 +-
.../static/src/scss/modal_properties.scss | 3 +
sql_export/tests/test_sql_query.py | 36 +++++++--
sql_export/views/ir_model_fields_view.xml | 76 -------------------
sql_export/views/sql_export_view.xml | 34 +++++----
sql_export/wizard/wizard_file.py | 71 +++--------------
sql_export/wizard/wizard_file_view.xml | 10 ++-
sql_export_mail/models/sql_export.py | 4 +-
14 files changed, 111 insertions(+), 234 deletions(-)
delete mode 100644 sql_export/demo/ir_model_fields.xml
create mode 100644 sql_export/static/src/scss/modal_properties.scss
delete mode 100644 sql_export/views/ir_model_fields_view.xml
diff --git a/sql_export/__manifest__.py b/sql_export/__manifest__.py
index f4ae2a407b..9293e64701 100644
--- a/sql_export/__manifest__.py
+++ b/sql_export/__manifest__.py
@@ -17,14 +17,17 @@
],
"data": [
"views/sql_export_view.xml",
- "views/ir_model_fields_view.xml",
"wizard/wizard_file_view.xml",
"security/sql_export_security.xml",
"security/ir.model.access.csv",
],
"demo": [
- "demo/ir_model_fields.xml",
"demo/sql_export.xml",
],
+ "assets": {
+ "web.assets_backend": [
+ "sql_export/static/src/scss/modal_properties.scss",
+ ]
+ },
"installable": True,
}
diff --git a/sql_export/demo/ir_model_fields.xml b/sql_export/demo/ir_model_fields.xml
deleted file mode 100644
index 49b68e6608..0000000000
--- a/sql_export/demo/ir_model_fields.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
- x_date
- Date
- date
-
- sql.file.wizard
- manual
-
-
-
- x_id
- x_ID
- integer
-
- sql.file.wizard
- manual
-
-
-
- x_partner_categ_ids
- Partner Categories
- many2many
-
- sql.file.wizard
- manual
- res.partner.category
-
-
-
diff --git a/sql_export/demo/sql_export.xml b/sql_export/demo/sql_export.xml
index 9c7d461c3b..8d29019ca9 100644
--- a/sql_export/demo/sql_export.xml
+++ b/sql_export/demo/sql_export.xml
@@ -30,14 +30,14 @@ SELECT p.id
FROM res_partner p
LEFT JOIN res_partner_res_partner_category_rel rel
ON rel.partner_id = p.id
-WHERE create_date < %(x_date)s
-AND id = %(x_id)s
-AND rel.category_id in %(x_partner_categ_ids)s
+ WHERE create_date > %(Date)s
+AND id = %(ID)s
+AND rel.category_id in %(Categories)s
]]>
diff --git a/sql_export/models/sql_export.py b/sql_export/models/sql_export.py
index 41ca11bcb0..e109a70eb9 100644
--- a/sql_export/models/sql_export.py
+++ b/sql_export/models/sql_export.py
@@ -17,29 +17,8 @@ class SqlExport(models.Model):
file_format = fields.Selection([("csv", "CSV")], default="csv", required=True)
- field_ids = fields.Many2many(
- "ir.model.fields",
- "fields_sqlquery_rel",
- "sql_id",
- "field_id",
- "Parameters",
- domain=[("model", "=", "sql.file.wizard"), ("state", "=", "manual")],
- help="Before adding parameters, make sure you have created one that fill your "
- "need in the dedicated menu with the right type and label. \n"
- "Then, when you add a parameter here, you have to include it in the SQL "
- "query in order to have dynamic values depending on the user choice.\n"
- "The format of the parameters in the SQL query must be like this :"
- " %(parameter_field_name)s. \n"
- "Example : from the variable menu, create an variable with type 'char', "
- "having field name 'x_name' and field label : 'Name' \n"
- "Then, you can create a SQL query like this : "
- "SELECT * FROM res_partner WHERE name = %(x_name)s the variable "
- "can be used in any number of different SQL queries. \n"
- "In the SQL query, you can also include these 2 special parameters "
- "%(user_id)s and %(company_id)s which will be replaced respectively by "
- "the user executing the query and the company of the user executing the"
- " query.",
- )
+ use_properties = fields.Boolean(compute="_compute_use_properties")
+ query_properties_definition = fields.PropertiesDefinition("Query Properties")
encoding = fields.Selection(
[
@@ -58,11 +37,27 @@ class SqlExport(models.Model):
default="utf-8",
)
+ def _compute_use_properties(self):
+ for rec in self:
+ rec.use_properties = bool(rec.query_properties_definition)
+
+ def configure_properties(self):
+ # we need a full window in order for property configuration to work, not a modal
+ wiz = self.env["sql.file.wizard"].create({"sql_export_id": self.id})
+ return {
+ "view_mode": "form",
+ "res_model": "sql.file.wizard",
+ "res_id": wiz.id,
+ "type": "ir.actions.act_window",
+ "context": self.env.context,
+ "nodestroy": True,
+ }
+
def export_sql_query(self):
self.ensure_one()
wiz = self.env["sql.file.wizard"].create({"sql_export_id": self.id})
# no variable input, we can return the file directly
- if not self.field_ids:
+ if not self.query_properties_definition:
return wiz.export_sql()
else:
return {
@@ -89,3 +84,10 @@ def csv_get_data_from_query(self, variable_dict):
if self.encoding:
res = res.decode(self.encoding)
return res
+
+ def _check_execution(self):
+ self.ensure_one()
+ # only check execution if query does not contains variable
+ if self.query_properties_definition:
+ return True
+ return super()._check_execution()
diff --git a/sql_export/readme/DESCRIPTION.rst b/sql_export/readme/DESCRIPTION.rst
index d739c787cb..f1bbef659a 100644
--- a/sql_export/readme/DESCRIPTION.rst
+++ b/sql_export/readme/DESCRIPTION.rst
@@ -1,4 +1,4 @@
Allow to export data in csv files FROM sql requests.
There are some restrictions in the sql query, you can only read datas.
No update, deletion or creation are possible.
-A new menu named Export is created.
+A new sub menu named Sql Export is available in the Dashboard main menu.
diff --git a/sql_export/readme/ROADMAP.rst b/sql_export/readme/ROADMAP.rst
index 073881de5a..22dfa4c8bf 100644
--- a/sql_export/readme/ROADMAP.rst
+++ b/sql_export/readme/ROADMAP.rst
@@ -14,5 +14,3 @@ See sql_request_abstract module to fix this issue.
* checking SQL request by execution and rollback is disabled in this module
since variables features has been introduced. This can be fixed by
overloading _prepare_request_check_execution() function.
-
-* V16 : Restore export with parameters features.
diff --git a/sql_export/readme/USAGE.rst b/sql_export/readme/USAGE.rst
index 33178ae361..118be52016 100644
--- a/sql_export/readme/USAGE.rst
+++ b/sql_export/readme/USAGE.rst
@@ -6,5 +6,4 @@ Dashboards > Sql Export
- `%(company_id)s` allows to set in the query the company id of the user
- `%(user_id)s` allows to set in the query the user id
-- for any created field with `Sql Export Variables` menu, you can use it with `%(x_field_example)s` syntax
- (Limitation for relational fields)
+- for any created property, you can use it with `%(Property String)s` syntax
diff --git a/sql_export/static/src/scss/modal_properties.scss b/sql_export/static/src/scss/modal_properties.scss
new file mode 100644
index 0000000000..282d61aa54
--- /dev/null
+++ b/sql_export/static/src/scss/modal_properties.scss
@@ -0,0 +1,3 @@
+.modal-body .o_field_property_add {
+ display: none;
+}
diff --git a/sql_export/tests/test_sql_query.py b/sql_export/tests/test_sql_query.py
index 1452bef96b..c970a38255 100644
--- a/sql_export/tests/test_sql_query.py
+++ b/sql_export/tests/test_sql_query.py
@@ -4,7 +4,6 @@
import base64
-from odoo import fields
from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase, tagged
@@ -61,15 +60,42 @@ def test_authorized_queries(self):
sql_export.state, "sql_valid", "%s is a valid request" % (query)
)
- def _test_sql_query_with_params(self):
+ def test_sql_query_with_params(self):
query = self.env.ref("sql_export.sql_export_partner_with_variables")
+ query.write({"state": "sql_valid"})
categ_id = self.env.ref("base.res_partner_category_0").id
wizard = self.wizard_obj.create(
{
"sql_export_id": query.id,
- "x_date": fields.Date.today(),
- "x_id": 1,
- "x_partner_categ_ids": [(6, 0, [categ_id])],
+ }
+ )
+ wizard.write(
+ {
+ "query_properties": [
+ {
+ "name": "630eca383bc142e6",
+ "string": "Date",
+ "type": "date",
+ "default": "",
+ "value": "2023-02-03",
+ },
+ {
+ "name": "ec0556e22932334b",
+ "string": "Categories",
+ "type": "many2many",
+ "default": False,
+ "comodel": "res.partner.category",
+ "domain": False,
+ "value": [[categ_id, "Consulting Services"]],
+ },
+ {
+ "name": "907ac618eccbab74",
+ "string": "ID",
+ "type": "integer",
+ "default": False,
+ "value": 1,
+ },
+ ]
}
)
wizard.export_sql()
diff --git a/sql_export/views/ir_model_fields_view.xml b/sql_export/views/ir_model_fields_view.xml
deleted file mode 100644
index d4a1efba43..0000000000
--- a/sql_export/views/ir_model_fields_view.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
- Sql_parameter_form_view
- ir.model.fields
- 150
-
-
-
-
-
-
- Sql_parameter_tree_view
- ir.model.fields
- 150
-
-
-
-
-
-
-
-
-
-
-
- SQL Parameter
- ir.model.fields
- tree,form
-
- [('model','=','sql.file.wizard'), ('state', '=', 'manual')]
-
-
-
-
- tree
-
-
-
-
-
-
- form
-
-
-
-
-
-
-
diff --git a/sql_export/views/sql_export_view.xml b/sql_export/views/sql_export_view.xml
index 60ba3bbcba..8965843d10 100644
--- a/sql_export/views/sql_export_view.xml
+++ b/sql_export/views/sql_export_view.xml
@@ -9,12 +9,15 @@
/>
primary
+
+
+
-
+
@@ -41,19 +52,14 @@
/>
-
-
-
+
In case of use of properties in the query, use this syntax : %%(Property String)s.
-
-
+ Example : SELECT id FROM sale_order WHERE create_date > %%(Start Date)s
+
diff --git a/sql_export/wizard/wizard_file.py b/sql_export/wizard/wizard_file.py
index ec938b8bac..aa492c012f 100644
--- a/sql_export/wizard/wizard_file.py
+++ b/sql_export/wizard/wizard_file.py
@@ -2,12 +2,9 @@
# @author: Florian da Costa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-import json
from datetime import datetime
-from lxml import etree
-
-from odoo import _, api, fields, models
+from odoo import fields, models
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
@@ -18,54 +15,11 @@ class SqlFileWizard(models.TransientModel):
binary_file = fields.Binary("File", readonly=True)
file_name = fields.Char(readonly=True)
sql_export_id = fields.Many2one(comodel_name="sql.export", required=True)
-
- @api.model
- def get_view(self, view_id=None, view_type="form", **options):
- export_obj = self.env["sql.export"]
- sql_export = export_obj.browse(self.env.context.get("active_id"))
-
- result = super().get_view(view_id=view_id, view_type=view_type, **options)
-
- if sql_export.field_ids:
- etree.fromstring(result["arch"])
- raise NotImplementedError(
- _("The export with parameters is not implemented in V16")
- )
- return result
-
- @api.model
- def fields_view_get(
- self, view_id=None, view_type="form", toolbar=False, submenu=False
- ):
- """
- Display dynamically parameter fields depending on the sql_export.
- """
- res = super(SqlFileWizard, self).fields_view_get(
- view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
- )
- export_obj = self.env["sql.export"]
- if view_type == "form":
- sql_export = export_obj.browse(self.env.context.get("active_id"))
- if sql_export.field_ids:
- eview = etree.fromstring(res["arch"])
- group = etree.Element("group", name="variables_group", colspan="4")
- toupdate_fields = []
- for field in sql_export.field_ids:
- toupdate_fields.append(field.name)
- attrib = {"name": field.name, "required": "0", "readonly": "0"}
- view_field = etree.SubElement(group, "field", attrib=attrib)
- modifiers = json.loads(view_field.get("modifiers", "{}"))
- if field.required:
- modifiers["required"] = True
- view_field.set("modifiers", json.dumps(modifiers))
-
- res["fields"].update(self.fields_get(toupdate_fields))
- placeholder = eview.xpath(
- "//separator[@string='variables_placeholder']"
- )[0]
- placeholder.getparent().replace(placeholder, group)
- res["arch"] = etree.tostring(eview, pretty_print=True)
- return res
+ query_properties = fields.Properties(
+ string="Properties",
+ definition="sql_export_id.query_properties_definition",
+ copy=False,
+ )
def export_sql(self):
self.ensure_one()
@@ -75,14 +29,11 @@ def export_sql(self):
variable_dict = {}
now_tz = fields.Datetime.context_timestamp(sql_export, datetime.now())
date = now_tz.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
- if sql_export.field_ids:
- for field in sql_export.field_ids:
- if field.ttype == "many2one":
- variable_dict[field.name] = self[field.name].id
- elif field.ttype == "many2many":
- variable_dict[field.name] = tuple(self[field.name].ids)
- else:
- variable_dict[field.name] = self[field.name]
+ for prop in self.query_properties:
+ if prop["type"] == "many2many" and prop["value"]:
+ variable_dict[prop["string"]] = tuple(prop["value"])
+ else:
+ variable_dict[prop["string"]] = prop["value"]
if "%(company_id)s" in sql_export.query:
company_id = self.env.company.id
variable_dict["company_id"] = company_id
diff --git a/sql_export/wizard/wizard_file_view.xml b/sql_export/wizard/wizard_file_view.xml
index 8ac944fbe3..ccf883215f 100644
--- a/sql_export/wizard/wizard_file_view.xml
+++ b/sql_export/wizard/wizard_file_view.xml
@@ -5,10 +5,11 @@
sql.file.wizard