Skip to content

Commit

Permalink
[MIG] excel_import_export: Migration to 18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
xaviedoanhduy committed Feb 25, 2025
1 parent 7ca65dd commit 23111d6
Show file tree
Hide file tree
Showing 23 changed files with 295 additions and 302 deletions.
25 changes: 14 additions & 11 deletions excel_import_export/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down Expand Up @@ -145,13 +145,15 @@ Another option for reporting is to use report action

.. code:: xml
<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
Expand All @@ -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

Expand Down Expand Up @@ -223,6 +225,7 @@ Contributors

- Kitti Upariphutthiphong. <[email protected]> (http://ecosoft.co.th)
- Saran Lim. <[email protected]> (http://ecosoft.co.th)
- Do Anh Duy <[email protected]>

Maintainers
-----------
Expand Down
2 changes: 1 addition & 1 deletion excel_import_export/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 3 additions & 6 deletions excel_import_export/controllers/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,27 @@
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)

Check warning on line 25 in excel_import_export/controllers/report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/controllers/report.py#L24-L25

Added lines #L24 - L25 were not covered by tests
if docids:
docids = [int(i) for i in docids.split(",")]
docids = [int(i) for i in docids.split(",") if i.isdigit()]

Check warning on line 27 in excel_import_export/controllers/report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/controllers/report.py#L27

Added line #L27 was not covered by tests
if data.get("options"):
data.update(json.loads(data.pop("options")))

Check warning on line 29 in excel_import_export/controllers/report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/controllers/report.py#L29

Added line #L29 was not covered by tests
if data.get("context"):
# Ignore 'lang' here, because the context in data is the one
# 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(

Check warning on line 36 in excel_import_export/controllers/report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/controllers/report.py#L34-L36

Added lines #L34 - L36 were not covered by tests
docids, data=data
)
Expand Down
14 changes: 0 additions & 14 deletions excel_import_export/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
33 changes: 18 additions & 15 deletions excel_import_export/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Check warning on line 31 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L25-L31

Added lines #L25 - L31 were not covered by tests


Expand All @@ -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)

Check warning on line 45 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L42-L45

Added lines #L42 - L45 were not covered by tests


Expand All @@ -53,7 +53,7 @@ def get_field_condition(field):
cond = field[i + 2 : j]
try:

Check warning on line 54 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L51-L54

Added lines #L51 - L54 were not covered by tests
if cond or cond == "":
return (field.replace("${%s}" % cond, ""), cond)
return (field.replace(f"${{{cond}}}", ""), cond)
except Exception:
return (field, False)
return (field, False)

Check warning on line 59 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L56-L59

Added lines #L56 - L59 were not covered by tests
Expand All @@ -74,7 +74,7 @@ def get_field_style(field):
cond = field[i + 2 : j]
try:

Check warning on line 75 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L72-L75

Added lines #L72 - L75 were not covered by tests
if cond or cond == "":
return (field.replace("#{%s}" % cond, ""), cond)
return (field.replace(f"#{{{cond}}}", ""), cond)
except Exception:
return (field, False)
return (field, False)

Check warning on line 80 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L77-L80

Added lines #L77 - L80 were not covered by tests
Expand All @@ -88,7 +88,7 @@ def get_field_style_cond(field):
cond = field[i + 2 : j]
try:

Check warning on line 89 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L86-L89

Added lines #L86 - L89 were not covered by tests
if cond or cond == "":
return (field.replace("#?%s?" % cond, ""), cond)
return (field.replace(f"#?{cond}?", ""), cond)
except Exception:
return (field, False)
return (field, False)

Check warning on line 94 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L91-L94

Added lines #L91 - L94 were not covered by tests
Expand All @@ -99,11 +99,14 @@ def fill_cell_style(field, field_style, styles):
for f in field_styles:
(key, value) = f.split("=")

Check warning on line 100 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L100

Added line #L100 was not covered by tests
if key not in styles.keys():
raise ValidationError(_("Invalid style type %s") % key)
raise ValidationError(_("Invalid style type %s", key))

Check warning on line 102 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L102

Added line #L102 was not covered by tests
if value.lower() not in styles[key].keys():
raise ValidationError(

Check warning on line 104 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L104

Added line #L104 was not covered by tests
_("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]

Check warning on line 111 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L111

Added line #L111 was not covered by tests
if key == "font":
Expand Down Expand Up @@ -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)

Check warning on line 155 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L155

Added line #L155 was not covered by tests
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)

Check warning on line 159 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L157-L159

Added lines #L157 - L159 were not covered by tests

Expand All @@ -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))

Check warning on line 169 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L167-L169

Added lines #L167 - L169 were not covered by tests


def xlrd_get_sheet_by_name(book, name):
Expand All @@ -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

Check warning on line 179 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L177-L179

Added lines #L177 - L179 were not covered by tests


def isfloat(input_val):
Expand Down Expand Up @@ -236,9 +239,9 @@ def csv_from_excel(excel_content, delimiter, quote):
raise ValidationError(

Check warning on line 239 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L239

Added line #L239 was not covered by tests
_(
"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)
Expand All @@ -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)

Check warning on line 254 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L254

Added line #L254 was not covered by tests
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

Check warning on line 258 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L256-L258

Added lines #L256 - L258 were not covered by tests
for c in col:
Expand Down Expand Up @@ -290,7 +293,7 @@ def _get_cell_value(cell, field_type=False):
value = value_str

Check warning on line 293 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L293

Added line #L293 was not covered by tests
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)

Check warning on line 297 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L297

Added line #L297 was not covered by tests
else:
value = cell.value

Check warning on line 299 in excel_import_export/models/common.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/common.py#L299

Added line #L299 was not covered by tests
Expand Down
23 changes: 13 additions & 10 deletions excel_import_export/models/ir_report.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -15,28 +15,31 @@ 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(

Check warning on line 18 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L18

Added line #L18 was not covered by tests
self.env._("Only one id is allowed for excel_import_export")
)
xlsx_template = self.env["xlsx.template"].search(

Check warning on line 21 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L21

Added line #L21 was not covered by tests
[("fname", "=", self.report_name), ("res_model", "=", self.model)]
)
if not xlsx_template or len(xlsx_template) != 1:
raise UserError(

Check warning on line 25 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L25

Added line #L25 was not covered by tests
_("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])

Check warning on line 32 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L32

Added line #L32 was not covered by tests

@api.model
def _get_report_from_name(self, report_name):
res = super()._get_report_from_name(report_name)

Check warning on line 36 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L36

Added line #L36 was not covered by tests
if res:
return res
report_obj = self.env["ir.actions.report"]
qwebtypes = ["excel"]
conditions = [
("report_type", "in", qwebtypes),
domain = [

Check warning on line 40 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L38-L40

Added lines #L38 - L40 were not covered by tests
("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)

Check warning on line 45 in excel_import_export/models/ir_report.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/ir_report.py#L44-L45

Added lines #L44 - L45 were not covered by tests
24 changes: 13 additions & 11 deletions excel_import_export/models/xlsx_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]

Check warning on line 80 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L77-L80

Added lines #L77 - L80 were not covered by tests
if max_row > 0 and len(lines) > max_row:
raise Exception(_("Records in %s exceed max records allowed") % line_field)
raise Exception(

Check warning on line 82 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L82

Added line #L82 was not covered by tests
self.env._("Records in %s exceed max records allowed", line_field)
)
vals = {field: [] for field in fields} # value and do_style

Check warning on line 85 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L85

Added line #L85 was not covered by tests
# Get field condition & aggre function
conditions_dict = self._get_conditions_dict()

Check warning on line 87 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L87

Added line #L87 was not covered by tests
Expand Down Expand Up @@ -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))

Check warning on line 128 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L125-L128

Added lines #L125 - L128 were not covered by tests
if not styles:
return False
res = safe_eval(style_cond, eval_context)

Check warning on line 131 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L130-L131

Added lines #L130 - L131 were not covered by tests
Expand All @@ -144,26 +146,26 @@ def _fill_workbook_data(self, workbook, record, data_dict):
st = co.openpyxl_get_sheet_by_name(workbook, sheet_name)

Check warning on line 146 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L146

Added line #L146 was not covered by tests
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]

Check warning on line 150 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L149-L150

Added lines #L149 - L150 were not covered by tests
if not st:
raise ValidationError(_("Sheet %s not found") % sheet_name)
raise ValidationError(self.env._("Sheet %s not found", sheet_name))

Check warning on line 152 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L152

Added line #L152 was not covered by tests
# 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(

Check warning on line 159 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L154-L159

Added lines #L154 - L159 were not covered by tests
_(
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(

Check warning on line 167 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L166-L167

Added lines #L166 - L167 were not covered by tests
_("Error filling data into Excel sheets\n%s") % e
self.env._("Error filling data into Excel sheets\n%s", e)
) from e

@api.model
Expand Down Expand Up @@ -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

Check warning on line 259 in excel_import_export/models/xlsx_export.py

View check run for this annotation

Codecov / codecov/patch

excel_import_export/models/xlsx_export.py#L256-L259

Added lines #L256 - L259 were not covered by tests
Expand Down
Loading

0 comments on commit 23111d6

Please sign in to comment.