Browse Source

style & documentation refresh, xls footer datetime fix

pull/3/head
Luc De Meyer 11 years ago
committed by Guewen Baconnier
parent
commit
5cdab8dba8
  1. 2
      report_xls/__init__.py
  2. 58
      report_xls/__openerp__.py
  3. 83
      report_xls/report_xls.py
  4. BIN
      report_xls/static/src/img/icon.png
  5. 11
      report_xls/utils.py

2
report_xls/__init__.py

@ -12,7 +12,7 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License

58
report_xls/__openerp__.py

@ -12,7 +12,7 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
@ -20,24 +20,60 @@
# #
############################################################################## ##############################################################################
{ {
'name': 'XLS report engine',
'version': '0.3',
'name': 'Excel report engine',
'version': '0.4',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': 'Noviat', 'author': 'Noviat',
'website': 'http://www.noviat.com', 'website': 'http://www.noviat.com',
'category': 'Reporting', 'category': 'Reporting',
'description': """
'description': """
Excel report engine
===================
This module adds Excel export capabilities to the standard OpenERP reporting engine.
Report development
''''''''''''''''''
In order to create an Excel report you can\n
- define a report of type 'xls'
- pass ``{'xls_export': 1}`` via the context to the report create method
The ``report_xls`` class contains a number of attributes and methods to facilitate
the creation XLS reports in OpenERP.
* cell types
Supported cell types : text, number, boolean, date.
* cell styles
The predefined cell style definitions result in a consistent
look and feel of the OpenERP Excel reports.
* cell formulas
Cell formulas can be easily added with the help of the ``rowcol_to_cell()`` function which
you can import from the ``utils.py`` module.
* Excel templates
It is possible to define Excel templates which can be adapted by 'inherited' modules.
Download the ``account_move_line_report_xls`` module from http://apps.openerp.com
as example.
* XLS with multiple sheets
Download the ``account_journal_report_xls`` module from http://apps.openerp.com as example.
Development assistance
''''''''''''''''''''''
Contact info@noviat.com for help with the development of Excel reports in OpenERP, .
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'], 'depends': ['base'],
'external_dependencies': {'python': ['xlwt']}, 'external_dependencies': {'python': ['xlwt']},
'demo_xml': [],
'init_xml': [],
'update_xml' : [],
'demo': [],
'data': [],
'active': False, 'active': False,
'installable': True, 'installable': True,
} }

83
report_xls/report_xls.py

@ -2,8 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
#
#Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
#
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -12,7 +12,7 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
@ -23,7 +23,8 @@
import xlwt import xlwt
from xlwt.Style import default_style from xlwt.Style import default_style
import cStringIO import cStringIO
import datetime, time
from datetime import datetime
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
import inspect import inspect
from types import CodeType from types import CodeType
from openerp.report.report_sxw import * from openerp.report.report_sxw import *
@ -32,13 +33,15 @@ from openerp.tools.translate import translate, _
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class AttrDict(dict): class AttrDict(dict):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs) super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self self.__dict__ = self
class report_xls(report_sxw): class report_xls(report_sxw):
xls_types = { xls_types = {
'bool': xlwt.Row.set_cell_boolean, 'bool': xlwt.Row.set_cell_boolean,
'date': xlwt.Row.set_cell_date, 'date': xlwt.Row.set_cell_date,
@ -53,36 +56,29 @@ class report_xls(report_sxw):
} }
# TO DO: move parameters infra to configurable data # TO DO: move parameters infra to configurable data
# header/footer # header/footer
DT_FORMAT = '%Y-%m-%d %H:%M:%S'
DT_FORMAT = '%Y-%m-%d %H:%M:%S'
hf_params = { hf_params = {
'font_size': 8, 'font_size': 8,
'font_style': 'I', # B: Bold, I: Italic, U: Underline
}
xls_headers = {
'standard': ''
'font_style': 'I', # B: Bold, I: Italic, U: Underline
} }
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 # styles
_pfc = '26' # default pattern fore_color
_bc = '22' # borders color
_pfc = '26' # default pattern fore_color
_bc = '22' # borders color
decimal_format = '#,##0.00' decimal_format = '#,##0.00'
date_format = 'YYYY-MM-DD'
date_format = 'YYYY-MM-DD'
xls_styles = { xls_styles = {
'xls_title': 'font: bold true, height 240;', 'xls_title': 'font: bold true, height 240;',
'bold': 'font: bold true;', 'bold': 'font: bold true;',
'underline': 'font: underline true;', 'underline': 'font: underline true;',
'italic': 'font: italic 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),
'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;', 'left': 'align: horz left;',
'center': 'align: horz center;', 'center': 'align: horz center;',
'right': 'align: horz right;', 'right': 'align: horz right;',
@ -91,8 +87,8 @@ class report_xls(report_sxw):
'bottom': 'align: vert bottom;', 'bottom': 'align: vert bottom;',
} }
# TO DO: move parameters supra to configurable data # TO DO: move parameters supra to configurable data
def create(self, cr, uid, ids, data, context=None):
def create(self, cr, uid, ids, data, context=None):
self.pool = pooler.get_pool(cr.dbname) self.pool = pooler.get_pool(cr.dbname)
self.cr = cr self.cr = cr
self.uid = uid self.uid = uid
@ -105,11 +101,13 @@ class report_xls(report_sxw):
if report_xml.report_type == 'xls': if report_xml.report_type == 'xls':
return self.create_source_xls(cr, uid, ids, data, context) return self.create_source_xls(cr, uid, ids, data, context)
elif context.get('xls_export'): elif context.get('xls_export'):
self.table = data.get('model') or self.table # use model from 'data' when no ir.actions.report.xml entry
return self.create_source_xls(cr, uid, ids, data, context) return self.create_source_xls(cr, uid, ids, data, context)
return super(report_xls, self).create(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): def create_source_xls(self, cr, uid, ids, data, context=None):
if not context: context = {}
if not context:
context = {}
parser_instance = self.parser(cr, uid, self.name2, context) parser_instance = self.parser(cr, uid, self.name2, context)
self.parser_instance = parser_instance self.parser_instance = parser_instance
objs = self.getObjects(cr, uid, ids, context) objs = self.getObjects(cr, uid, ids, context)
@ -119,15 +117,22 @@ class report_xls(report_sxw):
wb = xlwt.Workbook(encoding='utf-8') wb = xlwt.Workbook(encoding='utf-8')
_p = AttrDict(parser_instance.localcontext) _p = AttrDict(parser_instance.localcontext)
_xs = self.xls_styles _xs = self.xls_styles
self.xls_headers = {
'standard': '',
}
self.xls_footers = {
'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT) +
'&R&%(font_size)s&%(font_style)s&P / &N') % self.hf_params,
}
self.generate_xls_report(_p, _xs, data, objs, wb) self.generate_xls_report(_p, _xs, data, objs, wb)
wb.save(n) wb.save(n)
n.seek(0) n.seek(0)
return (n.read(), 'xls')
return (n.read(), 'xls')
def render(self, wanted, col_specs, rowtype, render_space='empty'): def render(self, wanted, col_specs, rowtype, render_space='empty'):
""" """
returns 'mako'-rendered col_specs
returns 'evaluated' col_specs
Input: Input:
- wanted: element from the wanted_list - wanted: element from the wanted_list
- col_specs : cf. specs[1:] documented in xls_row_template method - col_specs : cf. specs[1:] documented in xls_row_template method
@ -139,7 +144,7 @@ class report_xls(report_sxw):
caller_space = inspect.currentframe().f_back.f_back.f_locals caller_space = inspect.currentframe().f_back.f_back.f_locals
localcontext = self.parser_instance.localcontext localcontext = self.parser_instance.localcontext
render_space.update(caller_space) render_space.update(caller_space)
render_space.update(localcontext)
render_space.update(localcontext)
row = col_specs[wanted][rowtype][:] row = col_specs[wanted][rowtype][:]
for i in range(len(row)): for i in range(len(row)):
if isinstance(row[i], CodeType): if isinstance(row[i], CodeType):
@ -155,9 +160,9 @@ class report_xls(report_sxw):
def xls_row_template(self, specs, wanted_list): def xls_row_template(self, specs, wanted_list):
""" """
Returns a row template. Returns a row template.
Input : Input :
- 'wanted_list': list of Columns that will be returned in the row_template
- 'wanted_list': list of Columns that will be returned in the row_template
- 'specs': list with Column Characteristics - 'specs': list with Column Characteristics
0: Column Name (from wanted_list) 0: Column Name (from wanted_list)
1: Column Colspan 1: Column Colspan
@ -190,14 +195,14 @@ class report_xls(report_sxw):
if s_len > 7 and s[7] is not None: if s_len > 7 and s[7] is not None:
c.append(s[7]) c.append(s[7])
else: else:
c.append(None)
c.append(None)
r.append((col, c[1], c)) r.append((col, c[1], c))
col += c[1] col += c[1]
break break
if not found: if not found:
_logger.warn("report_xls.xls_row_template, column '%s' not found in specs", w) _logger.warn("report_xls.xls_row_template, column '%s' not found in specs", w)
return r return r
def xls_write_row(self, ws, row_pos, row_data, row_style=default_style, set_column_size=False): def xls_write_row(self, ws, row_pos, row_data, row_style=default_style, set_column_size=False):
r = ws.row(row_pos) r = ws.row(row_pos)
for col, size, spec in row_data: for col, size, spec in row_data:
@ -209,9 +214,9 @@ class report_xls(report_sxw):
data = report_xls.xls_types_default[spec[3]] data = report_xls.xls_types_default[spec[3]]
if size != 1: if size != 1:
if formula: if formula:
ws.write_merge(row_pos, row_pos, col, col+size-1, data, style)
ws.write_merge(row_pos, row_pos, col, col + size - 1, data, style)
else: else:
ws.write_merge(row_pos, row_pos, col, col+size-1, data, style)
ws.write_merge(row_pos, row_pos, col, col + size - 1, data, style)
else: else:
if formula: if formula:
ws.write(row_pos, col, formula, style) ws.write(row_pos, col, formula, style)
@ -220,5 +225,5 @@ class report_xls(report_sxw):
if set_column_size: if set_column_size:
ws.col(col).width = spec[2] * 256 ws.col(col).width = spec[2] * 256
return row_pos + 1 return row_pos + 1
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

BIN
report_xls/static/src/img/icon.png

After

Width: 88  |  Height: 85  |  Size: 11 KiB

11
report_xls/utils.py

@ -12,17 +12,18 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
#
def _render(code): def _render(code):
return compile(code, '<string>', 'eval')
return compile(code, '<string>', 'eval')
def rowcol_to_cell(row, col, row_abs=False, col_abs=False): def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
# Code based upon utils from xlwt distribution # Code based upon utils from xlwt distribution
@ -41,9 +42,9 @@ def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
else: else:
col_abs = '' col_abs = ''
if d > 0: if d > 0:
chr1 = chr(ord('A') + d - 1)
chr1 = chr(ord('A') + d - 1)
chr2 = chr(ord('A') + m) chr2 = chr(ord('A') + m)
# Zero index to 1-index # Zero index to 1-index
return col_abs + chr1 + chr2 + row_abs + str(row + 1) return col_abs + chr1 + chr2 + row_abs + str(row + 1)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
Loading…
Cancel
Save