Merge PR #367 into 13.0

Signed-off-by lmignon
pull/374/head
OCA-git-bot 2020-03-02 07:53:16 +00:00
commit bf683e17aa
21 changed files with 1115 additions and 0 deletions

View File

@ -0,0 +1,117 @@
===============
Base report csv
===============
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/licence-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/13.0-mig-report_csv/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-13-0-mig-report_csv/reporting-engine-13-0-mig-report_csv-report_csv
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/143/13.0-mig-report_csv
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module provides a basic report class to generate csv report.
**Table of contents**
.. contents::
:local:
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"
/>
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 smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_csv%0Aversion:%2013.0-mig-report_csv%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>
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/13.0-mig-report_csv/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,16 @@
# 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": "13.0.1.0.0",
"license": "AGPL-3",
"external_dependencies": {"python": ["csv"]},
"depends": ["base", "web"],
"data": ["views/webclient_templates.xml"],
"demo": ["demo/report.xml"],
"installable": True,
}

View File

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

View File

@ -0,0 +1,57 @@
# Copyright (C) 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
import json
import time
from odoo.http import content_disposition, request, route
from odoo.tools.safe_eval import safe_eval
from odoo.addons.web.controllers import main as report
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(docids, data=data)[0]
filename = "{}.{}".format(report.name, "csv")
if docids:
obj = request.env[report.model].browse(docids)
if report.print_report_name and not len(obj) > 1:
report_name = safe_eval(
report.print_report_name,
{"object": obj, "time": time, "multi": False},
)
filename = "{}.{}".format(report_name, "csv")
# When we print multiple records we still allow a custom
# filename.
elif report.print_report_name and len(obj) > 1:
report_name = safe_eval(
report.print_report_name,
{"objects": obj, "time": time, "multi": True},
)
filename = "{}.{}".format(report_name, "csv")
csvhttpheaders = [
("Content-Type", "text/csv"),
("Content-Length", len(csv)),
("Content-Disposition", content_disposition(filename)),
]
return request.make_response(csv, headers=csvhttpheaders)
return super(ReportController, self).report_routes(
reportname, docids, converter, **data
)

View File

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

View File

@ -0,0 +1,118 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * report_csv
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.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
#: code:addons/report_csv/models/ir_report.py:18
#, python-format
msgid "%s model was not found"
msgstr ""
#. module: report_csv
#. openerp-web
#: code:addons/report_csv/static/src/js/report/qwebactionmanager.js:49
#, python-format
msgid "A popup window with your report was blocked. You may need to change your browser settings to allow popup windows for this page."
msgstr ""
#. 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_report_report_csv_abstract__display_name
#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_partner_csv__display_name
msgid "Display Name"
msgstr ""
#. module: report_csv
#: selection:ir.actions.report,report_type:0
msgid "HTML"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_abstract__id
#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_partner_csv__id
msgid "ID"
msgstr ""
#. module: report_csv
#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_abstract____last_update
#: model:ir.model.fields,field_description:report_csv.field_report_report_csv_partner_csv____last_update
msgid "Last Modified on"
msgstr ""
#. module: report_csv
#: selection:ir.actions.report,report_type:0
msgid "PDF"
msgstr ""
#. module: report_csv
#: model:ir.actions.report,name:report_csv.partner_csv
msgid "Print to CSV"
msgstr ""
#. module: report_csv
#: selection:ir.actions.report,report_type:0
msgid "Py3o"
msgstr ""
#. module: report_csv
#: model:ir.model,name:report_csv.model_ir_actions_report
msgid "Report Action"
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
#: selection:ir.actions.report,report_type:0
msgid "Text"
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
#. openerp-web
#: code:addons/report_csv/static/src/js/report/qwebactionmanager.js:52
#, python-format
msgid "Warning"
msgstr ""
#. module: report_csv
#: selection:ir.actions.report,report_type:0
msgid "XLSX"
msgstr ""
#. module: report_csv
#: selection:ir.actions.report,report_type:0
msgid "XML"
msgstr ""
#. module: report_csv
#: selection:ir.actions.report,report_type:0
msgid "csv"
msgstr ""
#. module: report_csv
#: model:ir.model,name:report_csv.model_report_report_csv_partner_csv
msgid "report.report_csv.partner_csv"
msgstr ""

View File

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

View File

@ -0,0 +1,35 @@
# Copyright 2019 Creu Blanca
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class ReportAction(models.Model):
_inherit = "ir.actions.report"
report_type = fields.Selection(selection_add=[("csv", "csv")])
@api.model
def render_csv(self, docids, data):
report_model_name = "report.%s" % self.report_name
report_model = self.env.get(report_model_name)
if report_model is None:
raise UserError(_("%s model was not found" % report_model_name))
return report_model.with_context(
{"active_model": self.model}
).create_csv_report(docids, data)
@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,3 @@
* Enric Tobella <etobella@creublanca.es>
* Jaime Arroyo <jaime.arroyo@creublanca.es>
* Rattapong Chokmasermkul <rattapongc@ecosoft.co.th>

View File

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

View File

@ -0,0 +1,38 @@
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"
/>

View File

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

View File

@ -0,0 +1,61 @@
# 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
_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)
return file_data.read(), "csv"
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,462 @@
<?xml version="1.0" encoding="utf-8" ?>
<!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 0.15.1: http://docutils.sourceforge.net/" />
<title>Base report csv</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/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: grey; } /* 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 {
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" id="base-report-csv">
<h1 class="title">Base report csv</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" 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" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/reporting-engine/tree/13.0-mig-report_csv/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" href="https://translation.odoo-community.org/projects/reporting-engine-13-0-mig-report_csv/reporting-engine-13-0-mig-report_csv-report_csv"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/143/13.0-mig-report_csv"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-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="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<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>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<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 smashing 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:%2013.0-mig-report_csv%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">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Creu Blanca</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<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>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<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/13.0-mig-report_csv/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>
</body>
</html>

View File

@ -0,0 +1,88 @@
// © 2019 Creu Blanca
// License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
odoo.define("report_csv.report", function (require) {
"use strict";
var core = require("web.core");
var ActionManager = require("web.ActionManager");
var framework = require("web.framework");
var session = require("web.session");
var _t = core._t;
ActionManager.include({
_downloadReportCSV: function (url, actions) {
var self = this;
framework.blockUI();
var type = "csv";
var cloned_action = _.clone(actions);
if (_.isUndefined(cloned_action.data) ||
_.isNull(cloned_action.data) ||
(_.isObject(cloned_action.data) && _.isEmpty(cloned_action.data)))
{
if (cloned_action.context.active_ids) {
url += "/" + cloned_action.context.active_ids.join(',');
}
} else {
url += "?options=" + encodeURIComponent(JSON.stringify(cloned_action.data));
url += "&context=" + encodeURIComponent(JSON.stringify(cloned_action.context));
}
return new Promise(function (resolve, reject) {
var blocked = !session.get_file({
url: url,
data: {
data: JSON.stringify([url, type]),
},
success: resolve,
error: (error) => {
self.call('crash_manager', 'rpc_error', error);
reject();
},
complete: framework.unblockUI,
});
if (blocked) {
// AAB: this check should be done in get_file service directly,
// should not be the concern of the caller (and that way, get_file
// could return a deferred)
var message = _t('A popup window with your report was blocked. You ' +
'may need to change your browser settings to allow ' +
'popup windows for this page.');
this.do_warn(_t('Warning'), message, true);
}
});
},
_triggerDownload: function (action, options, type) {
var self = this;
var reportUrls = this._makeReportUrls(action);
if (type === "csv") {
return this._downloadReportCSV(reportUrls[type], action).then(function () {
if (action.close_on_report_download) {
var closeAction = {type: 'ir.actions.act_window_close'};
return self.doAction(closeAction, _.pick(options, 'on_close'));
} else {
return options.on_close();
}
});
}
return this._super.apply(this, arguments);
},
_makeReportUrls: function (action) {
var reportUrls = this._super.apply(this, arguments);
reportUrls.csv = '/report/csv/' + action.report_name;
return reportUrls;
},
_executeReportAction: function (action, options) {
var self = this;
if (action.report_type === 'csv') {
return self._triggerDownload(action, options, 'csv');
}
return this._super.apply(this, arguments);
}
});
});

View File

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

View File

@ -0,0 +1,58 @@
# 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.tests import common
_logger = logging.getLogger(__name__)
try:
import csv
except ImportError:
_logger.debug("Can not import csv.")
class TestReport(common.TransactionCase):
def setUp(self):
super().setUp()
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 = 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:
self.env["ir.actions.report"]._get_report_from_name("TEST")
report = self.report
self.assertEqual(report.report_type, "csv")
rep = report.render(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_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.assertEquals(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.assertEquals(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.assertEquals(objs, self.docs)
# Typical call from render
objs = self.csv_report._get_objs_for_report(self.docs.ids, {})
self.assertEquals(objs, self.docs)

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--
© 2019 Creu Blanca
License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
-->
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/report_csv/static/src/js/report/qwebactionmanager.js"/>
</xpath>
</template>
</odoo>