Merge PR #698 into 16.0

Signed-off-by legalsylvain
This commit is contained in:
OCA-git-bot
2023-09-25 10:09:44 +00:00
18 changed files with 1057 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
# generated from manifests external_dependencies
lxml
openpyxl
py3o.formats
py3o.template
PyPDF2

View File

@@ -0,0 +1 @@
../../../../sql_export_excel

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@@ -0,0 +1,85 @@
================
SQL Export Excel
================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! 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/15.0/sql_export_excel
: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-15-0/reporting-engine-15-0-sql_export_excel
: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/15.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
Add the possibility to extract data from a sql query toward an excel file.
It is also possible to provide an template excel file for a query. In this case,
the data will be inserted in the specified sheet of the provided excel file. This
is usefull when doing a lot of calculation in excel and the data is coming from Odoo.
**Table of contents**
.. contents::
:local:
Configuration
=============
If you want Odoo to update an existing excel file, you should create an attachment
with the excel file and configure this attachment on the query.
Then, you can configure the query to indicate if Odoo should export the header and where it should
insert the data. By default, it will insert it in the first sheet, at first row/column.
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:%20sql_export_excel%0Aversion:%2015.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
~~~~~~~
* Akretion
Contributors
~~~~~~~~~~~~
* Florian da Costa <florian.dacosta@akretion.com>
* Helly kapatel <helly.kapatel@initos.com>
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/15.0/sql_export_excel>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

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

View File

@@ -0,0 +1,22 @@
# Copyright 2019 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "SQL Export Excel",
"version": "16.0.1.0.0",
"author": "Akretion,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/reporting-engine",
"license": "AGPL-3",
"category": "Generic Modules/Others",
"summary": "Allow to export a sql query to an excel file.",
"depends": ["sql_export"],
"external_dependencies": {
"python": [
"openpyxl",
],
},
"data": [
"views/sql_export_view.xml",
],
"installable": True,
}

122
sql_export_excel/i18n/ca.po Normal file
View File

@@ -0,0 +1,122 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sql_export_excel
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2022-06-15 18:05+0000\n"
"Last-Translator: jabelchi <jabelchi@gmail.com>\n"
"Language-Team: none\n"
"Language: ca\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.3.2\n"
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__col_position
msgid "Column Position"
msgstr "Posició columna"
#. module: sql_export_excel
#: model:ir.model.fields.selection,name:sql_export_excel.selection__sql_export__file_format__excel
msgid "Excel"
msgstr "Excel"
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__attachment_id
msgid "Excel Template"
msgstr "Plantilla Excel"
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__file_format
msgid "File Format"
msgstr "Format de fitxer"
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__header
msgid "Header"
msgstr "Capçalera"
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__attachment_id
msgid ""
"If you configure an excel file (in xlsx format) here, the result of the query will be injected in it.\n"
"It is usefull to feed data in a excel file pre-configured with calculation"
msgstr ""
"Si configureu aquí un fitxer Excel (en format xlsx), s'hi injectarà el "
"resultat de la consulta.\n"
"Això és útil per a carregar dades en un fitxer Excel pre-configurat amb "
"càlculs"
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__col_position
msgid "Indicate from which column the result of the query should be injected."
msgstr ""
"Indiqueu des de quina columna ha de carregar-se el resultat de la consulta."
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__row_position
msgid "Indicate from which row the result of the query should be injected."
msgstr ""
"Indiqueu des de quina fila ha de carregar-se el resultat de la consulta."
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__header
msgid "Indicate if the header should be exported to the file."
msgstr "Indiqueu si la capçalera ha d'exportar-se al fitxer."
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__sheet_position
msgid ""
"Indicate the sheet's position of the excel template where the result of the "
"sql query should be injected."
msgstr ""
"Indiqueu la posició del full de la plantilla Excel on s'ha de carregar el "
"resultat de la consulta SQL."
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__row_position
msgid "Row Position"
msgstr "Posició fila"
#. module: sql_export_excel
#: model:ir.model,name:sql_export_excel.model_sql_export
msgid "SQL export"
msgstr "Exportació SQL"
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__sheet_position
msgid "Sheet Position"
msgstr "Posició del full"
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid ""
"The Excel Template file contains less than %s sheets Please, adjust the "
"Sheet Position parameter."
msgstr ""
"La plantilla Excel conté menys de %s fulls. Si us plau, corregiu el "
"paràmetre de la posició del full."
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid "The column position can't be less than 1."
msgstr "La posició de la columna no pot ser menor que 1."
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid "The row position can't be less than 1."
msgstr "La posició de la fila no pot ser menor que 1."
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid "The sheet position can't be less than 1."
msgstr "La posició del full no pot ser menor que 1."

View File

@@ -0,0 +1,109 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sql_export_excel
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.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: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__col_position
msgid "Column Position"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields.selection,name:sql_export_excel.selection__sql_export__file_format__excel
msgid "Excel"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__attachment_id
msgid "Excel Template"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__file_format
msgid "File Format"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__header
msgid "Header"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__attachment_id
msgid ""
"If you configure an excel file (in xlsx format) here, the result of the query will be injected in it.\n"
"It is usefull to feed data in a excel file pre-configured with calculation"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__col_position
msgid "Indicate from which column the result of the query should be injected."
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__row_position
msgid "Indicate from which row the result of the query should be injected."
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__header
msgid "Indicate if the header should be exported to the file."
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,help:sql_export_excel.field_sql_export__sheet_position
msgid ""
"Indicate the sheet's position of the excel template where the result of the "
"sql query should be injected."
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__row_position
msgid "Row Position"
msgstr ""
#. module: sql_export_excel
#: model:ir.model,name:sql_export_excel.model_sql_export
msgid "SQL export"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__sheet_position
msgid "Sheet Position"
msgstr ""
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid ""
"The Excel Template file contains less than %s sheets Please, adjust the "
"Sheet Position parameter."
msgstr ""
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid "The column position can't be less than 1."
msgstr ""
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid "The row position can't be less than 1."
msgstr ""
#. module: sql_export_excel
#: code:addons/sql_export_excel/models/sql_export.py:0
#, python-format
msgid "The sheet position can't be less than 1."
msgstr ""

View File

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

View File

@@ -0,0 +1,122 @@
# Copyright 2019 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import base64
import logging
from io import BytesIO
from odoo import _, api, exceptions, fields, models
_logger = logging.getLogger(__name__)
try:
import openpyxl
except ImportError:
_logger.debug("Can not import openpyxl")
class SqlExport(models.Model):
_inherit = "sql.export"
file_format = fields.Selection(
selection_add=[("excel", "Excel")], ondelete={"excel": "set default"}
)
header = fields.Boolean(
default=True, help="Indicate if the header should be exported to the file."
)
attachment_id = fields.Many2one(
"ir.attachment",
string="Excel Template",
help="If you configure an excel file (in xlsx format) here, the "
"result of the query will be injected in it.\nIt is usefull to "
"feed data in a excel file pre-configured with calculation",
)
sheet_position = fields.Integer(
default=1,
help="Indicate the sheet's position of the excel template where the "
"result of the sql query should be injected.",
)
row_position = fields.Integer(
default=1,
help="Indicate from which row the result of the query should be " "injected.",
)
col_position = fields.Integer(
string="Column Position",
default=1,
help="Indicate from which column the result of the query should be "
"injected.",
)
@api.constrains("sheet_position")
def check_sheet_position(self):
for export in self:
if export.sheet_position < 1:
raise exceptions.ValidationError(
_("The sheet position can't be less than 1.")
)
@api.constrains("row_position")
def check_row_position(self):
for export in self:
if export.row_position < 1:
raise exceptions.ValidationError(
_("The row position can't be less than 1.")
)
@api.constrains("col_position")
def check_column_position(self):
for export in self:
if export.col_position < 1:
raise exceptions.ValidationError(
_("The column position can't be less than 1.")
)
def _get_file_extension(self):
self.ensure_one()
if self.file_format == "excel":
return "xlsx"
else:
return super()._get_file_extension()
def excel_get_data_from_query(self, variable_dict):
self.ensure_one()
res = self._execute_sql_request(
params=variable_dict, mode="fetchall", header=self.header
)
# Case we insert data in an existing excel file.
if self.attachment_id:
datas = self.attachment_id.datas
infile = BytesIO()
infile.write(base64.b64decode(datas))
infile.seek(0)
wb = openpyxl.load_workbook(filename=infile)
sheets = wb.worksheets
try:
ws = sheets[self.sheet_position - 1]
except IndexError as err:
raise exceptions.ValidationError(
_(
"The Excel Template file contains less than %s sheets "
"Please, adjust the Sheet Position parameter."
)
) from err
row_position = self.row_position or 1
col_position = self.col_position or 1
# Case of excel file creation
else:
wb = openpyxl.Workbook()
ws = wb.active
row_position = 1
col_position = 1
for index, row in enumerate(res, row_position):
for col, val in enumerate(row, col_position):
# manage jsonb field as dict are not writable on the excel cell
if isinstance(val, dict):
val = str(val)
ws.cell(row=index, column=col).value = val
output = BytesIO()
wb.save(output)
output.getvalue()
output_datas = base64.b64encode(output.getvalue())
output.close()
return output_datas

View File

@@ -0,0 +1,4 @@
If you want Odoo to update an existing excel file, you should create an attachment
with the excel file and configure this attachment on the query.
Then, you can configure the query to indicate if Odoo should export the header and where it should
insert the data. By default, it will insert it in the first sheet, at first row/column.

View File

@@ -0,0 +1,2 @@
* Florian da Costa <florian.dacosta@akretion.com>
* Helly kapatel <helly.kapatel@initos.com>

View File

@@ -0,0 +1,4 @@
Add the possibility to extract data from a sql query toward an excel file.
It is also possible to provide an template excel file for a query. In this case,
the data will be inserted in the specified sheet of the provided excel file. This
is usefull when doing a lot of calculation in excel and the data is coming from Odoo.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,431 @@
<?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>SQL Export Excel</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="sql-export-excel">
<h1 class="title">SQL Export Excel</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/15.0/sql_export_excel"><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-15-0/reporting-engine-15-0-sql_export_excel"><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/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>Add the possibility to extract data from a sql query toward an excel file.
It is also possible to provide an template excel file for a query. In this case,
the data will be inserted in the specified sheet of the provided excel file. This
is usefull when doing a lot of calculation in excel and the data is coming from Odoo.</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="id1">Configuration</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="configuration">
<h1><a class="toc-backref" href="#id1">Configuration</a></h1>
<p>If you want Odoo to update an existing excel file, you should create an attachment
with the excel file and configure this attachment on the query.
Then, you can configure the query to indicate if Odoo should export the header and where it should
insert the data. By default, it will insert it in the first sheet, at first row/column.</p>
</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:%20sql_export_excel%0Aversion:%2015.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">
<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>Akretion</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Florian da Costa &lt;<a class="reference external" href="mailto:florian.dacosta&#64;akretion.com">florian.dacosta&#64;akretion.com</a>&gt;</li>
<li>Helly kapatel &lt;<a class="reference external" href="mailto:helly.kapatel&#64;initos.com">helly.kapatel&#64;initos.com</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/15.0/sql_export_excel">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 @@
from . import test_sql_query_excel

View File

@@ -0,0 +1,112 @@
# Copyright (C) 2019 Akretion (<http://www.akretion.com>)
# @author: Florian da Costa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import base64
import logging
from io import BytesIO
from odoo.tests.common import TransactionCase
_logger = logging.getLogger(__name__)
try:
import openpyxl
except ImportError:
_logger.debug("Can not import openpyxl")
class TestExportSqlQueryExcel(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.wizard_obj = cls.env["sql.file.wizard"]
def get_workbook_from_query(self, wizard):
wizard.export_sql()
decoded_data = base64.b64decode(wizard.binary_file)
xlsx_file = BytesIO(decoded_data)
return openpyxl.load_workbook(xlsx_file)
def test_excel_file_generation(self):
test_query = "SELECT 'testcol1' as firstcol, 2 as second_col"
query_vals = {
"name": "Test Query Excel",
"query": test_query,
"file_format": "excel",
}
query = self.env["sql.export"].create(query_vals)
query.button_validate_sql_expression()
wizard = self.wizard_obj.create(
{
"sql_export_id": query.id,
}
)
workbook = self.get_workbook_from_query(wizard)
ws = workbook.active
# Check values, header should be here by default
self.assertEqual(ws.cell(row=1, column=1).value, "firstcol")
self.assertEqual(ws.cell(row=2, column=1).value, "testcol1")
self.assertEqual(ws.cell(row=2, column=2).value, 2)
query.write({"header": False})
wb2 = self.get_workbook_from_query(wizard)
ws2 = wb2.active
# Check values, the header should not be present
self.assertEqual(ws2.cell(row=1, column=1).value, "testcol1")
self.assertEqual(ws2.cell(row=1, column=2).value, 2)
def test_excel_file_insert(self):
# Create excel file with 2 sheets. Create a header in second sheet
# where data will be inserted
wb = openpyxl.Workbook()
ws = wb.active
ws.cell(row=1, column=1, value="My Test Value")
ws2 = wb.create_sheet("data")
ws2.cell(row=1, column=1, value="Partner Id")
ws2.cell(row=1, column=2, value="Partner Name")
output = BytesIO()
wb.save(output)
data = output.getvalue()
# Create attachment with the created xlsx file which will be used as
# template in the sql query
attachmnent_vals = {
"name": "template xlsx sql export Res Partner",
"datas": base64.b64encode(data),
}
attachment = self.env["ir.attachment"].create(attachmnent_vals)
# Create the query and configure it to insert the data in the second
# sheet of the xlsx template file and start inserting data at the
# second row, ignoring header (because the template excel file
# already contains a header)
test_query = "SELECT id, name FROM res_partner"
query_vals = {
"name": "Test Query Excel",
"query": test_query,
"file_format": "excel",
"attachment_id": attachment.id,
"sheet_position": 2,
"header": False,
"row_position": 2,
}
query = self.env["sql.export"].create(query_vals)
query.button_validate_sql_expression()
wizard = self.wizard_obj.create(
{
"sql_export_id": query.id,
}
)
# Check the generated excel file. The first sheet should still contain
# the same data and the second sheet should have kept the header and
# inserted data from the query
wb2 = self.get_workbook_from_query(wizard)
sheets = wb2.worksheets
ws1 = sheets[0]
# Check values, header should be here by default
self.assertEqual(ws1.cell(row=1, column=1).value, "My Test Value")
ws2 = sheets[1]
self.assertEqual(ws2.cell(row=1, column=1).value, "Partner Id")
self.assertTrue(ws2.cell(row=2, column=1).value)

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="sql_export_excel_view_form" model="ir.ui.view">
<field name="model">sql.export</field>
<field name="inherit_id" ref="sql_export.sql_export_view_form" />
<field name="arch" type="xml">
<field name="file_format" position="after">
<field
name="header"
attrs="{'invisible': [('file_format', '=', 'csv')]}"
/>
<field
name="attachment_id"
attrs="{'invisible': [('file_format', '!=', 'excel')]}"
/>
<field
name="sheet_position"
attrs="{'invisible': [('attachment_id', '=', False)]}"
/>
<field
name="row_position"
attrs="{'invisible': [('attachment_id', '=', False)]}"
/>
<field
name="col_position"
attrs="{'invisible': [('attachment_id', '=', False)]}"
/>
</field>
</field>
</record>
</odoo>