Merge PR #582 into 15.0

Signed-off-by simahawk
This commit is contained in:
OCA-git-bot
2022-02-02 07:44:35 +00:00
17 changed files with 977 additions and 0 deletions

View File

@@ -1,4 +1,5 @@
# generated from manifests external_dependencies
lxml
openpyxl
xlrd
xlsxwriter

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%2Fserver--tools-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/149/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": "15.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,
}

View File

@@ -0,0 +1,154 @@
# 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__changeset_change_ids
msgid "Changeset Changes"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__changeset_ids
msgid "Changesets"
msgstr ""
#. 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,field_description:sql_export_excel.field_sql_export__count_pending_changeset_changes
msgid "Count Pending Changeset Changes"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__count_pending_changesets
msgid "Count Pending Changesets"
msgstr ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__display_name
msgid "Display Name"
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,field_description:sql_export_excel.field_sql_export__id
msgid "ID"
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____last_update
msgid "Last Modified on"
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
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__smart_search
msgid "Smart Search"
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 ""
#. module: sql_export_excel
#: model:ir.model.fields,field_description:sql_export_excel.field_sql_export__user_can_see_changeset
msgid "User Can See Changeset"
msgstr ""

View File

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

View File

@@ -0,0 +1,119 @@
# 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):
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%2Fserver--tools-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/149/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>