[IMP] Add back the variable feature in sql_export
Refactore to use the new properties fieldpull/718/head
parent
ef73f858f9
commit
741b22c989
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
Copyright (C) 2017 - Today: GRAP (http://www.grap.coop)
|
||||
@author Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<record id="date_field_variable_sql" model="ir.model.fields">
|
||||
<field name="name">x_date</field>
|
||||
<field name="field_description">Date</field>
|
||||
<field name="ttype">date</field>
|
||||
<field name="model_id" ref="sql_export.model_sql_file_wizard" />
|
||||
<field name="model">sql.file.wizard</field>
|
||||
<field name="state">manual</field>
|
||||
</record>
|
||||
|
||||
<record id="integer_field_variable_sql" model="ir.model.fields">
|
||||
<field name="name">x_id</field>
|
||||
<field name="field_description">x_ID</field>
|
||||
<field name="ttype">integer</field>
|
||||
<field name="model_id" ref="sql_export.model_sql_file_wizard" />
|
||||
<field name="model">sql.file.wizard</field>
|
||||
<field name="state">manual</field>
|
||||
</record>
|
||||
|
||||
<record id="m2m_field_variable_sql" model="ir.model.fields">
|
||||
<field name="name">x_partner_categ_ids</field>
|
||||
<field name="field_description">Partner Categories</field>
|
||||
<field name="ttype">many2many</field>
|
||||
<field name="model_id" ref="sql_export.model_sql_file_wizard" />
|
||||
<field name="model">sql.file.wizard</field>
|
||||
<field name="state">manual</field>
|
||||
<field name="relation">res.partner.category</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
|
@ -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
|
||||
]]>
|
||||
</field>
|
||||
<field
|
||||
eval="[(6, 0, [ref('date_field_variable_sql'), ref('integer_field_variable_sql'), ref('m2m_field_variable_sql')])]"
|
||||
name="field_ids"
|
||||
name="query_properties_definition"
|
||||
eval="[{'name': '630eca383bc142e6', 'type': 'date', 'string': 'Date'}, {'name': '907ac618eccbab74', 'type': 'integer', 'string': 'ID'}, {'name': 'ec0556e22932334b', 'string': 'Categories', 'type': 'many2many', 'default': False, 'comodel': 'res.partner.category', 'domain': False}]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -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 @@ class SqlExport(models.Model):
|
|||
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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.modal-body .o_field_property_add {
|
||||
display: none;
|
||||
}
|
|
@ -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 @@ class TestExportSqlQuery(TransactionCase):
|
|||
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()
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="sql_parameter_view_form" model="ir.ui.view">
|
||||
<field name="name">Sql_parameter_form_view</field>
|
||||
<field name="model">ir.model.fields</field>
|
||||
<field name="priority">150</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="SQL export">
|
||||
<group>
|
||||
<field name="name" />
|
||||
<field name="field_description" />
|
||||
<field name="ttype" />
|
||||
<field
|
||||
name="relation"
|
||||
attrs="{'invisible': [('ttype', 'not in', ('many2one', 'many2many', 'one2many'))], 'required': [('ttype', 'in', ('many2one', 'many2many', 'one2many'))]}"
|
||||
/>
|
||||
<field name="model_id" readonly="1" />
|
||||
<field name="model" invisible="1" />
|
||||
<field name="required" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sql_parameter_view_tree" model="ir.ui.view">
|
||||
<field name="name">Sql_parameter_tree_view</field>
|
||||
<field name="model">ir.model.fields</field>
|
||||
<field name="priority">150</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name" />
|
||||
<field name="field_description" />
|
||||
<field name="ttype" />
|
||||
<field name="required" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sql_parameter_action" model="ir.actions.act_window">
|
||||
<field name="name">SQL Parameter</field>
|
||||
<field name="res_model">ir.model.fields</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field
|
||||
name="context"
|
||||
eval="{'default_model_id': ref('sql_export.model_sql_file_wizard'), 'default_size': 64, 'search_default_state': 'manual', 'default_model': 'sql.file.wizard'}"
|
||||
/>
|
||||
<field
|
||||
name="domain"
|
||||
>[('model','=','sql.file.wizard'), ('state', '=', 'manual')]</field>
|
||||
</record>
|
||||
|
||||
<record id="sql_parameter_action_view_tree" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1" />
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="sql_parameter_view_tree" />
|
||||
<field name="act_window_id" ref="sql_parameter_action" />
|
||||
</record>
|
||||
|
||||
<record id="sql_parameter_action_view_form" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="2" />
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="sql_parameter_view_form" />
|
||||
<field name="act_window_id" ref="sql_parameter_action" />
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="sql_parameter_menu_view"
|
||||
name="SQL Export Variables"
|
||||
parent="spreadsheet_dashboard.spreadsheet_dashboard_menu_configuration"
|
||||
action="sql_parameter_action"
|
||||
sequence="50"
|
||||
groups="sql_request_abstract.group_sql_request_manager"
|
||||
/>
|
||||
|
||||
</odoo>
|
|
@ -9,12 +9,15 @@
|
|||
/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="query" position="after">
|
||||
<field name="use_properties" invisible="1" />
|
||||
</field>
|
||||
<button name="button_preview_sql_expression" position="attributes">
|
||||
<attribute name="states" />
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'invisible': [('field_ids', '!=', False)]}</attribute>
|
||||
>{'invisible': [('use_properties', '=', True)]}</attribute>
|
||||
</button>
|
||||
|
||||
<xpath expr="//header" position="inside">
|
||||
<button
|
||||
name="export_sql_query"
|
||||
|
@ -24,6 +27,14 @@
|
|||
class="oe_highlight"
|
||||
icon="fa-arrow-right text-success"
|
||||
/>
|
||||
<button
|
||||
name="configure_properties"
|
||||
states="draft"
|
||||
string="Configure Properties"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
icon="fa-arrow-right text-success"
|
||||
/>
|
||||
</xpath>
|
||||
<group name="group_main_info" position="inside">
|
||||
<group name="option">
|
||||
|
@ -41,19 +52,14 @@
|
|||
/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="group_query" position="after">
|
||||
<group string="Parameters">
|
||||
<field
|
||||
name="field_ids"
|
||||
nolabel="1"
|
||||
colspan="2"
|
||||
options="{'no_create': True}"
|
||||
context="{'tree_view_ref': 'sql_export.sql_parameter_view_tree', 'form_view_ref': 'sql_export.sql_parameter_view_form'}"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
groups="sql_request_abstract.group_sql_request_user"
|
||||
<field name="query" position="before">
|
||||
<p
|
||||
colspan="2"
|
||||
attrs="{'invisible': [('use_properties', '=', False)]}"
|
||||
> In case of use of properties in the query, use this syntax : %%(Property String)s. <br
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
Example : SELECT id FROM sale_order WHERE create_date > %%(Start Date)s</p>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
|
@ -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 @@ class SqlFileWizard(models.TransientModel):
|
|||
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
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
<field name="model">sql.file.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Csv File">
|
||||
<separator
|
||||
string="variables_placeholder"
|
||||
colspan="4"
|
||||
invisible="1"
|
||||
<field
|
||||
name="query_properties"
|
||||
nolabel="1"
|
||||
columns="1"
|
||||
hideKanbanOption="1"
|
||||
/>
|
||||
<separator
|
||||
string="Export file"
|
||||
|
@ -17,6 +18,7 @@
|
|||
/>
|
||||
<field name="binary_file" filename="file_name" />
|
||||
<field name="file_name" invisible="1" />
|
||||
<field name="sql_export_id" invisible="1" />
|
||||
<footer>
|
||||
<button
|
||||
name="export_sql"
|
||||
|
|
|
@ -113,10 +113,10 @@ class SqlExport(models.Model):
|
|||
else:
|
||||
export.send_mail()
|
||||
|
||||
@api.constrains("field_ids", "mail_user_ids")
|
||||
@api.constrains("query_properties_definition", "mail_user_ids")
|
||||
def check_no_parameter_if_sent_by_mail(self):
|
||||
for export in self:
|
||||
if export.field_ids and export.mail_user_ids:
|
||||
if export.query_properties_definition and export.mail_user_ids:
|
||||
raise UserError(
|
||||
_(
|
||||
"It is not possible to execute and send a query "
|
||||
|
|
Loading…
Reference in New Issue