[MRG] Add report_xls module. A generic module that add to OpenERP the possibility to export report as .XLS file
commit
37f6b50ad6
|
@ -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:
|
Loading…
Reference in New Issue