Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions excel_import_export/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

==========================
Excel Import/Export/Report
==========================
Expand All @@ -17,7 +13,7 @@ Excel Import/Export/Report
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
.. |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
Expand Down Expand Up @@ -230,6 +226,9 @@ Contributors
- Kitti Upariphutthiphong. <kittiu@gmail.com> (http://ecosoft.co.th)
- Saran Lim. <saranl@ecosoft.co.th> (http://ecosoft.co.th)
- Do Anh Duy <duyda@trobz.com>
- `Heliconia Solutions Pvt. Ltd. <https://www.heliconia.io>`__

- Bhavesh Heliconia

Maintainers
-----------
Expand Down
82 changes: 64 additions & 18 deletions excel_import_export/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@
import string
import uuid
from ast import literal_eval
from datetime import date
from datetime import datetime as dt
from io import StringIO
from io import BytesIO, StringIO

import xlrd
from dateutil.parser import parse

from odoo.tools.parse_version import parse_version

try:
from openpyxl import load_workbook
except ImportError:
load_workbook = None

from odoo import _
from odoo.exceptions import ValidationError

Expand Down Expand Up @@ -223,19 +231,57 @@ def str_to_number(input_val):


def csv_from_excel(excel_content, delimiter, quote):
wb = xlrd.open_workbook(file_contents=excel_content)
sh = wb.sheet_by_index(0)
try:
wb = xlrd.open_workbook(file_contents=excel_content)
sh = wb.sheet_by_index(0)
rows_iter = (sh.row_values(rownum) for rownum in range(sh.nrows))
except (
xlrd.XLRDError,
Exception,
): # Fallback to openpyxl if xlrd fails (e.g. xlsx in xlrd>=2.0)
if (
xlrd
and parse_version(xlrd.__VERSION__) >= parse_version("2.0")
and load_workbook
):
# Try openpyxl
try:
wb = load_workbook(
StringIO(excel_content)
if isinstance(excel_content, str)
else BytesIO(excel_content or b""),
data_only=True,
)
sh = wb.worksheets[0]
rows_iter = sh.iter_rows(values_only=True)
except Exception:
raise
else:
raise

content = StringIO()
quoting = csv.QUOTE_ALL
if not quote:
quoting = csv.QUOTE_NONE
if delimiter == " " and quoting == csv.QUOTE_NONE:
quoting = csv.QUOTE_MINIMAL
wr = csv.writer(content, delimiter=delimiter, quoting=quoting)
for rownum in range(sh.nrows):

for row_values in rows_iter:
row = []
for x in sh.row_values(rownum):
if quoting == csv.QUOTE_NONE and delimiter in x:
for x in row_values:
if isinstance(x, dt | date):
is_datetime = False
if isinstance(x, dt) and (
x.hour or x.minute or x.second or x.microsecond
):
is_datetime = True
x = (
x.strftime("%Y-%m-%d %H:%M:%S")
if is_datetime
else x.strftime("%Y-%m-%d")
)
if quoting == csv.QUOTE_NONE and delimiter in str(x):
raise ValidationError(
_(
"Template with CSV Quoting = False, data must not "
Expand Down Expand Up @@ -267,19 +313,19 @@ def _get_cell_value(cell, field_type=False):
if not know, just get value as is"""
value = False
datemode = 0 # From book.datemode, but we fix it for simplicity
if field_type in ["date", "datetime"]:
ctype = xlrd.sheet.ctype_text.get(cell.ctype, "unknown type")
if ctype in ("xldate", "number"):
is_datetime = cell.value % 1 != 0.0
if field_type in ("date", "datetime"):
if hasattr(cell, "ctype") and cell.ctype == xlrd.XL_CELL_DATE:
time_tuple = xlrd.xldate_as_tuple(cell.value, datemode)
date = dt(*time_tuple)
value = (
date.strftime("%Y-%m-%d %H:%M:%S")
if is_datetime
else date.strftime("%Y-%m-%d")
)
value = dt(*time_tuple)
else:
value = cell.value

if isinstance(value, dt | date):
if field_type == "date":
value = value.strftime("%Y-%m-%d")
else:
value = value.strftime("%Y-%m-%d %H:%M:%S")

elif field_type in ["integer", "float"]:
value_str = str(cell.value).strip().replace(",", "")
if len(value_str) == 0:
Expand All @@ -297,8 +343,8 @@ def _get_cell_value(cell, field_type=False):
value = str(cell.value)
else:
value = cell.value
else: # text, char
value = cell.value
else: # text, char, or unknown field_type
value = cell.value if hasattr(cell, "value") else cell
# If string, cleanup
if isinstance(value, str):
if value[-2:] == ".0":
Expand Down
15 changes: 10 additions & 5 deletions excel_import_export/models/xlsx_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def _process_worksheet(
row, col = co.pos2idx(rc)
if is_xlsx: # openpyxl
# openpyxl uses 1-based indexing
cell_value = st.cell(row=row + 1, column=col + 1).value
cell_value = st.cell(row=row + 1, column=col + 1)
else: # xlrd
cell_value = st.cell(row, col)
value = co._get_cell_value(cell_value, field_type=field_type)
Expand Down Expand Up @@ -249,7 +249,7 @@ def _import_record_data(self, import_file, record, data_dict):
out_st.write(0, 0, "id")
out_st.write(1, 0, xml_id)
header_fields = ["id"]
is_xlsx = self._is_xlsx_file()
is_xlsx = self._is_xlsx_file(decoded_data)
wb = self._load_workbook(decoded_data, is_xlsx)
# Process on all worksheets
self._process_worksheet(
Expand Down Expand Up @@ -279,10 +279,15 @@ def _generate_xml_id(self, record):
or "{}.{}".format("__excel_import_export__", uuid.uuid4())
)

def _is_xlsx_file(self):
if xlrd and parse_version(xlrd.__VERSION__) < parse_version("2.0"):
def _is_xlsx_file(self, content):
if not content:
return False
return True
# Check for Zip signature (PK..). Used by .xlsx, .xlsm
if content[:4] == b"\x50\x4b\x03\x04":
if xlrd and parse_version(xlrd.__VERSION__) < parse_version("2.0"):
return False
return True
return False

def _load_workbook(self, decoded_data, is_xlsx):
if not is_xlsx:
Expand Down
2 changes: 2 additions & 0 deletions excel_import_export/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
(<http://ecosoft.co.th>)
- Saran Lim. \<<saranl@ecosoft.co.th>\> (<http://ecosoft.co.th>)
- Do Anh Duy \<<duyda@trobz.com>\>
- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io)
- Bhavesh Heliconia
42 changes: 20 additions & 22 deletions excel_import_export/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Excel Import/Export/Report</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="excel-import-export-report">
<h1 class="title">Excel Import/Export/Report</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="excel-import-export-report">
<h1>Excel Import/Export/Report</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2ff7fc0fdd66cd8a126f345d098845f65ac464b9531d37d76ac1acebb4b46860
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-tools/tree/18.0/excel_import_export"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-excel_import_export"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-tools&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-tools/tree/18.0/excel_import_export"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-excel_import_export"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-tools&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>The module provide pre-built functions and wizards for developer to
build excel import / export / report with ease.</p>
<p>Without having to code to create excel file, developer do,</p>
Expand Down Expand Up @@ -409,22 +404,22 @@ <h1>Excel Import/Export/Report</h1>
</ul>
</div>
<div class="section" id="installation">
<h2><a class="toc-backref" href="#toc-entry-1">Installation</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Installation</a></h1>
<p>To install this module, you need to install following python library,
<strong>xlrd, xlwt, openpyxl</strong>.</p>
<p>Then, simply install <strong>excel_import_export</strong>.</p>
<p>For demo, install <strong>excel_import_export_demo</strong></p>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-2">Configuration</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Configuration</a></h1>
<p>If you have existing templates from the version 16.0.1.2.0 or earlier,
you need to click ‘REMOVE EXPORT ACTION’ and then click ‘ADD EXPORT
ACTION’ in these templates for export actions to work as expected.</p>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-3">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Usage</a></h1>
<div class="section" id="concepts">
<h3><a class="toc-backref" href="#toc-entry-4">Concepts</a></h3>
<h2><a class="toc-backref" href="#toc-entry-4">Concepts</a></h2>
<p>This module contain pre-defined function and wizards to make exporting,
importing and reporting easy.</p>
<p>At the heart of this module, there are 2 main methods</p>
Expand All @@ -442,7 +437,7 @@ <h3><a class="toc-backref" href="#toc-entry-4">Concepts</a></h3>
example use cases, please install <strong>excel_import_export_demo</strong></p>
</div>
<div class="section" id="use-cases">
<h3><a class="toc-backref" href="#toc-entry-5">Use Cases</a></h3>
<h2><a class="toc-backref" href="#toc-entry-5">Use Cases</a></h2>
<p><strong>Use Case 1:</strong> Export/Import Excel on existing document</p>
<p>This add export/import action menus in existing document (example -
excel_import_export_demo/import_export_sale_order)</p>
Expand Down Expand Up @@ -511,7 +506,7 @@ <h3><a class="toc-backref" href="#toc-entry-5">Use Cases</a></h3>
</ol>
</div>
<div class="section" id="easy-reporting-option">
<h3><a class="toc-backref" href="#toc-entry-6">Easy Reporting Option</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Easy Reporting Option</a></h2>
<p>Technically, this option is the same as “Create Excel Report” use case.
But instead of having to write XML / Python code like normally do, this
option allow user to create a report based on a model or view, all by
Expand Down Expand Up @@ -541,38 +536,42 @@ <h3><a class="toc-backref" href="#toc-entry-6">Easy Reporting Option</a></h3>
</div>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-7">Known issues / Roadmap</a></h2>
<h1><a class="toc-backref" href="#toc-entry-7">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Module extension e.g., excel_import_export_async, that add ability to
execute as async process.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-8">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-8">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-tools/issues/new?body=module:%20excel_import_export%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-9">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-9">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-10">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-10">Authors</a></h2>
<ul class="simple">
<li>Ecosoft</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-11">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-11">Contributors</a></h2>
<ul class="simple">
<li>Kitti Upariphutthiphong. &lt;<a class="reference external" href="mailto:kittiu&#64;gmail.com">kittiu&#64;gmail.com</a>&gt; (<a class="reference external" href="http://ecosoft.co.th">http://ecosoft.co.th</a>)</li>
<li>Saran Lim. &lt;<a class="reference external" href="mailto:saranl&#64;ecosoft.co.th">saranl&#64;ecosoft.co.th</a>&gt; (<a class="reference external" href="http://ecosoft.co.th">http://ecosoft.co.th</a>)</li>
<li>Do Anh Duy &lt;<a class="reference external" href="mailto:duyda&#64;trobz.com">duyda&#64;trobz.com</a>&gt;</li>
<li><a class="reference external" href="https://www.heliconia.io">Heliconia Solutions Pvt. Ltd.</a><ul>
<li>Bhavesh Heliconia</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-12">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-12">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" />
Expand All @@ -587,6 +586,5 @@ <h3><a class="toc-backref" href="#toc-entry-12">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>