Browse Source
[MRG] Add report_xls module. A generic module that add to OpenERP the possibility to export report as .XLS file
6.1
[MRG] Add report_xls module. A generic module that add to OpenERP the possibility to export report as .XLS file
6.1
Luc De Meyer
11 years ago
committed by
Joel Grand-Guillaume
4 changed files with 341 additions and 0 deletions
-
24report_xls/__init__.py
-
44report_xls/__openerp__.py
-
224report_xls/report_xls.py
-
49report_xls/utils.py
@ -0,0 +1,24 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# |
|||
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from . import report_xls |
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,44 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# |
|||
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
{ |
|||
'name': 'XLS report engine', |
|||
'version': '0.3', |
|||
'license': 'AGPL-3', |
|||
'author': 'Noviat', |
|||
'website': 'http://www.noviat.com', |
|||
'category': 'Reporting', |
|||
'description': """ |
|||
|
|||
This module adds XLS export capabilities to the standard OpenERP reporting engine. |
|||
|
|||
In order to generate an XLS export you can define a report of type 'xls' or alternatively pass {'xls_export' : 1) via the context to create method of the report. |
|||
|
|||
""", |
|||
'depends': ['base'], |
|||
'external_dependencies': {'python': ['xlwt']}, |
|||
'demo_xml': [], |
|||
'init_xml': [], |
|||
'update_xml' : [], |
|||
'active': False, |
|||
'installable': True, |
|||
} |
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,224 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# |
|||
#Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import xlwt |
|||
from xlwt.Style import default_style |
|||
import cStringIO |
|||
import datetime, time |
|||
import inspect |
|||
from types import CodeType |
|||
from openerp.report.report_sxw import * |
|||
from openerp import pooler |
|||
from openerp.tools.translate import translate, _ |
|||
import logging |
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
class AttrDict(dict): |
|||
def __init__(self, *args, **kwargs): |
|||
super(AttrDict, self).__init__(*args, **kwargs) |
|||
self.__dict__ = self |
|||
|
|||
class report_xls(report_sxw): |
|||
|
|||
xls_types = { |
|||
'bool': xlwt.Row.set_cell_boolean, |
|||
'date': xlwt.Row.set_cell_date, |
|||
'text': xlwt.Row.set_cell_text, |
|||
'number': xlwt.Row.set_cell_number, |
|||
} |
|||
xls_types_default = { |
|||
'bool': False, |
|||
'date': None, |
|||
'text': '', |
|||
'number': 0, |
|||
} |
|||
|
|||
# TO DO: move parameters infra to configurable data |
|||
|
|||
# header/footer |
|||
DT_FORMAT = '%Y-%m-%d %H:%M:%S' |
|||
hf_params = { |
|||
'font_size': 8, |
|||
'font_style': 'I', # B: Bold, I: Italic, U: Underline |
|||
} |
|||
xls_headers = { |
|||
'standard': '' |
|||
} |
|||
xls_footers = { |
|||
'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DT_FORMAT) + |
|||
'&R&%(font_size)s&%(font_style)s&P / &N') %hf_params |
|||
} |
|||
|
|||
# styles |
|||
_pfc = '26' # default pattern fore_color |
|||
_bc = '22' # borders color |
|||
decimal_format = '#,##0.00' |
|||
date_format = 'YYYY-MM-DD' |
|||
xls_styles = { |
|||
'xls_title': 'font: bold true, height 240;', |
|||
'bold': 'font: bold true;', |
|||
'underline': 'font: underline true;', |
|||
'italic': 'font: italic true;', |
|||
'fill': 'pattern: pattern solid, fore_color %s;' %_pfc, |
|||
'fill_blue' : 'pattern: pattern solid, fore_color 27;', |
|||
'fill_grey' : 'pattern: pattern solid, fore_color 22;', |
|||
'borders_all': 'borders: left thin, right thin, top thin, bottom thin, ' \ |
|||
'left_colour %s, right_colour %s, top_colour %s, bottom_colour %s;' %(_bc,_bc,_bc,_bc), |
|||
'left': 'align: horz left;', |
|||
'center': 'align: horz center;', |
|||
'right': 'align: horz right;', |
|||
'wrap': 'align: wrap true;', |
|||
'top': 'align: vert top;', |
|||
'bottom': 'align: vert bottom;', |
|||
} |
|||
# TO DO: move parameters supra to configurable data |
|||
|
|||
def create(self, cr, uid, ids, data, context=None): |
|||
self.pool = pooler.get_pool(cr.dbname) |
|||
self.cr = cr |
|||
self.uid = uid |
|||
report_obj = self.pool.get('ir.actions.report.xml') |
|||
report_ids = report_obj.search(cr, uid, |
|||
[('report_name', '=', self.name[7:])], context=context) |
|||
if report_ids: |
|||
report_xml = report_obj.browse(cr, uid, report_ids[0], context=context) |
|||
self.title = report_xml.name |
|||
if report_xml.report_type == 'xls': |
|||
return self.create_source_xls(cr, uid, ids, data, context) |
|||
elif context.get('xls_export'): |
|||
return self.create_source_xls(cr, uid, ids, data, context) |
|||
return super(report_xls, self).create(cr, uid, ids, data, context) |
|||
|
|||
def create_source_xls(self, cr, uid, ids, data, context=None): |
|||
if not context: context = {} |
|||
parser_instance = self.parser(cr, uid, self.name2, context) |
|||
self.parser_instance = parser_instance |
|||
objs = self.getObjects(cr, uid, ids, context) |
|||
parser_instance.set_context(objs, data, ids, 'xls') |
|||
objs = parser_instance.localcontext['objects'] |
|||
n = cStringIO.StringIO() |
|||
wb = xlwt.Workbook(encoding='utf-8') |
|||
_p = AttrDict(parser_instance.localcontext) |
|||
_xs = self.xls_styles |
|||
self.generate_xls_report(_p, _xs, data, objs, wb) |
|||
wb.save(n) |
|||
n.seek(0) |
|||
return (n.read(), 'xls') |
|||
|
|||
def render(self, wanted, col_specs, rowtype, render_space='empty'): |
|||
""" |
|||
returns 'mako'-rendered col_specs |
|||
|
|||
Input: |
|||
- wanted: element from the wanted_list |
|||
- col_specs : cf. specs[1:] documented in xls_row_template method |
|||
- rowtype : 'header' or 'data' |
|||
- render_space : type dict, (caller_space + localcontext) if not specified |
|||
""" |
|||
if render_space == 'empty': |
|||
render_space = {} |
|||
caller_space = inspect.currentframe().f_back.f_back.f_locals |
|||
localcontext = self.parser_instance.localcontext |
|||
render_space.update(caller_space) |
|||
render_space.update(localcontext) |
|||
row = col_specs[wanted][rowtype][:] |
|||
for i in range(len(row)): |
|||
if isinstance(row[i], CodeType): |
|||
row[i] = eval(row[i], render_space) |
|||
row.insert(0, wanted) |
|||
#_logger.warn('row O = %s', row) |
|||
return row |
|||
|
|||
def generate_xls_report(self, parser, xls_styles, data, objects, wb): |
|||
""" override this method to create your excel file """ |
|||
raise NotImplementedError() |
|||
|
|||
def xls_row_template(self, specs, wanted_list): |
|||
""" |
|||
Returns a row template. |
|||
|
|||
Input : |
|||
- 'wanted_list': list of Columns that will be returned in the row_template |
|||
- 'specs': list with Column Characteristics |
|||
0: Column Name (from wanted_list) |
|||
1: Column Colspan |
|||
2: Column Size (unit = the width of the character ’0′ as it appears in the sheet’s default font) |
|||
3: Column Type |
|||
4: Column Data |
|||
5: Column Formula (or 'None' for Data) |
|||
6: Column Style |
|||
""" |
|||
r = [] |
|||
col = 0 |
|||
for w in wanted_list: |
|||
found = False |
|||
for s in specs: |
|||
if s[0] == w: |
|||
found = True |
|||
s_len = len(s) |
|||
c = list(s[:5]) |
|||
# set write_cell_func or formula |
|||
if s_len > 5 and s[5] is not None: |
|||
c.append({'formula': s[5]}) |
|||
else: |
|||
c.append({'write_cell_func': report_xls.xls_types[c[3]]}) |
|||
# Set custom cell style |
|||
if s_len > 6 and s[6] is not None: |
|||
c.append(s[6]) |
|||
else: |
|||
c.append(None) |
|||
# Set cell formula |
|||
if s_len > 7 and s[7] is not None: |
|||
c.append(s[7]) |
|||
else: |
|||
c.append(None) |
|||
r.append((col, c[1], c)) |
|||
col += c[1] |
|||
break |
|||
if not found: |
|||
_logger.warn("report_xls.xls_row_template, column '%s' not found in specs", w) |
|||
return r |
|||
|
|||
def xls_write_row(self, ws, row_pos, row_data, row_style=default_style, set_column_size=False): |
|||
r = ws.row(row_pos) |
|||
for col, size, spec in row_data: |
|||
data = spec[4] |
|||
formula = spec[5].get('formula') and xlwt.Formula(spec[5]['formula']) or None |
|||
style = spec[6] and spec[6] or row_style |
|||
if not data: |
|||
# if no data, use default values |
|||
data = report_xls.xls_types_default[spec[3]] |
|||
if size != 1: |
|||
if formula: |
|||
ws.write_merge(row_pos, row_pos, col, col+size-1, data, style) |
|||
else: |
|||
ws.write_merge(row_pos, row_pos, col, col+size-1, data, style) |
|||
else: |
|||
if formula: |
|||
ws.write(row_pos, col, formula, style) |
|||
else: |
|||
spec[5]['write_cell_func'](r, col, data, style) |
|||
if set_column_size: |
|||
ws.col(col).width = spec[2] * 256 |
|||
return row_pos + 1 |
|||
|
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
@ -0,0 +1,49 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# |
|||
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
# |
|||
|
|||
def _render(code): |
|||
return compile(code, '<string>', 'eval') |
|||
|
|||
def rowcol_to_cell(row, col, row_abs=False, col_abs=False): |
|||
# Code based upon utils from xlwt distribution |
|||
""" |
|||
Convert numeric row/col notation to an Excel cell reference string in A1 notation. |
|||
""" |
|||
d = col // 26 |
|||
m = col % 26 |
|||
chr1 = "" # Most significant character in AA1 |
|||
if row_abs: |
|||
row_abs = '$' |
|||
else: |
|||
row_abs = '' |
|||
if col_abs: |
|||
col_abs = '$' |
|||
else: |
|||
col_abs = '' |
|||
if d > 0: |
|||
chr1 = chr(ord('A') + d - 1) |
|||
chr2 = chr(ord('A') + m) |
|||
# Zero index to 1-index |
|||
return col_abs + chr1 + chr2 + row_abs + str(row + 1) |
|||
|
|||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
Write
Preview
Loading…
Cancel
Save
Reference in new issue