Initial commit: OCA Report packages (45 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:05 +02:00
commit 2f4db400df
2543 changed files with 469120 additions and 0 deletions

View file

@ -0,0 +1,142 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
===============
Base report csv
===============
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ea7a062b40320443bdb91ba5319c9f79630439080c9ad3b7919abdbda5a6b294
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github
:target: https://github.com/OCA/reporting-engine/tree/16.0/report_csv
:alt: OCA/reporting-engine
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/reporting-engine-16-0/reporting-engine-16-0-report_csv
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module provides a basic report class to generate csv report.
**Table of contents**
.. contents::
:local:
Configuration
=============
In case the exported CSV report should be encoded in another system than UTF-8, following
fields of the report record (*Settings > Technical > Reports*) should be populated accordingly.
* Encoding: set an encoding system (such as cp932)
* Encode Error Handling: select 'Ignore' or 'Replace' as necessary.
* 'Ignore': in case of an encoding error, the problematic character will be removed from the exported file.
* 'Replace': in case of an encoding error, the problematic character will be replaced with '?' symbol.
* Leaving the field blank: in case of an encoding error, the report generation fails with an error message.
Usage
=====
An example of CSV report for partners on a module called `module_name`:
A python class ::
from odoo import models
class PartnerCSV(models.AbstractModel):
_name = 'report.report_csv.partner_csv'
_inherit = 'report.report_csv.abstract'
def generate_csv_report(self, writer, data, partners):
writer.writeheader()
for obj in partners:
writer.writerow({
'name': obj.name,
'email': obj.email,
})
def csv_report_options(self):
res = super().csv_report_options()
res['fieldnames'].append('name')
res['fieldnames'].append('email')
res['delimiter'] = ';'
res['quoting'] = csv.QUOTE_ALL
return res
A report XML record ::
<report
id="partner_csv"
model="res.partner"
string="Print to CSV"
report_type="csv"
name="module_name.report_name"
file="res_partner"
attachment_use="False"
/>
Update encoding with an appropriate value (e.g. cp932) as necessary.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_csv%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Creu Blanca
Contributors
~~~~~~~~~~~~
* Enric Tobella <etobella@creublanca.es>
* Jaime Arroyo <jaime.arroyo@creublanca.es>
* Rattapong Chokmasermkul <rattapongc@ecosoft.co.th>
* `Quartile <https://www.quartile.co>`__:
* Aung Ko Ko Lin
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/reporting-engine <https://github.com/OCA/reporting-engine/tree/16.0/report_csv>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,3 @@
from . import controllers
from . import models
from . import report

View file

@ -0,0 +1,20 @@
# Copyright 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Base report csv",
"summary": "Base module to create csv report",
"author": "Creu Blanca, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/reporting-engine",
"category": "Reporting",
"version": "16.0.2.1.1",
"license": "AGPL-3",
"depends": ["base", "web"],
"demo": ["demo/report.xml"],
"data": ["views/ir_actions_views.xml"],
"assets": {
"web.assets_backend": [
"report_csv/static/src/js/report/qwebactionmanager.esm.js"
]
},
"installable": True,
}

View file

@ -0,0 +1 @@
from . import main

View file

@ -0,0 +1,108 @@
# Copyright (C) 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
import json
import logging
from werkzeug.exceptions import InternalServerError
from werkzeug.urls import url_decode
from odoo.http import (
content_disposition,
request,
route,
serialize_exception as _serialize_exception,
)
from odoo.tools import html_escape
from odoo.tools.safe_eval import safe_eval, time
from odoo.addons.web.controllers import report
_logger = logging.getLogger(__name__)
class ReportController(report.ReportController):
@route()
def report_routes(self, reportname, docids=None, converter=None, **data):
if converter == "csv":
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(",")]
if data.get("options"):
data.update(json.loads(data.pop("options")))
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"])
csv = report.with_context(**context)._render_csv(
reportname, docids, data=data
)[0]
csvhttpheaders = [
("Content-Type", "text/csv"),
("Content-Length", len(csv)),
]
return request.make_response(csv, headers=csvhttpheaders)
return super(ReportController, self).report_routes(
reportname, docids, converter, **data
)
@route()
def report_download(self, data, context=None):
requestcontent = json.loads(data)
url, report_type = requestcontent[0], requestcontent[1]
reportname = ""
try:
if report_type == "csv":
reportname = url.split("/report/csv/")[1].split("?")[0]
docids = None
if "/" in reportname:
reportname, docids = reportname.split("/")
if docids:
# Generic report:
response = self.report_routes(
reportname, docids=docids, converter="csv", context=context
)
else:
# Particular report:
data = dict(
url_decode(url.split("?")[1]).items()
) # decoding the args represented in JSON
if "context" in data:
context, data_context = json.loads(context or "{}"), json.loads(
data.pop("context")
)
context = json.dumps({**context, **data_context})
response = self.report_routes(
reportname, converter="csv", context=context, **data
)
report = request.env["ir.actions.report"]._get_report_from_name(
reportname
)
filename = "%s.%s" % (report.name, "csv")
if docids:
ids = [int(x) for x in docids.split(",")]
obj = request.env[report.model].browse(ids)
if report.print_report_name and not len(obj) > 1:
report_name = safe_eval(
report.print_report_name, {"object": obj, "time": time}
)
filename = "%s.%s" % (report_name, "csv")
response.headers.add(
"Content-Disposition", content_disposition(filename)
)
return response
else:
return super(ReportController, self).report_download(data, context)
except Exception as e:
_logger.exception("Error while generating report %s", reportname)
se = _serialize_exception(e)
error = {"code": 200, "message": "Odoo Server Error", "data": se}
res = request.make_response(html_escape(json.dumps(error)))
raise InternalServerError(response=res) from e

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--
Copyright 2019 Creu Blanca
License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
-->
<record id="partner_csv" model="ir.actions.report">
<field name="name">Print to CSV</field>
<field name="model">res.partner</field>
<field name="report_type">csv</field>
<field name="report_name">report_csv.partner_csv</field>
<field name="report_file">res_partner</field>
</record>
</odoo>

View file

@ -0,0 +1,92 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_csv
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_abstract
msgid "Abstract Model for CSV reports"
msgstr "Apstraktni model za CSV izvještaje"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encode_error_handling
msgid "Encode Error Handling"
msgstr "Rukovanje greškom kodiranja"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encoding
msgid "Encoding"
msgstr "Kodna stranica:"
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encoding
msgid "Encoding to be applied to the generated CSV file. e.g. cp932"
msgstr "Kodiranje koje će se primijeniti na generisanu CSV datoteku. npr. cp932"
#. module: report_csv
#. odoo-python
#: code:addons/report_csv/report/report_csv.py:0
#, python-format
msgid "Failed to encode the data with the encoding set in the report."
msgstr "Neuspjeh kodiranja podataka s kodiranjem postavljenim u izvještaju."
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encode_error_handling
msgid ""
"If nothing is selected, CSV export will fail with an error message when "
"there is a character that fail to be encoded."
msgstr ""
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__ignore
msgid "Ignore"
msgstr "Ignoriši"
#. module: report_csv
#: model:ir.actions.report,name:report_csv.partner_csv
msgid "Print to CSV"
msgstr "Ispiši u CSV"
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__replace
msgid "Replace"
msgstr "Zamijeni"
#. module: report_csv
#: model:ir.model,name:report_csv.model_ir_actions_report
msgid "Report Action"
msgstr "Akcija izvještaja"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_partner_csv
msgid "Report Partner to CSV"
msgstr "Izvještaj partner u CSV"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__report_type
msgid "Report Type"
msgstr "Tip izvještaja"
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__report_type
msgid ""
"The type of the report that will be rendered, each one having its own "
"rendering method. HTML means the report will be opened directly in your "
"browser PDF means the report will be rendered using Wkhtmltopdf and "
"downloaded by the user."
msgstr ""
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__report_type__csv
msgid "csv"
msgstr "csv"

View file

@ -0,0 +1,103 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_csv
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-04-22 22:43+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_abstract
msgid "Abstract Model for CSV reports"
msgstr "Modelo abstracto para informes CSV"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encode_error_handling
msgid "Encode Error Handling"
msgstr "Tratamiento de Errores de Codificación"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encoding
msgid "Encoding"
msgstr "Codificación"
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encoding
msgid "Encoding to be applied to the generated CSV file. e.g. cp932"
msgstr "Codificación que se aplicará al archivo CSV generado. p. ej. cp932"
#. module: report_csv
#. odoo-python
#: code:addons/report_csv/report/report_csv.py:0
#, python-format
msgid "Failed to encode the data with the encoding set in the report."
msgstr ""
"No se han podido codificar los datos con la codificación establecida en el "
"informe."
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encode_error_handling
msgid ""
"If nothing is selected, CSV export will fail with an error message when "
"there is a character that fail to be encoded."
msgstr ""
"Si no se selecciona nada, la exportación CSV fallará con un mensaje de error "
"cuando haya un carácter que no se pueda codificar."
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__ignore
msgid "Ignore"
msgstr "Ignorar"
#. module: report_csv
#: model:ir.actions.report,name:report_csv.partner_csv
msgid "Print to CSV"
msgstr "Imprimir en CSV"
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__replace
msgid "Replace"
msgstr "Reemplazar"
#. module: report_csv
#: model:ir.model,name:report_csv.model_ir_actions_report
msgid "Report Action"
msgstr "Informar Acción"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_partner_csv
msgid "Report Partner to CSV"
msgstr "Reportar Socio a CSV"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__report_type
msgid "Report Type"
msgstr "Tipo de Informe"
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__report_type
msgid ""
"The type of the report that will be rendered, each one having its own "
"rendering method. HTML means the report will be opened directly in your "
"browser PDF means the report will be rendered using Wkhtmltopdf and "
"downloaded by the user."
msgstr ""
"El tipo de informe que se generará, cada uno con su propio método de "
"generación. HTML significa que el informe se abrirá directamente en el "
"navegador PDF significa que el informe se renderizará utilizando Wkhtmltopdf "
"y será descargado por el usuario."
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__report_type__csv
msgid "csv"
msgstr "csv"

View file

@ -0,0 +1,101 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_csv
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-01-18 09:34+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_abstract
msgid "Abstract Model for CSV reports"
msgstr "Modello astratto per resoconto CSV"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encode_error_handling
msgid "Encode Error Handling"
msgstr "Gestione errore codifica"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encoding
msgid "Encoding"
msgstr "Codifica"
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encoding
msgid "Encoding to be applied to the generated CSV file. e.g. cp932"
msgstr "Codifica da applicare al file CSV generato. Es. cp932"
#. module: report_csv
#. odoo-python
#: code:addons/report_csv/report/report_csv.py:0
#, python-format
msgid "Failed to encode the data with the encoding set in the report."
msgstr "Codifica dati fallita con la codifica impostata nel resoconto."
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encode_error_handling
msgid ""
"If nothing is selected, CSV export will fail with an error message when "
"there is a character that fail to be encoded."
msgstr ""
"Se non è selezionato nulla, l'esportazione del CSV fallirà con un messaggio "
"di errore qando c'è un carattere che non può essere codificato."
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__ignore
msgid "Ignore"
msgstr "Ignora"
#. module: report_csv
#: model:ir.actions.report,name:report_csv.partner_csv
msgid "Print to CSV"
msgstr "Stampa su CSV"
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__replace
msgid "Replace"
msgstr "Sostituzione"
#. module: report_csv
#: model:ir.model,name:report_csv.model_ir_actions_report
msgid "Report Action"
msgstr "Azione resoconto"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_partner_csv
msgid "Report Partner to CSV"
msgstr "Indica il partner al CSV"
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__report_type
msgid "Report Type"
msgstr "Tipo resoconto"
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__report_type
msgid ""
"The type of the report that will be rendered, each one having its own "
"rendering method. HTML means the report will be opened directly in your "
"browser PDF means the report will be rendered using Wkhtmltopdf and "
"downloaded by the user."
msgstr ""
"Il tipo di resoconto che verrà generato, ognuno avente il suo metodo di "
"generazione. HTML vuol dire che il resoconto sarà aperto direttamente nel "
"tuo browser mentre PDF vuol dire che il resoconto sarà generato usando "
"Wkhtmltopdf e scaricato dall'utente."
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__report_type__csv
msgid "csv"
msgstr "csv"

View file

@ -0,0 +1,92 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_csv
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_abstract
msgid "Abstract Model for CSV reports"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encode_error_handling
msgid "Encode Error Handling"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__encoding
msgid "Encoding"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encoding
msgid "Encoding to be applied to the generated CSV file. e.g. cp932"
msgstr ""
#. module: report_csv
#. odoo-python
#: code:addons/report_csv/report/report_csv.py:0
#, python-format
msgid "Failed to encode the data with the encoding set in the report."
msgstr ""
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__encode_error_handling
msgid ""
"If nothing is selected, CSV export will fail with an error message when "
"there is a character that fail to be encoded."
msgstr ""
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__ignore
msgid "Ignore"
msgstr ""
#. module: report_csv
#: model:ir.actions.report,name:report_csv.partner_csv
msgid "Print to CSV"
msgstr ""
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__encode_error_handling__replace
msgid "Replace"
msgstr ""
#. module: report_csv
#: model:ir.model,name:report_csv.model_ir_actions_report
msgid "Report Action"
msgstr ""
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_partner_csv
msgid "Report Partner to CSV"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_ir_actions_report__report_type
msgid "Report Type"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,help:report_csv.field_ir_actions_report__report_type
msgid ""
"The type of the report that will be rendered, each one having its own "
"rendering method. HTML means the report will be opened directly in your "
"browser PDF means the report will be rendered using Wkhtmltopdf and "
"downloaded by the user."
msgstr ""
#. module: report_csv
#: model:ir.model.fields.selection,name:report_csv.selection__ir_actions_report__report_type__csv
msgid "csv"
msgstr ""

View file

@ -0,0 +1 @@
from . import ir_report

View file

@ -0,0 +1,93 @@
# Copyright 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
from odoo import api, fields, models
from odoo.exceptions import AccessError
from odoo.tools.safe_eval import safe_eval, time
_logger = logging.getLogger(__name__)
class ReportAction(models.Model):
_inherit = "ir.actions.report"
report_type = fields.Selection(
selection_add=[("csv", "csv")], ondelete={"csv": "set default"}
)
encoding = fields.Char(
help="Encoding to be applied to the generated CSV file. e.g. cp932"
)
encode_error_handling = fields.Selection(
selection=[("ignore", "Ignore"), ("replace", "Replace")],
help="If nothing is selected, CSV export will fail with an error message when "
"there is a character that fail to be encoded.",
)
def _create_csv_attachment(self, record, data):
attachment_name = safe_eval(self.attachment, {"object": record, "time": time})
# Unable to compute a name for the attachment.
if not attachment_name:
return
if record and data:
attachment = {
"name": attachment_name,
"raw": data,
"res_model": self.model,
"res_id": record.id,
"type": "binary",
}
try:
self.env["ir.attachment"].create(attachment)
except AccessError:
_logger.info(
"Cannot save csv report %r as attachment", attachment["name"]
)
else:
_logger.info(
"The csv document %s is now saved in the database",
attachment["name"],
)
@api.model
def _render_csv(self, report_ref, docids, data):
report_sudo = self._get_report(report_ref)
report_model_name = "report.%s" % report_sudo.report_name
report_model = self.env[report_model_name]
res_id = docids[0] if docids and len(docids) == 1 else None
if not res_id or not report_sudo.attachment or not report_sudo.attachment_use:
return report_model.with_context(
**{
"active_model": report_sudo.model,
"encoding": self.encoding,
"encode_error_handling": self.encode_error_handling,
}
).create_csv_report(docids, data)
record = self.env[report_sudo.model].browse(res_id)
attachment = report_sudo.retrieve_attachment(record)
if attachment:
return attachment.raw.decode(), "csv"
data, ext = report_model.with_context(
**{
"active_model": report_sudo.model,
"encoding": self.encoding,
"encode_error_handling": self.encode_error_handling,
}
).create_csv_report(docids, data)
report_sudo._create_csv_attachment(record, data)
return data, ext
@api.model
def _get_report_from_name(self, report_name):
res = super(ReportAction, self)._get_report_from_name(report_name)
if res:
return res
report_obj = self.env["ir.actions.report"]
qwebtypes = ["csv"]
conditions = [
("report_type", "in", qwebtypes),
("report_name", "=", report_name),
]
context = self.env["res.users"].context_get()
return report_obj.with_context(**context).search(conditions, limit=1)

View file

@ -0,0 +1,9 @@
In case the exported CSV report should be encoded in another system than UTF-8, following
fields of the report record (*Settings > Technical > Reports*) should be populated accordingly.
* Encoding: set an encoding system (such as cp932)
* Encode Error Handling: select 'Ignore' or 'Replace' as necessary.
* 'Ignore': in case of an encoding error, the problematic character will be removed from the exported file.
* 'Replace': in case of an encoding error, the problematic character will be replaced with '?' symbol.
* Leaving the field blank: in case of an encoding error, the report generation fails with an error message.

View file

@ -0,0 +1,6 @@
* Enric Tobella <etobella@creublanca.es>
* Jaime Arroyo <jaime.arroyo@creublanca.es>
* Rattapong Chokmasermkul <rattapongc@ecosoft.co.th>
* `Quartile <https://www.quartile.co>`__:
* Aung Ko Ko Lin

View file

@ -0,0 +1 @@
This module provides a basic report class to generate csv report.

View file

@ -0,0 +1,40 @@
An example of CSV report for partners on a module called `module_name`:
A python class ::
from odoo import models
class PartnerCSV(models.AbstractModel):
_name = 'report.report_csv.partner_csv'
_inherit = 'report.report_csv.abstract'
def generate_csv_report(self, writer, data, partners):
writer.writeheader()
for obj in partners:
writer.writerow({
'name': obj.name,
'email': obj.email,
})
def csv_report_options(self):
res = super().csv_report_options()
res['fieldnames'].append('name')
res['fieldnames'].append('email')
res['delimiter'] = ';'
res['quoting'] = csv.QUOTE_ALL
return res
A report XML record ::
<report
id="partner_csv"
model="res.partner"
string="Print to CSV"
report_type="csv"
name="module_name.report_name"
file="res_partner"
attachment_use="False"
/>
Update encoding with an appropriate value (e.g. cp932) as necessary.

View file

@ -0,0 +1,2 @@
from . import report_csv
from . import report_partner_csv

View file

@ -0,0 +1,73 @@
# Copyright 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
from io import StringIO
from odoo import _, models
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
try:
import csv
except ImportError:
_logger.debug("Can not import csvwriter`.")
class ReportCSVAbstract(models.AbstractModel):
_name = "report.report_csv.abstract"
_description = "Abstract Model for CSV reports"
def _get_objs_for_report(self, docids, data):
"""
Returns objects for csv report. From WebUI these
are either as docids taken from context.active_ids or
in the case of wizard are in data. Manual calls may rely
on regular context, setting docids, or setting data.
:param docids: list of integers, typically provided by
qwebactionmanager for regular Models.
:param data: dictionary of data, if present typically provided
by qwebactionmanager for TransientModels.
:param ids: list of integers, provided by overrides.
:return: recordset of active model for ids.
"""
if docids:
ids = docids
elif data and "context" in data:
ids = data["context"].get("active_ids", [])
else:
ids = self.env.context.get("active_ids", [])
return self.env[self.env.context.get("active_model")].browse(ids)
def create_csv_report(self, docids, data):
objs = self._get_objs_for_report(docids, data)
file_data = StringIO()
file = csv.DictWriter(file_data, **self.csv_report_options())
self.generate_csv_report(file, data, objs)
file_data.seek(0)
encoding = self._context.get("encoding")
if not encoding:
return file_data.read(), "csv"
error_handling = self._context.get("encode_error_handling")
if error_handling:
return file_data.read().encode(encoding, errors=error_handling), "csv"
try:
return file_data.read().encode(encoding), "csv"
except Exception as e:
raise UserError(
_("Failed to encode the data with the encoding set in the report.")
) from e
def csv_report_options(self):
"""
:return: dictionary of parameters. At least return 'fieldnames', but
you can optionally return parameters that define the export format.
Valid parameters include 'delimiter', 'quotechar', 'escapechar',
'doublequote', 'skipinitialspace', 'lineterminator', 'quoting'.
"""
return {"fieldnames": []}
def generate_csv_report(self, file, data, objs):
raise NotImplementedError()

View file

@ -0,0 +1,24 @@
# Copyright 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import csv
from odoo import models
class PartnerCSV(models.AbstractModel):
_name = "report.report_csv.partner_csv"
_inherit = "report.report_csv.abstract"
_description = "Report Partner to CSV"
def generate_csv_report(self, writer, data, partners):
writer.writeheader()
for obj in partners:
writer.writerow({"name": obj.name, "email": obj.email})
def csv_report_options(self):
res = super().csv_report_options()
res["fieldnames"].append("name")
res["fieldnames"].append("email")
res["delimiter"] = ";"
res["quoting"] = csv.QUOTE_ALL
return res

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,492 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="base-report-csv">
<h1>Base report csv</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ea7a062b40320443bdb91ba5319c9f79630439080c9ad3b7919abdbda5a6b294
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/reporting-engine/tree/16.0/report_csv"><img alt="OCA/reporting-engine" src="https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/reporting-engine-16-0/reporting-engine-16-0-report_csv"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module provides a basic report class to generate csv report.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<p>In case the exported CSV report should be encoded in another system than UTF-8, following
fields of the report record (<em>Settings &gt; Technical &gt; Reports</em>) should be populated accordingly.</p>
<ul class="simple">
<li>Encoding: set an encoding system (such as cp932)</li>
<li>Encode Error Handling: select Ignore or Replace as necessary.<ul>
<li>Ignore: in case of an encoding error, the problematic character will be removed from the exported file.</li>
<li>Replace: in case of an encoding error, the problematic character will be replaced with ? symbol.</li>
<li>Leaving the field blank: in case of an encoding error, the report generation fails with an error message.</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<p>An example of CSV report for partners on a module called <cite>module_name</cite>:</p>
<p>A python class</p>
<pre class="literal-block">
from odoo import models
class PartnerCSV(models.AbstractModel):
_name = 'report.report_csv.partner_csv'
_inherit = 'report.report_csv.abstract'
def generate_csv_report(self, writer, data, partners):
writer.writeheader()
for obj in partners:
writer.writerow({
'name': obj.name,
'email': obj.email,
})
def csv_report_options(self):
res = super().csv_report_options()
res['fieldnames'].append('name')
res['fieldnames'].append('email')
res['delimiter'] = ';'
res['quoting'] = csv.QUOTE_ALL
return res
</pre>
<p>A report XML record</p>
<pre class="literal-block">
&lt;report
id=&quot;partner_csv&quot;
model=&quot;res.partner&quot;
string=&quot;Print to CSV&quot;
report_type=&quot;csv&quot;
name=&quot;module_name.report_name&quot;
file=&quot;res_partner&quot;
attachment_use=&quot;False&quot;
/&gt;
</pre>
<p>Update encoding with an appropriate value (e.g. cp932) as necessary.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/reporting-engine/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_csv%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-5">Authors</a></h3>
<ul class="simple">
<li>Creu Blanca</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
<ul class="simple">
<li>Enric Tobella &lt;<a class="reference external" href="mailto:etobella&#64;creublanca.es">etobella&#64;creublanca.es</a>&gt;</li>
<li>Jaime Arroyo &lt;<a class="reference external" href="mailto:jaime.arroyo&#64;creublanca.es">jaime.arroyo&#64;creublanca.es</a>&gt;</li>
<li>Rattapong Chokmasermkul &lt;<a class="reference external" href="mailto:rattapongc&#64;ecosoft.co.th">rattapongc&#64;ecosoft.co.th</a>&gt;</li>
<li><a class="reference external" href="https://www.quartile.co">Quartile</a>:<ul>
<li>Aung Ko Ko Lin</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/reporting-engine/tree/16.0/report_csv">OCA/reporting-engine</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,54 @@
/** @odoo-module **/
import {download} from "@web/core/network/download";
import {registry} from "@web/core/registry";
registry
.category("ir.actions.report handlers")
.add("csv_handler", async function (action, options, env) {
if (action.report_type === "csv") {
const type = action.report_type;
let url = `/report/${type}/${action.report_name}`;
const actionContext = action.context || {};
if (action.data && JSON.stringify(action.data) !== "{}") {
// Build a query string with `action.data` (it's the place where reports
// using a wizard to customize the output traditionally put their options)
const action_options = encodeURIComponent(JSON.stringify(action.data));
const context = encodeURIComponent(JSON.stringify(actionContext));
url += `?options=${action_options}&context=${context}`;
} else {
if (actionContext.active_ids) {
url += `/${actionContext.active_ids.join(",")}`;
}
if (type === "csv") {
const context = encodeURIComponent(
JSON.stringify(env.services.user.context)
);
url += `?context=${context}`;
}
}
env.services.ui.block();
try {
await download({
url: "/report/download",
data: {
data: JSON.stringify([url, action.report_type]),
context: JSON.stringify(env.services.user.context),
},
});
} finally {
env.services.ui.unblock();
}
const onClose = options.onClose;
if (action.close_on_report_download) {
return env.services.action.doAction(
{type: "ir.actions.act_window_close"},
{onClose}
);
} else if (onClose) {
onClose();
}
return Promise.resolve(true);
}
return Promise.resolve(false);
});

View file

@ -0,0 +1 @@
from . import test_report

View file

@ -0,0 +1,150 @@
# Copyright 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import json
import logging
from io import StringIO
from unittest import mock
from odoo import http
from odoo.exceptions import UserError
from odoo.tests import common
from odoo.tools import mute_logger
from odoo.addons.web.controllers.report import ReportController
_logger = logging.getLogger(__name__)
try:
import csv
except ImportError:
_logger.debug("Can not import csv.")
class TestCsvException(Exception):
def __init__(self, message):
"""
:param message: exception message and frontend modal content
"""
super().__init__(message)
class TestReport(common.TransactionCase):
def setUp(self):
super().setUp()
self.report_object = self.env["ir.actions.report"]
self.csv_report = self.env["report.report_csv.abstract"].with_context(
active_model="res.partner"
)
self.report_name = "report_csv.partner_csv"
self.report = self.report_object._get_report_from_name(self.report_name)
self.docs = self.env["res.company"].search([], limit=1).partner_id
def test_report(self):
# Test if not res:
report = self.report
self.assertEqual(report.report_type, "csv")
rep = self.report_object._render(self.report_name, self.docs.ids, {})
str_io = StringIO(rep[0])
dict_report = list(csv.DictReader(str_io, delimiter=";", quoting=csv.QUOTE_ALL))
self.assertEqual(self.docs.name, dict(dict_report[0])["name"])
def test_attachment_use(self):
self.report.attachment = "object.name+'.csv'"
self.report.attachment_use = True
rep = self.report_object._render(self.report_name, self.docs.ids, {})
attachment = self.env["ir.attachment"].search(
[("name", "=", self.docs.name + ".csv")]
)
self.assertTrue(attachment)
self.assertEqual(attachment.raw.decode(), rep[0])
rep_from_attachment = self.report_object._render(
self.report_name, self.docs.ids, {}
)
self.assertTupleEqual(rep, rep_from_attachment)
def test_id_retrieval(self):
# Typical call from WebUI with wizard
objs = self.csv_report._get_objs_for_report(
False, {"context": {"active_ids": self.docs.ids}}
)
self.assertEqual(objs, self.docs)
# Typical call from within code not to report_action
objs = self.csv_report.with_context(
active_ids=self.docs.ids
)._get_objs_for_report(False, False)
self.assertEqual(objs, self.docs)
# Typical call from WebUI
objs = self.csv_report._get_objs_for_report(
self.docs.ids, {"data": [self.report_name, self.report.report_type]}
)
self.assertEqual(objs, self.docs)
# Typical call from render
objs = self.csv_report._get_objs_for_report(self.docs.ids, {})
self.assertEqual(objs, self.docs)
def test_report_with_encoding(self):
report = self.report
report.write({"encoding": "cp932"})
rep = report._render_csv(self.report_name, self.docs.ids, {})
str_io = StringIO(rep[0].decode())
dict_report = list(csv.DictReader(str_io, delimiter=";", quoting=csv.QUOTE_ALL))
self.assertEqual(self.docs.name, dict(dict_report[0])["name"])
report.write({"encoding": "xyz"})
with self.assertRaises(UserError):
rep = report._render_csv(self.report_name, self.docs.ids, {})
def test_report_docids_is_none(self):
"""
Test when docids is None (should not raise TypeError)
"""
self.report_object._render_csv(self.report_name, None, {})
class TestCsvReport(common.HttpCase):
"""
Some tests calling controller
"""
def setUp(self):
super().setUp()
self.report_object = self.env["ir.actions.report"]
self.csv_report = self.env["report.report_csv.abstract"].with_context(
active_model="res.partner"
)
self.report_name = "report_csv.partner_csv"
self.report = self.report_object._get_report_from_name(self.report_name)
self.docs = self.env["res.company"].search([], limit=1).partner_id
self.session = self.authenticate("admin", "admin")
def test_csv(self):
filename = self.get_report_headers().headers.get("Content-Disposition")
self.assertTrue(".csv" in filename)
@mute_logger("odoo.addons.web.controllers.report")
def test_pdf_error(self):
with mock.patch.object(
ReportController, "report_routes"
) as route_patch, self.assertLogs(
"odoo.addons.report_csv.controllers.main", level=logging.ERROR
) as cm:
route_patch.side_effect = TestCsvException("Test")
self.get_report_headers(
suffix="/report/pdf/test/10", f_type="qweb-pdf"
).headers.get("Content-Disposition")
[msg] = cm.output
self.assertIn("Error while generating report", msg)
def get_report_headers(
self, suffix="/report/csv/report_csv.partner_csv/1", f_type="csv"
):
return self.url_open(
url="/report/download",
data={
"data": json.dumps([suffix, f_type]),
"csrf_token": http.Request.csrf_token(self),
},
)

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="act_report_xml_view" model="ir.ui.view">
<field name="name">ir.actions.report</field>
<field name="model">ir.actions.report</field>
<field name="inherit_id" ref="base.act_report_xml_view" />
<field name="arch" type="xml">
<xpath expr="//field[@name='report_type']" position="after">
<field
name="encoding"
attrs="{'invisible': [('report_type', '!=', 'csv')]}"
/>
<field
name="encode_error_handling"
attrs="{'invisible': [('encoding', '=', False)]}"
/>
</xpath>
</field>
</record>
</odoo>