From 8c33a4a34ccc2db790912f5e153b3b9f00b7c38c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Duy=20=28=C4=90=E1=BB=97=20Anh=29?=
Date: Wed, 19 Feb 2025 16:25:56 +0700
Subject: [PATCH] [MIG] excel_import_export: Migration to 18.0
---
excel_import_export/README.rst | 25 ++--
excel_import_export/__manifest__.py | 2 +-
excel_import_export/controllers/report.py | 9 +-
excel_import_export/models/__init__.py | 14 ---
excel_import_export/models/common.py | 33 ++---
excel_import_export/models/ir_report.py | 23 ++--
excel_import_export/models/xlsx_export.py | 24 ++--
excel_import_export/models/xlsx_import.py | 27 ++--
excel_import_export/models/xlsx_report.py | 7 +-
excel_import_export/models/xlsx_template.py | 63 +++++-----
excel_import_export/readme/CONTRIBUTORS.md | 1 +
excel_import_export/readme/USAGE.md | 20 +--
.../security/ir.model.access.csv | 6 +-
.../static/description/index.html | 25 ++--
.../js/report/action_manager_report.esm.js | 11 +-
excel_import_export/views/xlsx_report.xml | 20 ++-
.../views/xlsx_template_view.xml | 115 ++++++++----------
.../wizard/export_xlsx_wizard.py | 18 ++-
.../wizard/export_xlsx_wizard.xml | 40 +++---
.../wizard/import_xlsx_wizard.py | 71 ++++++-----
.../wizard/import_xlsx_wizard.xml | 35 ++----
.../wizard/report_xlsx_wizard.py | 5 +-
.../wizard/report_xlsx_wizard.xml | 2 +-
23 files changed, 295 insertions(+), 301 deletions(-)
diff --git a/excel_import_export/README.rst b/excel_import_export/README.rst
index ab61bd4c19e..5ee079ebf75 100644
--- a/excel_import_export/README.rst
+++ b/excel_import_export/README.rst
@@ -81,8 +81,8 @@ For reporting, also call export_xlsx(...) but through following method
- ``self.env['xslx.report'].report_xlsx(...)``
-After install this module, go to Settings > Excel Import/Export > XLSX
-Templates, this is where the key component located.
+After install this module, go to Settings > Technical > Excel
+Import/Export > XLSX Templates, this is where the key component located.
As this module provide tools, it is best to explain as use cases. For
example use cases, please install **excel_import_export_demo**
@@ -145,13 +145,15 @@ Another option for reporting is to use report action
.. code:: xml
-
+
+ Quotation / Order (.xlsx)
+ ir.model
+ 'sale.order'
+ 'sale.order'
+
+ report
+ excel
+
By using report action, Odoo will find template using combination of
model and name, then do the export for the underlining record. Please
@@ -168,8 +170,8 @@ But instead of having to write XML / Python code like normally do, this
option allow user to create a report based on a model or view, all by
configuration only.
-1. Goto > Technical> Excel Import/Export > XLSX Templates, and create a
- new template for a report.
+1. Go to Settings > Technical> Excel Import/Export > XLSX Templates, and
+ create a new template for a report.
2. On the new template, select "Easy Reporting" option, then select
followings
@@ -223,6 +225,7 @@ Contributors
- Kitti Upariphutthiphong. (http://ecosoft.co.th)
- Saran Lim. (http://ecosoft.co.th)
+- Do Anh Duy
Maintainers
-----------
diff --git a/excel_import_export/__manifest__.py b/excel_import_export/__manifest__.py
index 0c6170048dc..afcdfe450dc 100644
--- a/excel_import_export/__manifest__.py
+++ b/excel_import_export/__manifest__.py
@@ -4,7 +4,7 @@
{
"name": "Excel Import/Export/Report",
"summary": "Base module for developing Excel import/export/report",
- "version": "16.0.1.3.1",
+ "version": "18.0.1.0.0",
"author": "Ecosoft,Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/server-tools",
diff --git a/excel_import_export/controllers/report.py b/excel_import_export/controllers/report.py
index e779247b348..edd59530fa4 100644
--- a/excel_import_export/controllers/report.py
+++ b/excel_import_export/controllers/report.py
@@ -12,19 +12,19 @@
from odoo.tools import html_escape
from odoo.tools.safe_eval import safe_eval, time
-from odoo.addons.web.controllers import report
+from odoo.addons.web.controllers.report import ReportController
_logger = logging.getLogger(__name__)
-class ReportController(report.ReportController):
+class ReportExcelController(ReportController):
@route()
def report_routes(self, reportname, docids=None, converter=None, **data):
if converter == "excel":
report = request.env["ir.actions.report"]._get_report_from_name(reportname)
context = dict(request.env.context)
if docids:
- docids = [int(i) for i in docids.split(",")]
+ docids = [int(i) for i in docids.split(",") if i.isdigit()]
if data.get("options"):
data.update(json.loads(data.pop("options")))
if data.get("context"):
@@ -32,10 +32,7 @@ def report_routes(self, reportname, docids=None, converter=None, **data):
# from the webclient *but* if the user explicitely wants to
# change the lang, this mechanism overwrites it.
data["context"] = json.loads(data["context"])
- if data["context"].get("lang"):
- del data["context"]["lang"]
context.update(data["context"])
-
excel, report_name = report.with_context(**context)._render_excel(
docids, data=data
)
diff --git a/excel_import_export/models/__init__.py b/excel_import_export/models/__init__.py
index 04a47be5979..52501c936b5 100644
--- a/excel_import_export/models/__init__.py
+++ b/excel_import_export/models/__init__.py
@@ -7,17 +7,3 @@
from . import xlsx_template
from . import xlsx_report
from . import ir_report
-
-#
-#
-# INSERT INTO "purchase_order_line" (
-# "id", "create_uid", "create_date",
-# "write_uid", "write_date", "date_planned",
-# "display_type", "name", "order_id",
-# "price_unit", "product_qty", "product_uom",
-# "sequence") VALUES (
-# nextval('purchase_order_line_id_seq'), 2, (now() at time zone 'UTC'),
-# 2, (now() at time zone 'UTC'), '2020-10-05 09:39:28',
-# NULL, '[FURN_0269] Office Chair Black', 8,
-# '11111.00', '5.000', 1,
-# 10)
diff --git a/excel_import_export/models/common.py b/excel_import_export/models/common.py
index b18eb6552f0..e5462cf8504 100644
--- a/excel_import_export/models/common.py
+++ b/excel_import_export/models/common.py
@@ -27,7 +27,7 @@ def adjust_cell_formula(value, k):
val = value[i + 2 : j]
col, row = split_row_col(val)
new_val = f"{col}{row + k}"
- value = value.replace("?(%s)" % val, new_val)
+ value = value.replace(f"?({val})", new_val)
return value
@@ -41,7 +41,7 @@ def get_field_aggregation(field):
if cond or cond == "":
return (field[:i], cond)
except Exception:
- return (field.replace("@{%s}" % cond, ""), False)
+ return (field.replace(f"@{{{cond}}}", ""), False)
return (field, False)
@@ -53,7 +53,7 @@ def get_field_condition(field):
cond = field[i + 2 : j]
try:
if cond or cond == "":
- return (field.replace("${%s}" % cond, ""), cond)
+ return (field.replace(f"${{{cond}}}", ""), cond)
except Exception:
return (field, False)
return (field, False)
@@ -74,7 +74,7 @@ def get_field_style(field):
cond = field[i + 2 : j]
try:
if cond or cond == "":
- return (field.replace("#{%s}" % cond, ""), cond)
+ return (field.replace(f"#{{{cond}}}", ""), cond)
except Exception:
return (field, False)
return (field, False)
@@ -88,7 +88,7 @@ def get_field_style_cond(field):
cond = field[i + 2 : j]
try:
if cond or cond == "":
- return (field.replace("#?%s?" % cond, ""), cond)
+ return (field.replace(f"#?{cond}?", ""), cond)
except Exception:
return (field, False)
return (field, False)
@@ -99,11 +99,14 @@ def fill_cell_style(field, field_style, styles):
for f in field_styles:
(key, value) = f.split("=")
if key not in styles.keys():
- raise ValidationError(_("Invalid style type %s") % key)
+ raise ValidationError(_("Invalid style type %s", key))
if value.lower() not in styles[key].keys():
raise ValidationError(
- _("Invalid value %(value)s for style type %(key)s")
- % {"value": value, "key": key}
+ _(
+ "Invalid value %(value)s for style type %(key)s",
+ value=value,
+ key=key,
+ )
)
cell_style = styles[key][value]
if key == "font":
@@ -151,7 +154,7 @@ def get_groupby(line_field):
def split_row_col(pos):
match = re.match(r"([a-z]+)([0-9]+)", pos, re.I)
if not match:
- raise ValidationError(_("Position %s is not valid") % pos)
+ raise ValidationError(_("Position %s is not valid", pos))
col, row = match.groups()
return col, int(row)
@@ -163,7 +166,7 @@ def openpyxl_get_sheet_by_name(book, name):
if sheetname == name:
return book.worksheets[i]
i += 1
- raise ValidationError(_("'%s' sheet not found") % (name,))
+ raise ValidationError(_("'%s' sheet not found", name))
def xlrd_get_sheet_by_name(book, name):
@@ -173,7 +176,7 @@ def xlrd_get_sheet_by_name(book, name):
if sheet.name == name:
return sheet
except IndexError as exc:
- raise ValidationError(_("'%s' sheet not found") % (name,)) from exc
+ raise ValidationError(_("'%s' sheet not found", name)) from exc
def isfloat(input_val):
@@ -236,9 +239,9 @@ def csv_from_excel(excel_content, delimiter, quote):
raise ValidationError(
_(
"Template with CSV Quoting = False, data must not "
- 'contain the same char as delimiter -> "%s"'
+ 'contain the same char as delimiter -> "%s"',
+ delimiter,
)
- % delimiter
)
row.append(x)
wr.writerow(row)
@@ -250,7 +253,7 @@ def csv_from_excel(excel_content, delimiter, quote):
def pos2idx(pos):
match = re.match(r"([a-z]+)([0-9]+)", pos, re.I)
if not match:
- raise ValidationError(_("Position %s is not valid") % (pos,))
+ raise ValidationError(_("Position %s is not valid", pos))
col, row = match.groups()
col_num = 0
for c in col:
@@ -290,7 +293,7 @@ def _get_cell_value(cell, field_type=False):
value = value_str
elif field_type in ["many2one"]:
# If number, change to string
- if isinstance(cell.value, (int, float, complex)):
+ if isinstance(cell.value, int | float | complex):
value = str(cell.value)
else:
value = cell.value
diff --git a/excel_import_export/models/ir_report.py b/excel_import_export/models/ir_report.py
index 68e8029e8ea..40da6e5fcb2 100644
--- a/excel_import_export/models/ir_report.py
+++ b/excel_import_export/models/ir_report.py
@@ -1,7 +1,7 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import _, api, fields, models
+from odoo import api, fields, models
from odoo.exceptions import UserError
@@ -15,17 +15,21 @@ class ReportAction(models.Model):
@api.model
def _render_excel(self, docids, data):
if len(docids) != 1:
- raise UserError(_("Only one id is allowed for excel_import_export"))
+ raise UserError(
+ self.env._("Only one id is allowed for excel_import_export")
+ )
xlsx_template = self.env["xlsx.template"].search(
[("fname", "=", self.report_name), ("res_model", "=", self.model)]
)
if not xlsx_template or len(xlsx_template) != 1:
raise UserError(
- _("Template %(report_name)s on model %(model)s is not unique!")
- % {"report_name": self.report_name, "model": self.model}
+ self.env._(
+ "Template %(report_name)s on model %(model)s is not unique!",
+ report_name=self.report_name,
+ model=self.model,
+ )
)
- Export = self.env["xlsx.export"]
- return Export.export_xlsx(xlsx_template, self.model, docids[0])
+ return self.env["xlsx.export"].export_xlsx(xlsx_template, self.model, docids[0])
@api.model
def _get_report_from_name(self, report_name):
@@ -33,10 +37,9 @@ def _get_report_from_name(self, report_name):
if res:
return res
report_obj = self.env["ir.actions.report"]
- qwebtypes = ["excel"]
- conditions = [
- ("report_type", "in", qwebtypes),
+ domain = [
+ ("report_type", "=", "excel"),
("report_name", "=", report_name),
]
context = self.env["res.users"].context_get()
- return report_obj.with_context(**context).search(conditions, limit=1)
+ return report_obj.with_context(**context).search(domain, limit=1)
diff --git a/excel_import_export/models/xlsx_export.py b/excel_import_export/models/xlsx_export.py
index 8a1fb9ed77c..3cbe9a3030e 100644
--- a/excel_import_export/models/xlsx_export.py
+++ b/excel_import_export/models/xlsx_export.py
@@ -9,7 +9,7 @@
from datetime import datetime as dt
from io import BytesIO
-from odoo import _, api, fields, models
+from odoo import api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools.float_utils import float_compare
from odoo.tools.safe_eval import safe_eval
@@ -79,7 +79,9 @@ def _get_line_vals(self, record, line_field, fields):
line_field = line_field.replace("_EXTEND_", "") # Remove _EXTEND_ if any
lines = record[line_field]
if max_row > 0 and len(lines) > max_row:
- raise Exception(_("Records in %s exceed max records allowed") % line_field)
+ raise Exception(
+ self.env._("Records in %s exceed max records allowed", line_field)
+ )
vals = {field: [] for field in fields} # value and do_style
# Get field condition & aggre function
conditions_dict = self._get_conditions_dict()
@@ -123,7 +125,7 @@ def _eval_style_cond(self, model, record, value, style_cond):
i += 1
field, style = co.get_field_style(field)
styles.update({i: style})
- style_cond = style_cond.replace("#{%s}" % style, str(i))
+ style_cond = style_cond.replace(f"#{{{style}}}", str(i))
if not styles:
return False
res = safe_eval(style_cond, eval_context)
@@ -144,26 +146,26 @@ def _fill_workbook_data(self, workbook, record, data_dict):
st = co.openpyxl_get_sheet_by_name(workbook, sheet_name)
elif isinstance(sheet_name, int):
if sheet_name > len(workbook.worksheets):
- raise Exception(_("Not enough worksheets"))
+ raise Exception(self.env._("Not enough worksheets"))
st = workbook.worksheets[sheet_name - 1]
if not st:
- raise ValidationError(_("Sheet %s not found") % sheet_name)
+ raise ValidationError(self.env._("Sheet %s not found", sheet_name))
# Fill data, header and rows
self._fill_head(ws, st, record)
self._fill_lines(ws, st, record)
except KeyError as e:
- raise ValidationError(_("Key Error\n%s") % e) from e
+ raise ValidationError(self.env._("Key Error\n%s", e)) from e
except IllegalCharacterError as e:
raise ValidationError(
- _(
+ self.env._(
"IllegalCharacterError\n"
- "Some exporting data contain special character\n%s"
+ "Some exporting data contain special character\n%s",
+ e,
)
- % e
) from e
except Exception as e:
raise ValidationError(
- _("Error filling data into Excel sheets\n%s") % e
+ self.env._("Error filling data into Excel sheets\n%s", e)
) from e
@api.model
@@ -251,7 +253,7 @@ def _fill_lines(self, ws, st, record):
@api.model
def export_xlsx(self, template, res_model, res_ids):
if template.res_model != res_model:
- raise ValidationError(_("Template's model mismatch"))
+ raise ValidationError(self.env._("Template's model mismatch"))
data_dict = co.literal_eval(template.instruction.strip())
export_dict = data_dict.get("__EXPORT__", False)
out_name = template.name
diff --git a/excel_import_export/models/xlsx_import.py b/excel_import_export/models/xlsx_import.py
index 829edc7c9db..f1d1333f1d0 100644
--- a/excel_import_export/models/xlsx_import.py
+++ b/excel_import_export/models/xlsx_import.py
@@ -11,7 +11,7 @@
import xlrd
import xlwt
-from odoo import _, api, models
+from odoo import api, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_compare
from odoo.tools.safe_eval import safe_eval
@@ -71,7 +71,7 @@ def _get_field_type(self, model, field):
return field_type
except Exception as exc:
raise ValidationError(
- _("Invalid declaration, %s has no valid field type") % field
+ self.env._("Invalid declaration, %s has no valid field type", field)
) from exc
@api.model
@@ -94,7 +94,7 @@ def _delete_record_data(self, record, data_dict):
new_fv = data_dict[s].pop(f)
data_dict[s][f.replace("_NODEL_", "")] = new_fv
except Exception as e:
- raise ValidationError(_("Error deleting data\n%s") % e) from e
+ raise ValidationError(self.env._("Error deleting data\n%s", e)) from e
@api.model
def _get_end_row(self, st, worksheet, line_field):
@@ -112,12 +112,13 @@ def _get_end_row(self, st, worksheet, line_field):
cell_type = st.cell_type(idx, col) # empty type = 0
except Exception as e:
raise UserError(
- _(
+ self.env._(
"The value for the '%(field)s' field is expected to be "
"in cell %(cell_position)s, but no column exists for that "
- "cell in the Excel sheet. Please check your Excel file."
+ "cell in the Excel sheet. Please check your Excel file.",
+ field=_col,
+ cell_position=rc,
)
- % {"field": _col, "cell_position": rc}
) from e
r_types = test_rows.get(idx, [])
r_types.append(cell_type)
@@ -166,7 +167,7 @@ def _process_worksheet(self, wb, out_wb, out_st, model, data_dict, header_fields
elif isinstance(sheet_name, int):
st = wb.sheet_by_index(sheet_name - 1)
if not st:
- raise ValidationError(_("Sheet %s not found") % sheet_name)
+ raise ValidationError(self.env._("Sheet %s not found", sheet_name))
# HEAD updates
for rc, field in worksheet.get("_HEAD_", {}).items():
rc, key_eval_cond = co.get_field_condition(rc)
@@ -256,7 +257,7 @@ def _import_record_data(self, import_file, record, data_dict):
},
)
if errors.get("messages"):
- message = _("Error importing data")
+ message = self.env._("Error importing data")
messages = errors["messages"]
if isinstance(messages, dict):
message = messages["message"]
@@ -266,7 +267,7 @@ def _import_record_data(self, import_file, record, data_dict):
return self.env.ref(xml_id)
except xlrd.XLRDError as exc:
raise ValidationError(
- _("Invalid file style, only .xls or .xlsx file allowed")
+ self.env._("Invalid file style, only .xls or .xlsx file allowed")
) from exc
except Exception as e:
raise e
@@ -282,7 +283,9 @@ def _post_import_operation(self, record, operation):
eval_context = {"object": record}
safe_eval(code, eval_context)
except Exception as e:
- raise ValidationError(_("Post import operation error\n%s") % e) from e
+ raise ValidationError(
+ self.env._("Post import operation error\n%s", e)
+ ) from e
@api.model
def import_xlsx(self, import_file, template, res_model=False, res_id=False):
@@ -292,12 +295,12 @@ def import_xlsx(self, import_file, template, res_model=False, res_id=False):
- Import data from excel according to data_dict['__IMPORT__']
"""
if res_model and template.res_model != res_model:
- raise ValidationError(_("Template's model mismatch"))
+ raise ValidationError(self.env._("Template's model mismatch"))
record = self.env[template.res_model].browse(res_id)
data_dict = literal_eval(template.instruction.strip())
if not data_dict.get("__IMPORT__"):
raise ValidationError(
- _("No data_dict['__IMPORT__'] in template %s") % template.name
+ self.env._("No data_dict['__IMPORT__'] in template %s", template.name)
)
if record:
# Delete existing data first
diff --git a/excel_import_export/models/xlsx_report.py b/excel_import_export/models/xlsx_report.py
index f7e5be04304..0c1bf9a4081 100644
--- a/excel_import_export/models/xlsx_report.py
+++ b/excel_import_export/models/xlsx_report.py
@@ -1,7 +1,7 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import _, api, fields, models
+from odoo import api, fields, models
from odoo.exceptions import ValidationError
@@ -33,11 +33,11 @@ def default_get(self, fields):
template_domain = self._context.get("template_domain", [])
templates = self.env["xlsx.template"].search(template_domain)
if not templates:
- raise ValidationError(_("No template found"))
+ raise ValidationError(self.env._("No template found"))
defaults = super().default_get(fields)
for template in templates:
if not template.datas:
- raise ValidationError(_("No file in %s") % (template.name,))
+ raise ValidationError(self.env._("No file in %s", template.name))
defaults["template_id"] = len(templates) == 1 and templates.id or False
return defaults
@@ -47,6 +47,7 @@ def report_xlsx(self):
out_file, out_name = Export.export_xlsx(self.template_id, self._name, self.id)
self.write({"state": "get", "data": out_file, "name": out_name})
return {
+ "name": self.env._("Excel Report"),
"type": "ir.actions.act_window",
"res_model": self._name,
"view_mode": "form",
diff --git a/excel_import_export/models/xlsx_template.py b/excel_import_export/models/xlsx_template.py
index f22439f33eb..934b3e64299 100644
--- a/excel_import_export/models/xlsx_template.py
+++ b/excel_import_export/models/xlsx_template.py
@@ -6,7 +6,7 @@
from ast import literal_eval
from os.path import join as opj
-from odoo import _, api, fields, models
+from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.modules.module import get_module_path
@@ -125,9 +125,7 @@ class XLSXTemplate(models.Model):
def _compute_result_field(self):
for rec in self:
- rec.result_field = (
- ("x_%s_results" % rec.id) if rec.result_model_id else False
- )
+ rec.result_field = (f"x_{rec.id}_results") if rec.result_model_id else False
@api.constrains("redirect_action", "res_model")
def _check_action_model(self):
@@ -138,8 +136,10 @@ def _check_action_model(self):
and rec.res_model != rec.redirect_action.res_model
):
raise ValidationError(
- _("The selected redirect action is " "not for model %s")
- % rec.res_model
+ self.env._(
+ "The selected redirect action is " "not for model %s",
+ rec.res_model,
+ )
)
@api.model
@@ -196,6 +196,7 @@ def _update_result_field_common_wizard(self):
self.ensure_one()
_model = self.env["ir.model"].search([("model", "=", "report.xlsx.wizard")])
_model.ensure_one()
+ result_model = self.result_model_id.model
_field = self.env["ir.model.fields"].search(
[("model", "=", "report.xlsx.wizard"), ("name", "=", self.result_field)]
)
@@ -206,16 +207,18 @@ def _update_result_field_common_wizard(self):
"name": self.result_field,
"field_description": "Results",
"ttype": "many2many",
- "relation": self.result_model_id.model,
+ "relation": result_model,
"store": False,
"depends": "res_model",
}
)
else:
_field.ensure_one()
- _field.write({"relation": self.result_model_id.model})
+ _field.write({"relation": result_model})
_field.compute = f"""
-self['{self.result_field}'] = self.env['{self.result_model_id.model}'].search(self.safe_domain(self.domain))
+self[
+'{self.result_field}'
+] = self.env['{result_model}'].search(self.safe_domain(self.domain))
"""
def _update_result_export_ids(self):
@@ -387,9 +390,9 @@ def _compute_output_instruction(self):
if line.section_type in ("head", "row"):
row_field = line.row_field
if line.section_type == "row" and line.is_cont:
- row_field = "_CONT_%s" % row_field
+ row_field = f"_CONT_{row_field}"
if line.section_type == "row" and line.is_extend:
- row_field = "_EXTEND_%s" % row_field
+ row_field = f"_EXTEND_{row_field}"
row_dict = {row_field: {}}
inst_dict[itype][prev_sheet].update(row_dict)
prev_row = row_field
@@ -413,7 +416,7 @@ def _compute_output_instruction(self):
if line.section_type in ("head", "row"):
row_field = line.row_field
if line.section_type == "row" and line.no_delete:
- row_field = "_NODEL_%s" % row_field
+ row_field = f"_NODEL_{row_field}"
row_dict = {row_field: {}}
inst_dict[itype][prev_sheet].update(row_dict)
prev_row = row_field
@@ -450,12 +453,13 @@ def _create_export_action(self, model):
"binding_type": "action",
"target": "new",
"view_mode": "form",
- "context": """
- {'template_domain': [('res_model', '=', '%s'),
- ('export_action_id', '!=', False),
- ('gname', '=', False)]}
- """
- % (self.res_model),
+ "context": {
+ "template_domain": [
+ ("res_model", "=", self.res_model),
+ ("export_action_id", "!=", False),
+ ("gname", "=", False),
+ ]
+ },
}
return self.env["ir.actions.act_window"].create(vals)
@@ -490,12 +494,13 @@ def add_import_action(self):
"binding_type": "action",
"target": "new",
"view_mode": "form",
- "context": """
- {'template_domain': [('res_model', '=', '%s'),
- ('fname', '=', '%s'),
- ('gname', '=', False)]}
- """
- % (self.res_model, self.fname),
+ "context": {
+ "template_domain": [
+ ("res_model", "=", self.res_model),
+ ("fname", "=", self.fname),
+ ("gname", "=", False),
+ ],
+ },
}
action = self.env["ir.actions.act_window"].create(vals)
self.import_action_id = action
@@ -508,7 +513,7 @@ def remove_import_action(self):
def add_report_menu(self):
self.ensure_one()
if not self.fname:
- raise UserError(_("No file content!"))
+ raise UserError(self.env._("No file content!"))
# Create report action
vals = {
"name": self.name,
@@ -587,7 +592,7 @@ def create(self, vals_list):
def _extract_field_name(self, vals):
if self._context.get("compute_from_input") and vals.get("field_name"):
field_name, field_cond = co.get_field_condition(vals["field_name"])
- field_cond = field_cond and "${%s}" % (field_cond or "") or False
+ field_cond = field_cond and f"${{{field_cond or ''}}}" or False
vals.update({"field_name": field_name, "field_cond": field_cond})
return vals
@@ -643,9 +648,9 @@ def _extract_field_name(self, vals):
vals.update(
{
"field_name": field_name,
- "field_cond": "${%s}" % (field_cond or ""),
- "style": "#{%s}" % (style or ""),
- "style_cond": "#?%s?" % (style_cond or ""),
+ "field_cond": f"${{{field_cond or ''}}}",
+ "style": f"#{{{style or ''}}}",
+ "style_cond": f"#?{style_cond or ''}?",
"is_sum": func == "sum" and True or False,
}
)
diff --git a/excel_import_export/readme/CONTRIBUTORS.md b/excel_import_export/readme/CONTRIBUTORS.md
index 566b4bf9f87..f5c065bfe39 100644
--- a/excel_import_export/readme/CONTRIBUTORS.md
+++ b/excel_import_export/readme/CONTRIBUTORS.md
@@ -1,3 +1,4 @@
- Kitti Upariphutthiphong. \<\>
()
- Saran Lim. \<\> ()
+- Do Anh Duy \<\>
diff --git a/excel_import_export/readme/USAGE.md b/excel_import_export/readme/USAGE.md
index 47db3f0d736..34ffa3a1005 100644
--- a/excel_import_export/readme/USAGE.md
+++ b/excel_import_export/readme/USAGE.md
@@ -12,7 +12,7 @@ For reporting, also call export_xlsx(...) but through following method
- `self.env['xslx.report'].report_xlsx(...)`
-After install this module, go to Settings \> Excel Import/Export \> XLSX
+After install this module, go to Settings \> Technical \> Excel Import/Export \> XLSX
Templates, this is where the key component located.
As this module provide tools, it is best to explain as use cases. For
@@ -75,13 +75,15 @@ Another option for reporting is to use report action
(report_type='excel'), I.e.,
``` xml
-
+
+ Quotation / Order (.xlsx)
+ ir.model
+ 'sale.order'
+ 'sale.order'
+
+ report
+ excel
+
```
By using report action, Odoo will find template using combination of
@@ -98,7 +100,7 @@ But instead of having to write XML / Python code like normally do, this
option allow user to create a report based on a model or view, all by
configuration only.
-1. Goto \> Technical\> Excel Import/Export \> XLSX Templates, and
+1. Go to Settings \> Technical\> Excel Import/Export \> XLSX Templates, and
create a new template for a report.
2. On the new template, select "Easy Reporting" option, then select
followings
diff --git a/excel_import_export/security/ir.model.access.csv b/excel_import_export/security/ir.model.access.csv
index 5dad48a6941..c71433f5bc0 100644
--- a/excel_import_export/security/ir.model.access.csv
+++ b/excel_import_export/security/ir.model.access.csv
@@ -1,7 +1,7 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-xlsx_template_user,xlsx_template_user,model_xlsx_template,,1,1,1,1
-xlsx_template_export_user,xlsx_template_export_user,model_xlsx_template_export,,1,1,1,1
-xlsx_template_import_user,xlsx_template_import_user,model_xlsx_template_import,,1,1,1,1
+xlsx_template_user,xlsx_template_user,model_xlsx_template,base.group_user,1,1,1,1
+xlsx_template_export_user,xlsx_template_export_user,model_xlsx_template_export,base.group_user,1,1,1,1
+xlsx_template_import_user,xlsx_template_import_user,model_xlsx_template_import,base.group_user,1,1,1,1
access_export_xlsx_wizard,access_export_xlsx_wizard,model_export_xlsx_wizard,base.group_user,1,1,1,1
access_import_xlsx_wizard,access_import_xlsx_wizard,model_import_xlsx_wizard,base.group_user,1,1,1,1
access_report_xlsx_wizard,access_report_xlsx_wizard,model_report_xlsx_wizard,base.group_user,1,1,1,1
diff --git a/excel_import_export/static/description/index.html b/excel_import_export/static/description/index.html
index c67490b3e69..e500ba84cdd 100644
--- a/excel_import_export/static/description/index.html
+++ b/excel_import_export/static/description/index.html
@@ -431,8 +431,8 @@
- self.env['xslx.report'].report_xlsx(...)
-After install this module, go to Settings > Excel Import/Export > XLSX
-Templates, this is where the key component located.
+After install this module, go to Settings > Technical > Excel
+Import/Export > XLSX Templates, this is where the key component located.
As this module provide tools, it is best to explain as use cases. For
example use cases, please install excel_import_export_demo
@@ -487,13 +487,15 @@
Another option for reporting is to use report action
(report_type=’excel’), I.e.,
-<report id='action_report_saleorder_excel'
- string='Quotation / Order (.xlsx)'
- model='sale.order'
- name='sale_order.xlsx'
- file='sale_order'
- report_type='excel'
-/>
+<record id="action_report_saleorder_excel" model="ir.actions.report">
+ <field name="name">Quotation / Order (.xlsx)</field>
+ <field name="model">ir.model</field>
+ <field name="report_name">'sale.order'</field>
+ <field name="report_file">'sale.order'</field>
+ <field name="binding_model_id" ref="sale.model_sale_order"/>
+ <field name="binding_type">report</field>
+ <field name="report_type">excel</field>
+</record>
By using report action, Odoo will find template using combination of
model and name, then do the export for the underlining record. Please
@@ -510,8 +512,8 @@
option allow user to create a report based on a model or view, all by
configuration only.
-- Goto > Technical> Excel Import/Export > XLSX Templates, and create a
-new template for a report.
+- Go to Settings > Technical> Excel Import/Export > XLSX Templates, and
+create a new template for a report.
- On the new template, select “Easy Reporting” option, then select
followings
- Report Model, this can be data model or data view we want to get
@@ -561,6 +563,7 @@
diff --git a/excel_import_export/static/src/js/report/action_manager_report.esm.js b/excel_import_export/static/src/js/report/action_manager_report.esm.js
index 0b67e026285..20b073be419 100644
--- a/excel_import_export/static/src/js/report/action_manager_report.esm.js
+++ b/excel_import_export/static/src/js/report/action_manager_report.esm.js
@@ -1,9 +1,8 @@
-/** @odoo-module **/
-
import {download} from "@web/core/network/download";
import {registry} from "@web/core/registry";
+import {user} from "@web/core/user";
-function getReportUrl({report_name, context, data}, env) {
+function getReportUrl({report_name, context, data}) {
// Rough copy of action_service.js _getReportUrl method.
let url = `/report/excel/${report_name}`;
const actionContext = context || {};
@@ -15,7 +14,7 @@ function getReportUrl({report_name, context, data}, env) {
if (actionContext.active_ids) {
url += `/${actionContext.active_ids.join(",")}`;
}
- const userContext = encodeURIComponent(JSON.stringify(env.services.user.context));
+ const userContext = encodeURIComponent(JSON.stringify(user.context));
return `${url}?context=${userContext}`;
}
@@ -26,8 +25,8 @@ async function triggerDownload(action, {onClose}, env) {
await download({
url: "/report/download",
data: {
- data: JSON.stringify([getReportUrl(action, env), "excel"]),
- context: JSON.stringify(env.services.user.context),
+ data: JSON.stringify([getReportUrl(action), "excel"]),
+ context: JSON.stringify(user.context),
},
});
} finally {
diff --git a/excel_import_export/views/xlsx_report.xml b/excel_import_export/views/xlsx_report.xml
index 5f9272bcab9..e412a351c34 100644
--- a/excel_import_export/views/xlsx_report.xml
+++ b/excel_import_export/views/xlsx_report.xml
@@ -9,24 +9,18 @@