From a1cde42913ff981dabe86b6cd2432deb6725ddbf Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Thu, 7 Nov 2013 10:48:58 +0100 Subject: [PATCH 01/69] Initial commit. --- README | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 000000000..2137fe9fb --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +This repository hosts alternative reporting engines to the ones included on OpenERP core (RML and Webkit). It is complemented with the ones that host the reports theirself: + +https://launchpad.net/account-financial-report +https://launchpad.net/purchase-report +https://launchpad.net/sale-reports +... + +The convention is to use a suffix to each module to indicate that it's for using with that report engine (for example, account_invoice_report_birt or sale_order_report_pentaho). + +It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). + From bfb191e0ed9b1ac09320674be9b7b1fc2df6da39 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Thu, 7 Nov 2013 10:59:40 +0100 Subject: [PATCH 02/69] [ADD] module base_report_assembler --- base_report_assembler/__init__.py | 24 +++ base_report_assembler/__openerp__.py | 34 +++++ base_report_assembler/assembled_report.py | 40 +++++ .../i18n/base_report_assembler.pot | 36 +++++ base_report_assembler/i18n/fr.po | 37 +++++ base_report_assembler/ir_report.py | 112 ++++++++++++++ base_report_assembler/report_assembler.py | 138 ++++++++++++++++++ 7 files changed, 421 insertions(+) create mode 100644 base_report_assembler/__init__.py create mode 100644 base_report_assembler/__openerp__.py create mode 100644 base_report_assembler/assembled_report.py create mode 100644 base_report_assembler/i18n/base_report_assembler.pot create mode 100644 base_report_assembler/i18n/fr.po create mode 100644 base_report_assembler/ir_report.py create mode 100644 base_report_assembler/report_assembler.py diff --git a/base_report_assembler/__init__.py b/base_report_assembler/__init__.py new file mode 100644 index 000000000..f91d090ee --- /dev/null +++ b/base_report_assembler/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Yannick Vaucher +# Copyright 2013 Camptocamp SA +# +# 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 . +# +############################################################################## +from . import report_assembler +from . import assembled_report +from . import ir_report + diff --git a/base_report_assembler/__openerp__.py b/base_report_assembler/__openerp__.py new file mode 100644 index 000000000..45dbc41ad --- /dev/null +++ b/base_report_assembler/__openerp__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Yannick Vaucher +# Copyright 2013 Camptocamp SA +# +# 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 . +# +############################################################################## +{'name' : 'Base Report Assembler', + 'version' : '1.0', + 'category': 'Report', + 'description': """Defines a new type of report which is an assemblage of multiple other reports""", + 'author' : 'Camptocamp', + 'maintainer': 'Camptocamp', + 'website': 'http://www.camptocamp.com/', + 'depends' : ['base'], + 'data': [], + 'test': [], + 'installable': True, + 'auto_install': False, + 'application': False, + } diff --git a/base_report_assembler/assembled_report.py b/base_report_assembler/assembled_report.py new file mode 100644 index 000000000..f2fe0ca4b --- /dev/null +++ b/base_report_assembler/assembled_report.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Yannick Vaucher +# Copyright 2013 Camptocamp SA +# +# 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 . +# +############################################################################## +from openerp.osv import orm, fields + +class AssembledReport(orm.Model): + _name = 'assembled.report' + + _order = 'sequence' + + _columns = { + 'report_id': fields.many2one('ir.actions.report.xml', 'Report', + domain="[('model', '=', model)," + "('report_type', '!=', 'assemblage')]", required=True), + 'model': fields.char('Object model'), + 'sequence': fields.integer('Sequence', required=True), + 'company_id': fields.many2one('res.company', 'Company', required=True), + } + + _defaults = { + 'sequence': 1, + 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'assembled.report', context=c) + } diff --git a/base_report_assembler/i18n/base_report_assembler.pot b/base_report_assembler/i18n/base_report_assembler.pot new file mode 100644 index 000000000..5b2331ef3 --- /dev/null +++ b/base_report_assembler/i18n/base_report_assembler.pot @@ -0,0 +1,36 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * base_report_assembler +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-11-05 14:55+0000\n" +"PO-Revision-Date: 2013-11-05 14:55+0000\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: base_report_assembler +#: field:assembled.report,model:0 +msgid "Object model" +msgstr "" + +#. module: base_report_assembler +#: field:assembled.report,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: base_report_assembler +#: field:assembled.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: base_report_assembler +#: field:assembled.report,report_id:0 +msgid "Report" +msgstr "" diff --git a/base_report_assembler/i18n/fr.po b/base_report_assembler/i18n/fr.po new file mode 100644 index 000000000..b95b27a20 --- /dev/null +++ b/base_report_assembler/i18n/fr.po @@ -0,0 +1,37 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * base_report_assembler +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-11-05 14:55+0000\n" +"PO-Revision-Date: 2013-11-05 14:55+0000\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: base_report_assembler +#: field:assembled.report,model:0 +msgid "Object model" +msgstr "Modèle d'object" + +#. module: base_report_assembler +#: field:assembled.report,sequence:0 +msgid "Sequence" +msgstr "Séquence" + +#. module: base_report_assembler +#: field:assembled.report,company_id:0 +msgid "Company" +msgstr "Companie" + +#. module: base_report_assembler +#: field:assembled.report,report_id:0 +msgid "Report" +msgstr "Rapport" + diff --git a/base_report_assembler/ir_report.py b/base_report_assembler/ir_report.py new file mode 100644 index 000000000..ab2e7c967 --- /dev/null +++ b/base_report_assembler/ir_report.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Yannick Vaucher +# Copyright 2013 Camptocamp SA +# +# 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 . +# +############################################################################## + +from openerp.osv import orm +from openerp import netsvc +from openerp.report.report_sxw import rml_parse +from report_assembler import PDFReportAssembler + +def register_report(name, model, parser=rml_parse): + """Register the report into the services""" + name = 'report.%s' % name + if netsvc.Service._services.get(name, False): + service = netsvc.Service._services[name] + if isinstance(service, PDFReportAssembler): + #already instantiated properly, skip it + return + if hasattr(service, 'parser'): + parser = service.parser + del netsvc.Service._services[name] + PDFReportAssembler(name, model, parser=parser) + + +class ReportAssembleXML(orm.Model): + + _name = 'ir.actions.report.xml' + _inherit = 'ir.actions.report.xml' + + def __init__(self, pool, cr): + super(ReportAssembleXML, self).__init__(pool, cr) + + def register_all(self,cursor): + value = super(ReportAssembleXML, self).register_all(cursor) + cursor.execute("SELECT * FROM ir_act_report_xml WHERE report_type = 'assemblage'") + records = cursor.dictfetchall() + for record in records: + register_report(record['report_name'], record['model']) + return value + + def unlink(self, cursor, user, ids, context=None): + """Delete report and unregister it""" + trans_obj = self.pool.get('ir.translation') + trans_ids = trans_obj.search( + cursor, + user, + [('type', '=', 'report'), ('res_id', 'in', ids)] + ) + trans_obj.unlink(cursor, user, trans_ids) + + # Warning: we cannot unregister the services at the moment + # because they are shared across databases. Calling a deleted + # report will fail so it's ok. + + res = super(ReportAssembleXML, self).unlink( + cursor, + user, + ids, + context + ) + return res + + def create(self, cursor, user, vals, context=None): + "Create report and register it" + res = super(ReportAssembleXML, self).create(cursor, user, vals, context) + if vals.get('report_type','') == 'assemblage': + # I really look forward to virtual functions :S + register_report( + vals['report_name'], + vals['model'], + ) + return res + + def write(self, cr, uid, ids, vals, context=None): + "Edit report and manage its registration" + if isinstance(ids, (int, long)): + ids = [ids,] + for rep in self.browse(cr, uid, ids, context=context): + if rep.report_type != 'assemblage': + continue + if vals.get('report_name', False) and \ + vals['report_name'] != rep.report_name: + report_name = vals['report_name'] + else: + report_name = rep.report_name + + register_report( + report_name, + vals.get('model', rep.model), + False + ) + res = super(ReportAssembleXML, self).write(cr, uid, ids, vals, context) + return res + + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/base_report_assembler/report_assembler.py b/base_report_assembler/report_assembler.py new file mode 100644 index 000000000..8753f1057 --- /dev/null +++ b/base_report_assembler/report_assembler.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Yannick Vaucher +# Copyright 2013 Camptocamp SA +# +# 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 . +# +############################################################################## +import time +import base64 +from PyPDF2 import PdfFileReader, PdfFileWriter +from StringIO import StringIO + +from openerp.service.web_services import report_spool +from openerp.netsvc import Service +from openerp.netsvc import ExportService +from openerp.report import report_sxw +from openerp import pooler, sql_db + +_POLLING_DELAY = 0.25 + +def assemble_pdf(pdf_list): + """ + Assemble a list of pdf + """ + # Even though we are using PyPDF2 we can't use PdfFileMerger + # as this issue still exists in mostly used wktohtml reports version + # http://code.google.com/p/wkhtmltopdf/issues/detail?id=635 + #merger = PdfFileMerger() + #merger.append(fileobj=StringIO(invoice_pdf)) + #merger.append(fileobj=StringIO(bvr_pdf)) + + #with tempfile.TemporaryFile() as merged_pdf: + #merger.write(merged_pdf) + #return merged_pdf.read(), 'pdf' + + output = PdfFileWriter() + for pdf in pdf_list: + reader = PdfFileReader(StringIO(pdf)) + for page in range(reader.getNumPages()): + output.addPage(reader.getPage(page)) + s = StringIO() + output.write(s) + return s.getvalue() + +class PDFReportAssembler(report_sxw.report_sxw): + """ PDFReportAssembler allows to put 2 invoice reports in one single pdf""" + + def _generate_all_pdf(self, cr, uid, ids, data, report_ids, context=None): + """ + Return a list of pdf encoded in base64 + """ + pool = pooler.get_pool(cr.dbname) + db = sql_db.db_connect(cr.dbname) + report_obj = pool.get('ir.actions.report.xml') + + spool = ExportService._services['report'] + + pdf_reports = [] + report_list = report_obj.browse(cr, uid, report_ids, context=context) + for report in report_list: + + report_key = spool.exp_report(cr.dbname, uid, report.report_name, ids, datas=data, context=context) + while 1: + res = spool.exp_report_get(cr.dbname, uid, report_key) + if res.get('state'): + break + time.sleep(_POLLING_DELAY) + pdf = base64.b64decode(res.get('result')) + pdf_reports.append(pdf) + return pdf_reports + + #pdf_reports = {} + #report_list = report_obj.browse(cr, uid, report_ids, context=context) + #report_obj = pool.get('ir.actions.report.xml') + #pool = pooler.get_pool(cr.dbname) + #for report in report_list: + + #report_parser = Service._services['report.%s' %report.report_name] + #pdf_reports[report.id] = report_parser.create_single_pdf(cr, uid, ids, data, report, context=context)[0] + #return pdf_reports + + + + def _get_report_ids(self, cr, uid, ids, context=None): + """ + Hook to define the list of report to print + """ + return [] + + def create_single_pdf(self, cr, uid, ids, data, report_xml, context=None): + """Call both report to assemble both pdf""" + + report_ids = self._get_report_ids(cr, uid, ids, context=context) + + pdf_reports = self._generate_all_pdf(cr, uid, ids, data, report_ids, context=context) + + pdf_assemblage = assemble_pdf(pdf_reports) + return pdf_assemblage, 'pdf' + + def create(self, cr, uid, ids, data, context=None): + """We override the create function in order to handle generator + Code taken from report openoffice. Thanks guys :) """ + pool = pooler.get_pool(cr.dbname) + ir_obj = pool.get('ir.actions.report.xml') + report_xml_ids = ir_obj.search(cr, uid, + [('report_name', '=', self.name[7:])], context=context) + if report_xml_ids: + + report_xml = ir_obj.browse(cr, + uid, + report_xml_ids[0], + context=context) + report_xml.report_rml = None + report_xml.report_rml_content = None + report_xml.report_sxw_content_data = None + report_xml.report_sxw_content = None + report_xml.report_sxw = None + else: + return super(PDFReportAssembler, self).create(cr, uid, ids, data, context) + if report_xml.report_type != 'assemblage' : + return super(PDFReportAssembler, self).create(cr, uid, ids, data, context) + result = self.create_source_pdf(cr, uid, ids, data, report_xml, context) + if not result: + return (False, False) + return result From 1a80ccd02283e72590d6b03204bedb3cb0233ea3 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Thu, 7 Nov 2013 11:19:04 +0100 Subject: [PATCH 03/69] [IMP] clean commented code --- base_report_assembler/report_assembler.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/base_report_assembler/report_assembler.py b/base_report_assembler/report_assembler.py index 8753f1057..cfd953c77 100644 --- a/base_report_assembler/report_assembler.py +++ b/base_report_assembler/report_assembler.py @@ -23,8 +23,6 @@ import base64 from PyPDF2 import PdfFileReader, PdfFileWriter from StringIO import StringIO -from openerp.service.web_services import report_spool -from openerp.netsvc import Service from openerp.netsvc import ExportService from openerp.report import report_sxw from openerp import pooler, sql_db @@ -82,18 +80,6 @@ class PDFReportAssembler(report_sxw.report_sxw): pdf_reports.append(pdf) return pdf_reports - #pdf_reports = {} - #report_list = report_obj.browse(cr, uid, report_ids, context=context) - #report_obj = pool.get('ir.actions.report.xml') - #pool = pooler.get_pool(cr.dbname) - #for report in report_list: - - #report_parser = Service._services['report.%s' %report.report_name] - #pdf_reports[report.id] = report_parser.create_single_pdf(cr, uid, ids, data, report, context=context)[0] - #return pdf_reports - - - def _get_report_ids(self, cr, uid, ids, context=None): """ Hook to define the list of report to print From eae4fbbb7554ea1f3847e5afcd79f6fa9b1d7f4d Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Thu, 7 Nov 2013 11:25:56 +0100 Subject: [PATCH 04/69] [PEP8] --- base_report_assembler/assembled_report.py | 8 ++++-- base_report_assembler/ir_report.py | 34 +++++++++++------------ base_report_assembler/report_assembler.py | 10 ++++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/base_report_assembler/assembled_report.py b/base_report_assembler/assembled_report.py index f2fe0ca4b..d59c95c89 100644 --- a/base_report_assembler/assembled_report.py +++ b/base_report_assembler/assembled_report.py @@ -20,15 +20,17 @@ ############################################################################## from openerp.osv import orm, fields + class AssembledReport(orm.Model): _name = 'assembled.report' _order = 'sequence' _columns = { - 'report_id': fields.many2one('ir.actions.report.xml', 'Report', + 'report_id': fields.many2one( + 'ir.actions.report.xml', 'Report', domain="[('model', '=', model)," - "('report_type', '!=', 'assemblage')]", required=True), + "('report_type', '!=', 'assemblage')]", required=True), 'model': fields.char('Object model'), 'sequence': fields.integer('Sequence', required=True), 'company_id': fields.many2one('res.company', 'Company', required=True), @@ -36,5 +38,5 @@ class AssembledReport(orm.Model): _defaults = { 'sequence': 1, - 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'assembled.report', context=c) + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'assembled.report', context=c) } diff --git a/base_report_assembler/ir_report.py b/base_report_assembler/ir_report.py index ab2e7c967..494d5485a 100644 --- a/base_report_assembler/ir_report.py +++ b/base_report_assembler/ir_report.py @@ -18,12 +18,12 @@ # along with this program. If not, see . # ############################################################################## - from openerp.osv import orm from openerp import netsvc from openerp.report.report_sxw import rml_parse from report_assembler import PDFReportAssembler + def register_report(name, model, parser=rml_parse): """Register the report into the services""" name = 'report.%s' % name @@ -46,7 +46,7 @@ class ReportAssembleXML(orm.Model): def __init__(self, pool, cr): super(ReportAssembleXML, self).__init__(pool, cr) - def register_all(self,cursor): + def register_all(self, cursor): value = super(ReportAssembleXML, self).register_all(cursor) cursor.execute("SELECT * FROM ir_act_report_xml WHERE report_type = 'assemblage'") records = cursor.dictfetchall() @@ -69,42 +69,40 @@ class ReportAssembleXML(orm.Model): # report will fail so it's ok. res = super(ReportAssembleXML, self).unlink( - cursor, - user, - ids, - context - ) + cursor, + user, + ids, + context) return res def create(self, cursor, user, vals, context=None): "Create report and register it" res = super(ReportAssembleXML, self).create(cursor, user, vals, context) - if vals.get('report_type','') == 'assemblage': + if vals.get('report_type', '') == 'assemblage': # I really look forward to virtual functions :S register_report( - vals['report_name'], - vals['model'], - ) + vals['report_name'], + vals['model']) return res def write(self, cr, uid, ids, vals, context=None): "Edit report and manage its registration" if isinstance(ids, (int, long)): - ids = [ids,] + ids = [ids] for rep in self.browse(cr, uid, ids, context=context): if rep.report_type != 'assemblage': continue - if vals.get('report_name', False) and \ - vals['report_name'] != rep.report_name: + if (vals.get('report_name', False) + and vals['report_name'] != rep.report_name): report_name = vals['report_name'] else: report_name = rep.report_name register_report( - report_name, - vals.get('model', rep.model), - False - ) + report_name, + vals.get('model', rep.model), + False + ) res = super(ReportAssembleXML, self).write(cr, uid, ids, vals, context) return res diff --git a/base_report_assembler/report_assembler.py b/base_report_assembler/report_assembler.py index cfd953c77..cab264ad0 100644 --- a/base_report_assembler/report_assembler.py +++ b/base_report_assembler/report_assembler.py @@ -25,10 +25,11 @@ from StringIO import StringIO from openerp.netsvc import ExportService from openerp.report import report_sxw -from openerp import pooler, sql_db +from openerp import pooler _POLLING_DELAY = 0.25 + def assemble_pdf(pdf_list): """ Assemble a list of pdf @@ -53,6 +54,7 @@ def assemble_pdf(pdf_list): output.write(s) return s.getvalue() + class PDFReportAssembler(report_sxw.report_sxw): """ PDFReportAssembler allows to put 2 invoice reports in one single pdf""" @@ -61,7 +63,6 @@ class PDFReportAssembler(report_sxw.report_sxw): Return a list of pdf encoded in base64 """ pool = pooler.get_pool(cr.dbname) - db = sql_db.db_connect(cr.dbname) report_obj = pool.get('ir.actions.report.xml') spool = ExportService._services['report'] @@ -70,7 +71,8 @@ class PDFReportAssembler(report_sxw.report_sxw): report_list = report_obj.browse(cr, uid, report_ids, context=context) for report in report_list: - report_key = spool.exp_report(cr.dbname, uid, report.report_name, ids, datas=data, context=context) + report_key = spool.exp_report(cr.dbname, uid, report.report_name, + ids, datas=data, context=context) while 1: res = spool.exp_report_get(cr.dbname, uid, report_key) if res.get('state'): @@ -116,7 +118,7 @@ class PDFReportAssembler(report_sxw.report_sxw): report_xml.report_sxw = None else: return super(PDFReportAssembler, self).create(cr, uid, ids, data, context) - if report_xml.report_type != 'assemblage' : + if report_xml.report_type != 'assemblage': return super(PDFReportAssembler, self).create(cr, uid, ids, data, context) result = self.create_source_pdf(cr, uid, ids, data, report_xml, context) if not result: From 0caa3641d61f25c9220b5443af39a826ebf237ab Mon Sep 17 00:00:00 2001 From: Luc De Meyer Date: Fri, 15 Nov 2013 16:56:47 +0100 Subject: [PATCH 05/69] add report_xls module --- report_xls/__init__.py | 24 ++++ report_xls/__openerp__.py | 44 ++++++++ report_xls/report_xls.py | 224 ++++++++++++++++++++++++++++++++++++++ report_xls/utils.py | 49 +++++++++ 4 files changed, 341 insertions(+) create mode 100644 report_xls/__init__.py create mode 100644 report_xls/__openerp__.py create mode 100644 report_xls/report_xls.py create mode 100644 report_xls/utils.py diff --git a/report_xls/__init__.py b/report_xls/__init__.py new file mode 100644 index 000000000..aab5a45ba --- /dev/null +++ b/report_xls/__init__.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 . +# +############################################################################## + +from . import report_xls +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py new file mode 100644 index 000000000..c8fea8490 --- /dev/null +++ b/report_xls/__openerp__.py @@ -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 . +# +############################################################################## +{ + '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: diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py new file mode 100644 index 000000000..e8263a2f7 --- /dev/null +++ b/report_xls/report_xls.py @@ -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 . +# +############################################################################## + +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: diff --git a/report_xls/utils.py b/report_xls/utils.py new file mode 100644 index 000000000..30ce800d1 --- /dev/null +++ b/report_xls/utils.py @@ -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 . +# +############################################################################## +# + +def _render(code): + return compile(code, '', '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: From d7d440cb2f24066ac0a09e4fa0e5922ef2d6e4c4 Mon Sep 17 00:00:00 2001 From: Luc De Meyer Date: Mon, 6 Jan 2014 23:22:39 +0100 Subject: [PATCH 06/69] style & documentation refresh, xls footer datetime fix --- report_xls/__init__.py | 2 +- report_xls/__openerp__.py | 58 ++++++++++++++++---- report_xls/report_xls.py | 83 +++++++++++++++-------------- report_xls/static/src/img/icon.png | Bin 0 -> 11645 bytes report_xls/utils.py | 11 ++-- 5 files changed, 98 insertions(+), 56 deletions(-) create mode 100644 report_xls/static/src/img/icon.png diff --git a/report_xls/__init__.py b/report_xls/__init__.py index aab5a45ba..835cd44c7 100644 --- a/report_xls/__init__.py +++ b/report_xls/__init__.py @@ -12,7 +12,7 @@ # # 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 +# 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 diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index c8fea8490..5e85adac0 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -12,7 +12,7 @@ # # 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 +# 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 @@ -20,24 +20,60 @@ # ############################################################################## { - 'name': 'XLS report engine', - 'version': '0.3', + 'name': 'Excel report engine', + 'version': '0.4', 'license': 'AGPL-3', 'author': 'Noviat', 'website': 'http://www.noviat.com', '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'], 'external_dependencies': {'python': ['xlwt']}, - 'demo_xml': [], - 'init_xml': [], - 'update_xml' : [], + 'demo': [], + 'data': [], 'active': False, 'installable': True, } diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py index e8263a2f7..5e19c859e 100644 --- a/report_xls/report_xls.py +++ b/report_xls/report_xls.py @@ -2,8 +2,8 @@ ############################################################################## # # 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 # 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, # 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. # # You should have received a copy of the GNU Affero General Public License @@ -23,7 +23,8 @@ import xlwt from xlwt.Style import default_style import cStringIO -import datetime, time +from datetime import datetime +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT import inspect from types import CodeType from openerp.report.report_sxw import * @@ -32,13 +33,15 @@ 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, @@ -53,36 +56,29 @@ class report_xls(report_sxw): } # TO DO: move parameters infra to configurable data - + # header/footer - DT_FORMAT = '%Y-%m-%d %H:%M:%S' + DT_FORMAT = '%Y-%m-%d %H:%M:%S' hf_params = { 'font_size': 8, - 'font_style': 'I', # B: Bold, I: Italic, U: Underline + '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 + _pfc = '26' # default pattern fore_color + _bc = '22' # borders color decimal_format = '#,##0.00' - date_format = 'YYYY-MM-DD' + 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), + '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;', @@ -91,8 +87,8 @@ class report_xls(report_sxw): 'bottom': 'align: vert bottom;', } # 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.cr = cr self.uid = uid @@ -105,11 +101,13 @@ class report_xls(report_sxw): if report_xml.report_type == 'xls': return self.create_source_xls(cr, uid, ids, data, context) 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 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 = {} + 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) @@ -119,15 +117,22 @@ class report_xls(report_sxw): wb = xlwt.Workbook(encoding='utf-8') _p = AttrDict(parser_instance.localcontext) _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) wb.save(n) n.seek(0) - return (n.read(), 'xls') - + return (n.read(), 'xls') + def render(self, wanted, col_specs, rowtype, render_space='empty'): """ - returns 'mako'-rendered col_specs - + returns 'evaluated' col_specs + Input: - wanted: element from the wanted_list - 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 localcontext = self.parser_instance.localcontext render_space.update(caller_space) - render_space.update(localcontext) + render_space.update(localcontext) row = col_specs[wanted][rowtype][:] for i in range(len(row)): if isinstance(row[i], CodeType): @@ -155,9 +160,9 @@ class report_xls(report_sxw): 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 + - '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 @@ -190,14 +195,14 @@ class report_xls(report_sxw): if s_len > 7 and s[7] is not None: c.append(s[7]) else: - c.append(None) + 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: @@ -209,9 +214,9 @@ class report_xls(report_sxw): 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) + 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) + ws.write_merge(row_pos, row_pos, col, col + size - 1, data, style) else: if formula: ws.write(row_pos, col, formula, style) @@ -220,5 +225,5 @@ class report_xls(report_sxw): if set_column_size: ws.col(col).width = spec[2] * 256 return row_pos + 1 - + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/static/src/img/icon.png b/report_xls/static/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a2fcdff9d1ae795a22cc4608a55d3c290b6e0c GIT binary patch literal 11645 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 Hu0mjfi6p;w literal 0 HcmV?d00001 diff --git a/report_xls/utils.py b/report_xls/utils.py index 30ce800d1..e4f3d878b 100644 --- a/report_xls/utils.py +++ b/report_xls/utils.py @@ -12,17 +12,18 @@ # # 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 +# 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 . # ############################################################################## -# + def _render(code): - return compile(code, '', 'eval') + return compile(code, '', 'eval') + def rowcol_to_cell(row, col, row_abs=False, col_abs=False): # Code based upon utils from xlwt distribution @@ -41,9 +42,9 @@ def rowcol_to_cell(row, col, row_abs=False, col_abs=False): else: col_abs = '' if d > 0: - chr1 = chr(ord('A') + d - 1) + 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: From 9e5cf0fe958d583fc5dd1fcd7bc0e55e9c69de75 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Mon, 13 Jan 2014 15:28:05 +0100 Subject: [PATCH 07/69] Update translations --- base_report_assembler/i18n/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_report_assembler/i18n/fr.po b/base_report_assembler/i18n/fr.po index b95b27a20..6dee61e0b 100644 --- a/base_report_assembler/i18n/fr.po +++ b/base_report_assembler/i18n/fr.po @@ -28,7 +28,7 @@ msgstr "Séquence" #. module: base_report_assembler #: field:assembled.report,company_id:0 msgid "Company" -msgstr "Companie" +msgstr "Société" #. module: base_report_assembler #: field:assembled.report,report_id:0 From e6c65b62d4a6db20181d537994d740e2bda0fda3 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Mon, 13 Jan 2014 15:32:45 +0100 Subject: [PATCH 08/69] [FIX] docstrings --- base_report_assembler/ir_report.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base_report_assembler/ir_report.py b/base_report_assembler/ir_report.py index 494d5485a..6cc901e54 100644 --- a/base_report_assembler/ir_report.py +++ b/base_report_assembler/ir_report.py @@ -25,7 +25,7 @@ from report_assembler import PDFReportAssembler def register_report(name, model, parser=rml_parse): - """Register the report into the services""" + """ Register the report into the services """ name = 'report.%s' % name if netsvc.Service._services.get(name, False): service = netsvc.Service._services[name] @@ -55,7 +55,7 @@ class ReportAssembleXML(orm.Model): return value def unlink(self, cursor, user, ids, context=None): - """Delete report and unregister it""" + """ Delete report and unregister it """ trans_obj = self.pool.get('ir.translation') trans_ids = trans_obj.search( cursor, @@ -76,7 +76,7 @@ class ReportAssembleXML(orm.Model): return res def create(self, cursor, user, vals, context=None): - "Create report and register it" + """ Create report and register it """ res = super(ReportAssembleXML, self).create(cursor, user, vals, context) if vals.get('report_type', '') == 'assemblage': # I really look forward to virtual functions :S @@ -86,7 +86,7 @@ class ReportAssembleXML(orm.Model): return res def write(self, cr, uid, ids, vals, context=None): - "Edit report and manage its registration" + """ Edit report and manage its registration """ if isinstance(ids, (int, long)): ids = [ids] for rep in self.browse(cr, uid, ids, context=context): From dbb95abf3945e1442dd778e55b49b7bb6247cfd9 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Mon, 13 Jan 2014 15:50:00 +0100 Subject: [PATCH 09/69] [IMP] module description --- base_report_assembler/__openerp__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/base_report_assembler/__openerp__.py b/base_report_assembler/__openerp__.py index 45dbc41ad..6a2bd92a1 100644 --- a/base_report_assembler/__openerp__.py +++ b/base_report_assembler/__openerp__.py @@ -21,7 +21,22 @@ {'name' : 'Base Report Assembler', 'version' : '1.0', 'category': 'Report', - 'description': """Defines a new type of report which is an assemblage of multiple other reports""", + 'description': """ +Base Report Assembler +===================== + +Defines a new type of report which is an assemblage of multiple other reports +of the same object. + +For example you can merge the pdf output of a rml invoice report with the pdf +output of a webkit payment slip. + +To install this assemblage option for specific object you can install +the folling module(s): + +- Invoices: invoice_report_assemble (lp:account-invoice-report) + +""", 'author' : 'Camptocamp', 'maintainer': 'Camptocamp', 'website': 'http://www.camptocamp.com/', From 50c3f45a5a535910bfca24c999033f5b71e4d81f Mon Sep 17 00:00:00 2001 From: Nicolas Bessi Date: Fri, 31 Jan 2014 11:58:38 +0100 Subject: [PATCH 10/69] [PEP8] --- base_report_assembler/__openerp__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base_report_assembler/__openerp__.py b/base_report_assembler/__openerp__.py index 6a2bd92a1..53ccc32d6 100644 --- a/base_report_assembler/__openerp__.py +++ b/base_report_assembler/__openerp__.py @@ -18,8 +18,8 @@ # along with this program. If not, see . # ############################################################################## -{'name' : 'Base Report Assembler', - 'version' : '1.0', +{'name': 'Base Report Assembler', + 'version': '1.0', 'category': 'Report', 'description': """ Base Report Assembler @@ -37,10 +37,10 @@ the folling module(s): - Invoices: invoice_report_assemble (lp:account-invoice-report) """, - 'author' : 'Camptocamp', + 'author': 'Camptocamp', 'maintainer': 'Camptocamp', 'website': 'http://www.camptocamp.com/', - 'depends' : ['base'], + 'depends': ['base'], 'data': [], 'test': [], 'installable': True, From 7d4f9a77cb72285f2355b4157f83474ddb13198f Mon Sep 17 00:00:00 2001 From: Nicolas Bessi Date: Fri, 31 Jan 2014 12:09:04 +0100 Subject: [PATCH 11/69] [PEP8] and correction of doc string --- base_report_assembler/assembled_report.py | 4 ++-- base_report_assembler/report_assembler.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base_report_assembler/assembled_report.py b/base_report_assembler/assembled_report.py index d59c95c89..212e5bf27 100644 --- a/base_report_assembler/assembled_report.py +++ b/base_report_assembler/assembled_report.py @@ -20,7 +20,6 @@ ############################################################################## from openerp.osv import orm, fields - class AssembledReport(orm.Model): _name = 'assembled.report' @@ -38,5 +37,6 @@ class AssembledReport(orm.Model): _defaults = { 'sequence': 1, - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'assembled.report', context=c) + 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get( + cr, uid, 'assembled.report', context=c) } diff --git a/base_report_assembler/report_assembler.py b/base_report_assembler/report_assembler.py index cab264ad0..08b5a9b98 100644 --- a/base_report_assembler/report_assembler.py +++ b/base_report_assembler/report_assembler.py @@ -56,7 +56,7 @@ def assemble_pdf(pdf_list): class PDFReportAssembler(report_sxw.report_sxw): - """ PDFReportAssembler allows to put 2 invoice reports in one single pdf""" + """ PDFReportAssembler allows to put 2 pdf reports in one single pdf""" def _generate_all_pdf(self, cr, uid, ids, data, report_ids, context=None): """ From 8a1cbdb707ff0dd141a18c208ce29c5344c89fb4 Mon Sep 17 00:00:00 2001 From: Luc De Meyer Date: Mon, 12 May 2014 22:49:36 +0200 Subject: [PATCH 12/69] report_xls timezone fix --- report_xls/__init__.py | 4 ++-- report_xls/__openerp__.py | 6 +++--- report_xls/report_xls.py | 5 +++-- report_xls/utils.py | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/report_xls/__init__.py b/report_xls/__init__.py index 835cd44c7..1f5df8f6b 100644 --- a/report_xls/__init__.py +++ b/report_xls/__init__.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # -# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. +# Copyright (c) 2014 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 @@ -16,7 +16,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index 5e85adac0..7e505828a 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # -# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. +# Copyright (c) 2014 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 @@ -16,7 +16,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## { @@ -67,7 +67,7 @@ the creation XLS reports in OpenERP. Development assistance '''''''''''''''''''''' -Contact info@noviat.com for help with the development of Excel reports in OpenERP, . +Contact info@noviat.com for help with the development of Excel reports in OpenERP. """, 'depends': ['base'], diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py index 5e19c859e..707fc34b8 100644 --- a/report_xls/report_xls.py +++ b/report_xls/report_xls.py @@ -24,6 +24,7 @@ import xlwt from xlwt.Style import default_style import cStringIO from datetime import datetime +from openerp.osv.fields import datetime as datetime_field from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT import inspect from types import CodeType @@ -58,7 +59,6 @@ class report_xls(report_sxw): # 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 @@ -120,8 +120,9 @@ class report_xls(report_sxw): self.xls_headers = { 'standard': '', } + report_date = datetime_field.context_timestamp(cr, uid, datetime.now(), context).strftime(DEFAULT_SERVER_DATETIME_FORMAT) self.xls_footers = { - 'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT) + + 'standard': ('&L&%(font_size)s&%(font_style)s' + report_date + '&R&%(font_size)s&%(font_style)s&P / &N') % self.hf_params, } self.generate_xls_report(_p, _xs, data, objs, wb) diff --git a/report_xls/utils.py b/report_xls/utils.py index e4f3d878b..f21644ca2 100644 --- a/report_xls/utils.py +++ b/report_xls/utils.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # -# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. +# Copyright (c) 2014 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 @@ -16,7 +16,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## From 834c3fc277752571ee8affda5311b3e06871e770 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 30 Sep 2014 21:09:35 +0200 Subject: [PATCH 13/69] Preparation for 8.0 branch --- .gitignore | 56 ++++++++++++++++++ .travis.yml | 23 +++++++ README | 11 ---- README.md | 24 ++++++++ .../base_report_assembler}/__init__.py | 0 .../base_report_assembler}/__openerp__.py | 2 +- .../assembled_report.py | 0 .../i18n/base_report_assembler.pot | 0 .../base_report_assembler}/i18n/fr.po | 0 .../base_report_assembler}/ir_report.py | 0 .../report_assembler.py | 0 .../report_xls}/__init__.py | 0 .../report_xls}/__openerp__.py | 2 +- .../report_xls}/report_xls.py | 0 .../report_xls}/static/src/img/icon.png | Bin .../report_xls}/utils.py | 0 16 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 .gitignore create mode 100644 .travis.yml delete mode 100644 README create mode 100644 README.md rename {base_report_assembler => __unported__/base_report_assembler}/__init__.py (100%) rename {base_report_assembler => __unported__/base_report_assembler}/__openerp__.py (98%) rename {base_report_assembler => __unported__/base_report_assembler}/assembled_report.py (100%) rename {base_report_assembler => __unported__/base_report_assembler}/i18n/base_report_assembler.pot (100%) rename {base_report_assembler => __unported__/base_report_assembler}/i18n/fr.po (100%) rename {base_report_assembler => __unported__/base_report_assembler}/ir_report.py (100%) rename {base_report_assembler => __unported__/base_report_assembler}/report_assembler.py (100%) rename {report_xls => __unported__/report_xls}/__init__.py (100%) rename {report_xls => __unported__/report_xls}/__openerp__.py (99%) rename {report_xls => __unported__/report_xls}/report_xls.py (100%) rename {report_xls => __unported__/report_xls}/static/src/img/icon.png (100%) rename {report_xls => __unported__/report_xls}/utils.py (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..890ff0109 --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Pycharm +.idea + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Sphinx documentation +docs/_build/ + +# Backup files +*~ +*.swp diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..efa1f2775 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: python + +python: + - "2.7" + +env: + - VERSION="8.0" ODOO_REPO="odoo/odoo" + - VERSION="8.0" ODOO_REPO="OCA/OCB" + +virtualenv: + system_site_packages: true + +install: + - pip install xlwt + - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools + - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} + - travis_install_nightly ${VERSION} + +script: + - travis_run_tests ${VERSION} + +after_success: + coveralls diff --git a/README b/README deleted file mode 100644 index 2137fe9fb..000000000 --- a/README +++ /dev/null @@ -1,11 +0,0 @@ -This repository hosts alternative reporting engines to the ones included on OpenERP core (RML and Webkit). It is complemented with the ones that host the reports theirself: - -https://launchpad.net/account-financial-report -https://launchpad.net/purchase-report -https://launchpad.net/sale-reports -... - -The convention is to use a suffix to each module to indicate that it's for using with that report engine (for example, account_invoice_report_birt or sale_order_report_pentaho). - -It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). - diff --git a/README.md b/README.md new file mode 100644 index 000000000..e1e94d847 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +[![Build Status](https://travis-ci.org/OCA/reporting-engine.svg?branch=8.0)](https://travis-ci.org/OCA/reporting-engine) +[![Coverage Status](https://img.shields.io/coveralls/OCA/reporting-engine.svg)](https://coveralls.io/r/OCA/reporting-engine?branch=8.0) + +OCA alternative reporting engines and reporting utilities for Odoo +================================================================== + +This repository hosts alternative reporting engines to the ones included on Odoo core (RML, QWeb and Webkit). It is complemented with the ones that host the reports theirself: + +https://github.com/OCA/account-financial-reporting +https://github.com/OCA/purchase-reporting +https://github.com/OCA/sale-reporting +... + +The convention is to use a suffix to each module to indicate that it's for using with that report engine (for example, account_invoice_report_birt or sale_order_report_pentaho). + +It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). + +---- + +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. + +http://odoo-community.org/ diff --git a/base_report_assembler/__init__.py b/__unported__/base_report_assembler/__init__.py similarity index 100% rename from base_report_assembler/__init__.py rename to __unported__/base_report_assembler/__init__.py diff --git a/base_report_assembler/__openerp__.py b/__unported__/base_report_assembler/__openerp__.py similarity index 98% rename from base_report_assembler/__openerp__.py rename to __unported__/base_report_assembler/__openerp__.py index 53ccc32d6..03b4e7d43 100644 --- a/base_report_assembler/__openerp__.py +++ b/__unported__/base_report_assembler/__openerp__.py @@ -43,7 +43,7 @@ the folling module(s): 'depends': ['base'], 'data': [], 'test': [], - 'installable': True, + 'installable': False, 'auto_install': False, 'application': False, } diff --git a/base_report_assembler/assembled_report.py b/__unported__/base_report_assembler/assembled_report.py similarity index 100% rename from base_report_assembler/assembled_report.py rename to __unported__/base_report_assembler/assembled_report.py diff --git a/base_report_assembler/i18n/base_report_assembler.pot b/__unported__/base_report_assembler/i18n/base_report_assembler.pot similarity index 100% rename from base_report_assembler/i18n/base_report_assembler.pot rename to __unported__/base_report_assembler/i18n/base_report_assembler.pot diff --git a/base_report_assembler/i18n/fr.po b/__unported__/base_report_assembler/i18n/fr.po similarity index 100% rename from base_report_assembler/i18n/fr.po rename to __unported__/base_report_assembler/i18n/fr.po diff --git a/base_report_assembler/ir_report.py b/__unported__/base_report_assembler/ir_report.py similarity index 100% rename from base_report_assembler/ir_report.py rename to __unported__/base_report_assembler/ir_report.py diff --git a/base_report_assembler/report_assembler.py b/__unported__/base_report_assembler/report_assembler.py similarity index 100% rename from base_report_assembler/report_assembler.py rename to __unported__/base_report_assembler/report_assembler.py diff --git a/report_xls/__init__.py b/__unported__/report_xls/__init__.py similarity index 100% rename from report_xls/__init__.py rename to __unported__/report_xls/__init__.py diff --git a/report_xls/__openerp__.py b/__unported__/report_xls/__openerp__.py similarity index 99% rename from report_xls/__openerp__.py rename to __unported__/report_xls/__openerp__.py index 2c8833074..a0b256ebe 100644 --- a/report_xls/__openerp__.py +++ b/__unported__/report_xls/__openerp__.py @@ -75,6 +75,6 @@ Contact info@noviat.com for help with the development of Excel reports in OpenER 'demo': [], 'data': [], 'active': False, - 'installable': True, + 'installable': False, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/report_xls.py b/__unported__/report_xls/report_xls.py similarity index 100% rename from report_xls/report_xls.py rename to __unported__/report_xls/report_xls.py diff --git a/report_xls/static/src/img/icon.png b/__unported__/report_xls/static/src/img/icon.png similarity index 100% rename from report_xls/static/src/img/icon.png rename to __unported__/report_xls/static/src/img/icon.png diff --git a/report_xls/utils.py b/__unported__/report_xls/utils.py similarity index 100% rename from report_xls/utils.py rename to __unported__/report_xls/utils.py From 74c3afa3637536f72f92fcca226b60c9cd4a08e3 Mon Sep 17 00:00:00 2001 From: luc-demeyer Date: Tue, 30 Sep 2014 13:50:51 +0200 Subject: [PATCH 14/69] 8.0 port of report_xls module --- __unported__/report_xls/__init__.py | 1 + __unported__/report_xls/__openerp__.py | 28 +- __unported__/report_xls/report_xls.py | 65 +++-- .../report_xls/static/src/img/icon.png | Bin 11645 -> 11647 bytes __unported__/report_xls/utils.py | 3 +- report_xls/__init__.py | 25 ++ report_xls/__openerp__.py | 82 ++++++ report_xls/ir_report.py | 36 +++ report_xls/report_xls.py | 249 ++++++++++++++++++ report_xls/static/description/help.png | Bin 0 -> 31176 bytes report_xls/static/description/icon.png | Bin 0 -> 11645 bytes report_xls/static/description/icon2.png | Bin 0 -> 11645 bytes report_xls/static/description/index.html | 84 ++++++ .../static/description/open_receivables.png | Bin 0 -> 50405 bytes report_xls/utils.py | 51 ++++ 15 files changed, 587 insertions(+), 37 deletions(-) create mode 100644 report_xls/__init__.py create mode 100644 report_xls/__openerp__.py create mode 100644 report_xls/ir_report.py create mode 100644 report_xls/report_xls.py create mode 100644 report_xls/static/description/help.png create mode 100644 report_xls/static/description/icon.png create mode 100644 report_xls/static/description/icon2.png create mode 100644 report_xls/static/description/index.html create mode 100644 report_xls/static/description/open_receivables.png create mode 100644 report_xls/utils.py diff --git a/__unported__/report_xls/__init__.py b/__unported__/report_xls/__init__.py index 1f5df8f6b..e100e7861 100644 --- a/__unported__/report_xls/__init__.py +++ b/__unported__/report_xls/__init__.py @@ -20,5 +20,6 @@ # ############################################################################## +from . import ir_report from . import report_xls # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/__unported__/report_xls/__openerp__.py b/__unported__/report_xls/__openerp__.py index a0b256ebe..e53584ffe 100644 --- a/__unported__/report_xls/__openerp__.py +++ b/__unported__/report_xls/__openerp__.py @@ -21,7 +21,7 @@ ############################################################################## { 'name': 'Excel report engine', - 'version': '0.4.1', + 'version': '0.6', 'license': 'AGPL-3', 'author': 'Noviat', 'website': 'http://www.noviat.com', @@ -30,7 +30,8 @@ Excel report engine =================== -This module adds Excel export capabilities to the standard OpenERP reporting engine. +This module adds Excel export capabilities to the standard odoo reporting +engine. Report development '''''''''''''''''' @@ -38,8 +39,8 @@ 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. +The ``report_xls`` class contains a number of attributes and methods to +facilitate the creation XLS reports in OpenERP. * cell types @@ -52,28 +53,29 @@ the creation XLS reports in OpenERP. * 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. + 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. + 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.odoo.com as example. * XLS with multiple sheets - Download the ``account_journal_report_xls`` module from http://apps.openerp.com as example. + Download the ``account_journal_report_xls`` module + from http://apps.odoo.com as example. Development assistance '''''''''''''''''''''' -Contact info@noviat.com for help with the development of Excel reports in OpenERP. +Contact info@noviat.com for help with the development of +Excel reports in odoo. """, 'depends': ['base'], 'external_dependencies': {'python': ['xlwt']}, - 'demo': [], - 'data': [], 'active': False, 'installable': False, } diff --git a/__unported__/report_xls/report_xls.py b/__unported__/report_xls/report_xls.py index 707fc34b8..13fcb5bb1 100644 --- a/__unported__/report_xls/report_xls.py +++ b/__unported__/report_xls/report_xls.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # -# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved. +# Copyright (c) 2014 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 @@ -16,7 +16,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -60,8 +60,8 @@ class report_xls(report_sxw): # header/footer hf_params = { - 'font_size': 8, - 'font_style': 'I', # B: Bold, I: Italic, U: Underline + 'font_size': 8, + 'font_style': 'I', # B: Bold, I: Italic, U: Underline } # styles @@ -77,8 +77,12 @@ class report_xls(report_sxw): '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), + '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;', @@ -93,15 +97,17 @@ class report_xls(report_sxw): 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) + 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) + 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'): - self.table = data.get('model') or self.table # use model from 'data' when no ir.actions.report.xml entry + # use model from 'data' when no ir.actions.report.xml entry + self.table = data.get('model') or self.table return self.create_source_xls(cr, uid, ids, data, context) return super(report_xls, self).create(cr, uid, ids, data, context) @@ -110,6 +116,7 @@ class report_xls(report_sxw): context = {} parser_instance = self.parser(cr, uid, self.name2, context) self.parser_instance = parser_instance + self.context = context objs = self.getObjects(cr, uid, ids, context) parser_instance.set_context(objs, data, ids, 'xls') objs = parser_instance.localcontext['objects'] @@ -120,10 +127,14 @@ class report_xls(report_sxw): self.xls_headers = { 'standard': '', } - report_date = datetime_field.context_timestamp(cr, uid, datetime.now(), context).strftime(DEFAULT_SERVER_DATETIME_FORMAT) + report_date = datetime_field.context_timestamp( + cr, uid, datetime.now(), context) + report_date = report_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) self.xls_footers = { - 'standard': ('&L&%(font_size)s&%(font_style)s' + report_date + - '&R&%(font_size)s&%(font_style)s&P / &N') % self.hf_params, + 'standard': ( + '&L&%(font_size)s&%(font_style)s' + report_date + + '&R&%(font_size)s&%(font_style)s&P / &N' + ) % self.hf_params, } self.generate_xls_report(_p, _xs, data, objs, wb) wb.save(n) @@ -138,7 +149,8 @@ class report_xls(report_sxw): - 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 + - render_space : type dict, (caller_space + localcontext) + if not specified """ if render_space == 'empty': render_space = {} @@ -151,7 +163,6 @@ class report_xls(report_sxw): 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): @@ -163,11 +174,13 @@ class report_xls(report_sxw): Returns a row template. 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 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) + 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) @@ -186,7 +199,8 @@ class report_xls(report_sxw): 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]]}) + 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]) @@ -201,23 +215,28 @@ class report_xls(report_sxw): col += c[1] break 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 - 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) for col, size, spec in row_data: data = spec[4] - formula = spec[5].get('formula') and xlwt.Formula(spec[5]['formula']) or None + 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) + 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) + ws.write_merge( + row_pos, row_pos, col, col + size - 1, data, style) else: if formula: ws.write(row_pos, col, formula, style) diff --git a/__unported__/report_xls/static/src/img/icon.png b/__unported__/report_xls/static/src/img/icon.png index b8a2fcdff9d1ae795a22cc4608a55d3c290b6e0c..44c5db163b5add8d42862f23593457fb657aa963 100644 GIT binary patch delta 13 Ucmewx^*@TKQ(+@xjV>b>04sk4!T. +# +############################################################################## + +from . import ir_report +from . import report_xls +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py new file mode 100644 index 000000000..1ca25bbc2 --- /dev/null +++ b/report_xls/__openerp__.py @@ -0,0 +1,82 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# +# Copyright (c) 2014 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 . +# +############################################################################## +{ + 'name': 'Excel report engine', + 'version': '0.6', + 'license': 'AGPL-3', + 'author': 'Noviat', + 'website': 'http://www.noviat.com', + 'category': 'Reporting', + 'description': """ +Excel report engine +=================== + +This module adds Excel export capabilities to the standard odoo 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.odoo.com as example. + +* XLS with multiple sheets + + Download the ``account_journal_report_xls`` module + from http://apps.odoo.com as example. + +Development assistance +'''''''''''''''''''''' +Contact info@noviat.com for help with the development of +Excel reports in odoo. + + """, + 'depends': ['base'], + 'external_dependencies': {'python': ['xlwt']}, + 'active': False, + 'installable': True, +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/ir_report.py b/report_xls/ir_report.py new file mode 100644 index 000000000..f35e60690 --- /dev/null +++ b/report_xls/ir_report.py @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# +# Copyright (c) 2014 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 . +# +############################################################################## + +from openerp.osv import fields, orm + + +class ir_actions_report_xml(orm.Model): + _inherit = 'ir.actions.report.xml' + + def _check_selection_field_value(self, cr, uid, + field, value, context=None): + if field == 'report_type' and value == 'xls': + return + return super(ir_actions_report_xml, self)._check_selection_field_value( + cr, uid, field, value, context=context) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py new file mode 100644 index 000000000..13fcb5bb1 --- /dev/null +++ b/report_xls/report_xls.py @@ -0,0 +1,249 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# +# Copyright (c) 2014 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 . +# +############################################################################## + +import xlwt +from xlwt.Style import default_style +import cStringIO +from datetime import datetime +from openerp.osv.fields import datetime as datetime_field +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +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 + hf_params = { + 'font_size': 8, + 'font_style': 'I', # B: Bold, I: Italic, U: Underline + } + + # 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'): + # use model from 'data' when no ir.actions.report.xml entry + self.table = data.get('model') or self.table + 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 + self.context = context + 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.xls_headers = { + 'standard': '', + } + report_date = datetime_field.context_timestamp( + cr, uid, datetime.now(), context) + report_date = report_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + self.xls_footers = { + 'standard': ( + '&L&%(font_size)s&%(font_style)s' + report_date + + '&R&%(font_size)s&%(font_style)s&P / &N' + ) % self.hf_params, + } + 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 'evaluated' 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) + 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: diff --git a/report_xls/static/description/help.png b/report_xls/static/description/help.png new file mode 100644 index 0000000000000000000000000000000000000000..3e7338c8a713379f50f669d091c9785b82ec5b14 GIT binary patch literal 31176 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 literal 0 HcmV?d00001 diff --git a/report_xls/static/description/icon.png b/report_xls/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a2fcdff9d1ae795a22cc4608a55d3c290b6e0c GIT binary patch literal 11645 zcmV-@ErQaCP)C<(Ewy&_zRo_QU6!mF+ma>OGPaS8u~8CiBn}u1hBz_CkYEBFAYd@d z0mlSR90-sTAF{B8gA-#m8?dqLVA+yoOV&tZ&C+{UbyZh&SJkVx-h1=?_r9v>nVtm6 zI^j5PdaA3cU%hwlfB*mczkj*Kwbtywf%hb5$~+h52S>`7rof)% z%dPWI)W3MH^<*Pn@5QY&>u|>L5ve3yi!-ept+>6oQc5%Y(?T<$v^|ujyK(xfvu{Y; zo++g=moYEQfoDp&+PpRc&M)F*dWzs>FZaSzSu z6^+nmA?HOUTZmYK8kLeK29eIg=cWAedH{rEDew znc|G$%i-Lm%AeywN;3jYZ|+|trR`%nx)i;yuqsTZtftydg_ZMbJlEseg5WIWW~gS7 zND4+GT=8f}BzpTG3qF9eS@Gq&KfW966MdE&l9)nm2F#8i24%$q-WNoc_E z;@pnkA-&>eyhd7f=(vyO`=+hUd<19v-lQL8f8*fXUf)QYhmMdR`P`rX^51^Y$U!pU zeD#7bhr^U(N2oc>K@XVo^wv{YTI$`KN-&@%^MaRKk@8&HeiaAR?$RW zfFL}&g`zuTLd7iD>M#G*Qy>4Evma`PXQxWD_h0|rcV2h@4Rg27x-(|19NL?Z!E>z= zodxzPN(~h~*Q~4b?wZPbO??)ZRc7>r%PX&@mGa8vDdlOFapqX9QS7-L?*HwN4nbzX zI*+b>^249|(3e|(I#Xb8dC`x*@|JHrF>_0qO=1ZKX`JaSPNO&rrOqT*Lb}DYG@Q=< zwY4~~1&5OXjH zlQ@pzC`n_-DZrK`0hO_HG7dasqrpD!LqB(@H?TUohjF8d!)+BAE08{@(r`x4K@ifo zN#i46RTjlb7xvQi-}&@!{Nbhd&z4XB^Oyh7eJ5X4U=x{4yS?rp49$EQcYur8)$7Eb zyJ5b-LysOtLNvZZUj`jIirYmwEP9I8zf{*yKFa6OU zTzKzv>BZlF&(FVf`jtFzTHShG?Q%`5(VFn+7V>LJBeY@S=9yix=OKxaAYdFiW&KB2 zKlR@p`&+i{<;hn&nl*y0wRk(#Q7Py3+y*&0OqeG&2D(!G*IJ5$<9hB3cFj5Q z1E)x(_aW%>@>#AV6&k>*2F&~TlYjBd?w_2QdD|P$JXB(H&3^iCDPEk4GQ=3nI?8_glIo?0M^T7#!YLJ9$n``iw08q^d2@rqdK8_H1!0V6z zGN}-X3G?P~A{a_~;6{ocaHY&3D2WO-YT#kZ5Hna;pJnOmYg)0!uGJi*> zA3yv1yXoqKH~r*o3wL1F8;zA9>^UxC2c?bryUM9nYBc#Qh1bk73GHyXtE9|iWnQVkq%a+*R1WpPLWC0M)=4oy;7W26@>kJE>T%-ttB zb@{x~5fFsyhphkDrH}P8_Vp*e{lwI*Q1wP*L+T_;O;*V=49Wa*!E=kWx`IM{2xJ&h z<~fBSYBG#dGMYXKjHaS85{K0xk{Dh#GkLfVn@t}R-_2!t-6^15Q#E^lB8F; z{useU$sjYr9BP;mtbk4`t@J+qF%bsw33Uya`uj|SU1&aiVerf=r@!@v*_$xbt#%{D zWwK1T9#n!{68wN<1+QQ*K-=^oSv92}OzsHauQN>wZpQ_y0z-kLpo{?Y)Q;aQF4UHA z2NN_{$vuR*4pDXPnB~=y+!47UQW9>YX+~j;(QrpeQp%Ko5vFNk083_Z9A|M#2(D$V zT)q47VOQ9IRql&H4?<`DjvKO#r&~{KF?Pqo%V+(u-e6~KubyJC5O0>+*aKq{T2#s< z>m+eAtcU~qAX7EzL#c)1YLR6uCF_tbDZ4a^P56@HY62fKs6e`dz{bIcY%-E^Z8=rf zCh*aEL=f}k#^s?~&?^WFr#r?Rgw9oykIuw9sm{W0y6+8dxch$J^HaGigiEP5bI>B} zLu0|kFgeZLMt|ed=7l+zU3cu1V9w57bFbI&d};GjoIDp+Mpp_|&-bk|VA&ysBuUn` zR&*(JJ=b$Q7>?_>aIS*9d*)uYAbE~Qae1-JH1Bq~W;w9a!JdMf)Z9xsnmK=<0%pEliArRnbjH55GP+?`j_!MQ)_)N%ahnS zL^}uACHZzcz~ zSn1z>C)0+x1BbGr^iY)p`Cs&=*K-WUMY-I5P5_pc(q#M>`Kat zzh};g>02Ls{dfJ`@4szkVIDW~Ws$2=mcYe_(#xK#f8x(S{?YG#^HE3p0=MBRmSMZ! zxU~6nYj?B0x7CAR%%GeA7ROq!q!ndpD8p{t*lezEtbXru(=Xddi2Jk#mQ+VXa% znZTtBazX{a0JFsv;Jiz$=$n^NFNfYD6Y!Kvhv#vECsTQ2tlRB=>QjGv;>5|LM~=_W zFS?y{2SL|#d75#r7xm*L0QM1dk#i%MfibvQp;nsI`_S1UI??Uax$E@Tz5dJJ_sFGR z_bW4rQpOX2?Zld=^W#f@^vPek>H6F6J@Za$G}pJk>9yZ<>c~k0&RUQY;azh`!*oeZfd~<+jrn63NH+^q7N(lG zwK8EO;}KS262C<|IE3^UUkzqzw3M6eRAoP zN{l+gRl)ekg$;IsW__^P3U;90gmbRTb7kd#5@LY_L$v;28?AP$zES_i``-i!z_tK5 z0>a`B@s2Dj6bkSi6iHFZP{L*TI3Z19kT%5%l+YWD8LQF5Gn@sx26O^2&}$KM3CdUl7j7o=7(^ej22Nr zRw|Z(R$fc|Y@xUwiedUsZ5inaG0Ulg{uVvFADkf7GdQ z>%^vcUX-L?aAa^RhH@DCO}<6<G{Qv|F8eK zymh`G?@E>!7fy5;mW1RPJyzun-8RyWy`SD+U!)xAH5>;0h;D(4=6Y3~izr%2% zB*KrRl#qhH8F8BU!c3fEHg24YL&%CJNfM10YaYcuHrydv0YUWpJwOS(Ay8D9god4llg&(r+CT6Ym@pK)j3r35F z7u{KIS)s{T;G?cnUfZ}dJ2$^@cmdi%0htuN-LM5mvV8ts-~1DAe&E4caY7|2^whRf z7(obagAp58!@~^*aT=$T9FlC9kI=*Z0f%gcn;Zs70iOrOo|>2f-5928%%F7E>-CIF zgC>WuNS(0kts`N#<>X%JSelWbJ1v87_8b>P@CXc=I+kULq zV=TpNe3?+BNSaD|26pM4ed^0WuMhSEm1%ExS1xXJo8d3~;BS1}gKw=B#~Ecn@gVKY z4U?e`8ZY(SjH;6{i?+Lst=85~YbOb^6f7i!9V@^ZB`I*DOxc_wHhn3 zSj2?3(g)qaRR=-j`z7BiLmQ0iQ8FSyNg`Za4S3?=JcKMln)U)-t&JDGf{Ccbm8p(A z1;@0nY#2(RfjDyP=zVv*=`)XhbiBGG*)WAjhD)b+yR8e)tew2!WTz9HKY!uI>C^9i z_q%Vq;dV#5lv&_;?+3dgVar`fjzc?y7|Y8mPo8}&NP;vIWxrDJON9dBOu)ZOyusmk z#6O$(0tRPMQZAMN{RqBXuy)X1?f}O6{Xwx%hWmtFko(+c@!F(|C>??+VTJJ)c-Eju<=f8O`q-0yH;4uxc3S6cQYH_j993R9e~HI_ zKkD6o^FR9Dw|?*9+!9X>K`>z$H@#K+)sKc@s@sAhQ~wS3yz$IUFX?nUje4uy*{yGH zwRd-RI<582N-J6cov!**1+QH4N}gK)$jYuR7t6v1@TRB<5v6Iox3>o=04zOXoZKBn zBQ^m^nMXyb5k)uyhnh{)CV}2LeX?-F`7k=f=mBjKyf{d^tMv<8t+i%%E0WQzx1D+9 zbDt=(s+|ylJ_&=Mk7weqKKuD!{{Hv9^;_OHS(=4+;7Sx9bp7vNNa@t2EHOC_|$L#DI%H z1eTkVY3N)NO$JTtBuO%O0mF7pLO?(x3^QTE<(Ff)9Gz6EBWT@T&{}Pt-|DP3+8eur z_8V6)o!e{mbZ3ScY+3-KqTWtW^U2H>DHUq zH&(JNAyTA;DGHEUz%d^lAN$w8{tKsH^wRt9dtDqwzFV-Pzjh5`)Lmuxkg%CtGDtOA z6J;|HChcQS_n13Tnc!8%rsu8M3zOV3@=$RS_WS+0xmjq0O^%6CJC-#KlfJhZMq&}L zqH4Jc1Iu-4SURz3bAWWCy|S|P%vNi;zO&lu)_d_D0&uGEWZCTe?CrO_Y!F58^=V9A z1Be4<&RC{A38Q)62i~{VSQQ08K&FRPGMh3mG}-(xJA7`n0`Wistwm76W9>jg=!Y70 z91v6zwV_><q>LNo&WMl*>YLiyS-Y_N#hq@e-QDRYy+p<&%|s4|FK>R)$>j@|wwsNj zUnQ1GjYX~r8ME0!@sWog|DE6c(7WFCP7r)Ycn-B6LGdWJ3kZhg`%Zz!lpE5n$P6Ag zI`y^Y8a%4&J2kfkRU#r_etg6el+fUgq68A$nDSVP@L0iFaB|2*Nla;g#7W6s7=;B( zgAsZC*ep3Y<3x&7Ne4g;;GC+>`l2W@fEA%ARd*aLaMBIh%bORs+G|_U@?L*8j6%w* zb2Usr5*UD6Vw^92*=>LFr<++Exx&*n%r=o0xXbi)h4=l!`>Qjh(`W90HF}Qmfz*wg zW}wnhq#||Ob+^xq&k;{CJh~I?E^n{&(rzpxFNHL)qVRv6Yi`#MefkRkVE-f77ijzm|`tNu2v&P_O4Bi-PU8UwpamJ$=vjuFkPXLCP(PN zlT^j)t(EiZUuo?%I=!84*h;u2?f%m-UJ*BY{JRJZa&f;CT#>V!!XTS9JH@*8Mr6StPDb_X@@ck#={FpCi%jXpZm2)820V zz*~R(HTS;8R|QY|){{U|y|9O9ayRZ!Ag^PymXxnEfdZvuaeJx;VnH+y!)SS9dHU3} zFn+;G!Aw+y14{E5e(hSYBza`EJ%X}c4&YQe8I>0}fVVI;IZ1UI2*@E{3hS5{o4)tV zeT|*QR(+$>*-OH7uh(w3T6=>|qC(@Guqc$_hryD5x*#^C>yO{~*+(AnfVzgpnLH~Y zB+#Nz<)8V~7Z9P(9-8b0-JOkgx~G5YT|ayGOYikEpL!CAP!Z_CR6)ZskpV2hwH2{@ z6MjgOA2-B~4|@|Ji@2ZE+x60|W$Q=Ggzc^2yaU1$6Lej6pM2<~jLc}o5IycRnr=K% zDOR|lQV;_`xa5!BbmBILB446jWJ((0U<8av(=>>J_HMh^?f1I9y?+w}$-t>?B=#SoU%NaNE2y+M{m9b5- znn66E4o{wDJabiEF=7_OO{`9|SQw{)I69VDsH){LmjVeJgHYiX=X85rh+u@2`*;NP zN`h}?!ts0kV6(n;Zm0FonYWZn6*FRO9JnJ>igO`cK$>SLMap%lG(K6e2@jGNK{G&? zrEsnocS->>>&Lx!T>7U!^uzDDu(?_(diy$jFlyl`-rWg5{WqU@=z+JrG^ zue;^=?HQ07WIE^ajsW@8o7G^5}V!u4&4re6vZRlAfu2mjl(Qs+BU7 z!?StS=`av@7DX6cGdR%N`5 zfSPFHum0M<+uE%Au5b4x7yvhk7Ak(iG@>wzQi5}g#j%-oSS*%4BaTaHpp2X6HmRj# zL5OnMAGHEn2Z_)V7*R03^Rh;mlo_*?E1IA;ED*YEX>k$G-_lr`y&66rQD2iJy_yk| zjTX!=;~UH;@u5WWvEsygf8wX#{+-`bZ#J6Eo$g-u!ubmqFI;Txw4QkMNqqFNzxu*& z{`=qlu^;_cgtne%a_t6IRg4h;djLNDC<^10N{GWoXEkg%uz6r0}RrG+Jnam3gt8JPC_y=JQgwpVY|S65cHo7+c^9v$vcq6e&7 z#*+GG{oJ$9hOiN;;_-r`;r-}lRK=mB77U6}2xokfWS~Q3w^S+=M(u`niJ=jL!NBuN zqlk-*n$q{jJ5=eG>51txl%X@tB4#vdAO}002D}nv;SqBvxuWENcbf7F8x>dqCY0=> zH8V*@W};6LmhB1Qwp3|kO?Mwz{*-NfdDtT3+`R0J8m8ce6 zAXw6%;|1R-y0njk^4x}E*v(7Y+M4-4bRX1b!feMB8+HTbMQ;;*>r~%$$6YTueL9I# z1pm98-Mz5e3;NsJ%{H8N2W)?Rd#Bl?48-QDu2+N?^wKxq@<5?jvARhpr?le=koSHN z99k@tN~Oyh!G>*(c@n^M;`UOuG`mo63XGZub7}5H?QYcUhpj=>F*5Tw_dVxw4~DI~ zacZ4V?fUMSo9}wbZFf?W2xEg}(2sYi-fe1<of9A!F^ ze6SCMJO$8Vw+QQ=D@|t(oV2n%7ilJwEJ@RB(C@ZEVFvx3PG|Yz^7weI;1`E&6?Ov{ zPY@JDF_v${ri^K1M9fS_lX0XTr4E2@#}|$in2+ZP*mdr7pr8#H@yXD@66ENmTx zIlj(&y|}G7V&#K=u>8#WOK!Z|GwmAO$vVOI*|oEg3^S2>RQ4|v-9klF%5E70H5s## zFyR1V1eb^fb)s1v)kCduMbR;-_=G|LDp?Ji$-t|fvR9-|t?I<7#bZ?9VM@MQ=2;5K zY&pZ4XOd)D%4~*ipGFL`9wVTjdkA*u&|yItPi3lt)bKqB1I(WUFNwCH)y8TDHpp@81-p&itw9>ZI)ZNl|Mj?EfI=~flxz!+=M@RZ{X(%&5(t6A z^oA2R&Q8yo#w;pBVg7)5nwvow+jnEza17v>ZCyl;DbluBZNX?bJx%R^TEq8X&1^Da zgp61A3Y+sYmEBe?O;1cyG)Q@m%+wIoAnZx1R7a*hN@!$=uXU$JEXs7o!4-t5&x)9$ zaU&#%Ubxp9G=iuX$}p8FJP5SiGy+ifB*l)dC#WaS!TGppuhm;QxAp^X|Ce)9bFQX- zvv#k2arGjY0^%y4TwkF|$#_CX+=#44kXgckXj|R1R0r;IpI0Pv)C^u2H@5EcfFSm5 zOH4I8K6}SRb;_YY84*}Yai>ngq@U>o#F;o8sjOqTw_+o=Nm4Nx;8;$NEtbn=N_dfK zf@=q<4nsK@WPv8XM$L1^|EghQq_9#+=-fdt?F2X{y@Qo43hqG>ryxEMp=wc zdXDcaPK8S^A!KX4ve<18`OVxo7hbHXq@TPC0@y z5yHp#(qs^AKD+Vl-}u(kx15&Qusz+Z6G+6c#^qjipSESwfJ)vD)0gbn;S*)QDk3MZ zvQtW@A0>e{IY84-Ya+fYtV$0LgnjA6+>}q#pc%uO22qe#6_icCa#Nu+vQuoRQwDRT z(20tJc&>Jc0N;QeK%qD2?S}1E*bHEsfI6bU>Co#tl#-xpxdl<;9_7~io!;v5`EL;q zAx^!l^ax=xx!3KMh-Vc>>Og{!rlZu9fM_l4UdltW6lG|agM@K%ax{*ey-qjk1xW~1 zh5R8}hcLvk8>R`HO^*P6Vik{%Pf*BUj5WmaBoQ-_G6kyF(7y`L~U`%s?F5#@NeHUR8)(2A#UPT*TT&^rOI=qMOR83I$N^n$(4>AezPsB) z2yPRWmj%Ix-HW7Ng7EKDiOtT;annGY?_%d-dUE!jn_s=Qwzj?9*xhZdw!Q*81iNL0 z0=R9lR01~ughTBkfNCe1sLf!`@FI>6wThA~A~{iA*|P<6EYpe z$@JtTHO-RRh(u;k$xMxUO!IE;D=I<5Z&fh8SPUV|WOWj*c>if;k41urt5_)PM@nY) zYY5ABp={K}n6Z;%XC`aY;QLfcg*TNM*8kR4fBr`44`pcF1iRRI$hnIfKzjk!s+QGDWP z7v`8o@#OfF;N}WcB}x16J1W!mGqzLtU^yuNDhc`zzB683gdq0G$izER4gxCwUM&b^ znjvUc0kubDEN0g)-dHMBT<%%&59I^+V0k@BMWLc!881vNPEmi!ncH6m6^)W8Ns}my z>dkr>4H}K+)<$!2W{DI0Pzii{WBchZKh>23u#sw|A}IGxx&yzDD9IRv5+=Vu-a5j^ z1n{X^samQ8HWj8(O08aihm>}^Ftcqy$)PLAqihn7%7ec&ORCi|@;m(US67%ez^vEr zS1V)tQ`p8d%y%Og>yng;&)_?c7ThB78UiA7S27VArdGqqo;1kp`_xXES4xHQM48Xe zERbGPharO#qb*RJQ2N-BQ*VCFcWyQ|+wIQs`o&JKwHd5K0jV_$KC)Ey3*g)c?x?5_ zHB5V{O4xX<#)g^LIE|nYh>0l)PJ(buMom}-k_RT2OM^CwRn=;hAJCc*D;7l-Aog!s zb@_1HNuG}xz;_;nLpGf~%IcF*@)4-p?^DRXRe{5GF8>i`! z!zYg%KEWw8S9`r~LM8cbv$fr9Zf|aE?)G+Mdb0ZBMcYJ_hWO1n!5zG(U}wo@m;**iUK90DWyiD zoS!J;^doajU2lAc$}l&0geZ=ht!BH^K|sB7X*KBfC(08{+FlX9)!zBiSDsd;;kpRY zNQFJB*N_Se6V5x_?gi1b3xZNtgDe}bjZ+7i=}Nc4fC_{m{vvJO;0lc>M(#vnvaQN{ z!<}z@?U~n>C)`eFuer6;?FIVis^z2F7DX^gyY0vZ&c!#$dVS{|w0h*I1HQdiPjDzysZOn>Qn8Wr2+buVFBfVm$Pu=q3lQ*Q4 zbR7WdfHjC9H(Q%qjYgxjeSYoPRMf7jdNd*_7NHp2SI?f zvf0Wj1ko%t+>-yBpZN8OPtG{fQB;1`!LD4tRDX8u{7$=<)!C=N%-9ZNQU009W9;~$ zn~xkhy0o}7H@`49KX>TRp_!Q(fKwInDObv#?~-9rv}bqe?b`uGe1Ejj&(h7j5x~?% zOpKWKC=4@tyr>IWgJ#Ow#Q4G_(?>{#2z&qpAQwQtUeK%8>u~CgMt!5+z|TxPFEvwvVKOXfm79rO7Ij(dFHY+k$;r5P$Zrr-zMOdPK+}kU^4kcLzz3#ZgL)&x2kN5Q*yb`h;y^hyXDR zL+XMhj}*c`BfM>Gwd_-8 z6pIC$M|NFj^u1bC5M+`M_c4yBk{*7)iuK?5*8$k9j8u7QhS(N?LJiPXtWvX*%3Q(K z&_qcPL166y#}!`V*wjt?c-a+hSIrxvml_Ta5oDUi@W7ZnCXB~k52AsHdL$5#&B374 z>BQzMSD=1ixaKr`Dp^_CID7USu{C1@02gS=!aOu)c4~SG0>VqMh6)U;2I+X#CkQGW zs64g)LsG%k>B&h<<-42XDWu-F=yeEBA_- zy)TrMbh%$BGWz0{d}*ZJm}xfsVyba$rXR<+M_>+mLXrv1JM<=A#R-4tP z8<47Lj^ogZ>R5GdjwG_MFgI)d&CJe@RY`GbwJ}5tRKCE_9n;MRQ26w%?~K5R3% zOhV15!9GcIOJug82xoyttDqM`(&cj5*5yaCaR1LcA+LSlgQlSF&==Y;HZd{$o;cbq zPSqM5Ns>h77z__+P%u39_V(zlU;w*<1dS-Ek$tDt+1lKG=huCMPD-gt0BY#1H;G93- z^YhJD2c%=ZTYz*2f*{Db(y|~WOuzv1z{<=1HkK9U(pg#m%DHFLEVQP8WuKg!nwy)S znwkRi%+Jp+EiDnjO?$dJ_QG+(#heQBM&@gBO;%~SNcL)$8y!=`b#NT(Ass|-S5T&ug z6{=&}Lo3;EyKxc$I_JD%G zzW_?DMqK{-q^t54|34nqPhYjd2M_x_9FPTV9=@`uf#ZJznpD3AGd6+600000NkvXX Hu0mjfi6p;w literal 0 HcmV?d00001 diff --git a/report_xls/static/description/icon2.png b/report_xls/static/description/icon2.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a2fcdff9d1ae795a22cc4608a55d3c290b6e0c GIT binary patch literal 11645 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 Hu0mjfi6p;w literal 0 HcmV?d00001 diff --git a/report_xls/static/description/index.html b/report_xls/static/description/index.html new file mode 100644 index 000000000..1cdf4263c --- /dev/null +++ b/report_xls/static/description/index.html @@ -0,0 +1,84 @@ +
+
+
+

Excel report engine

+
+
+
+ +
+
+
+

+

This module adds Excel export capabilities to the standard odoo reporting engine. +

+
+
+
+ +
+
+

Report development

+

+ In order to create an Excel report you can +

    +
  • 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 odoo. +

    +
  • + 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 odoo 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.odoo.com as example. +
  • +
+
    +
  • + XLS with multiple sheets +
    Download the account_journal_report_xls module from http://apps.odoo.com as example. +
  • +
+

+
+
+ +
+
+
+

Development assistance

+
+
+
+
+
+
+
+
+

+

Contact info@noviat.com for help with the development of Excel reports in odoo. +

+
+
+
+ diff --git a/report_xls/static/description/open_receivables.png b/report_xls/static/description/open_receivables.png new file mode 100644 index 0000000000000000000000000000000000000000..a00c3ba4497e9c8f48f61915feb5c9ef42c6b672 GIT binary patch literal 50405 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 Z1CO9$UFG}$2bT_m$xACql}H%*{U547X-)tD literal 0 HcmV?d00001 diff --git a/report_xls/utils.py b/report_xls/utils.py new file mode 100644 index 000000000..943daae7c --- /dev/null +++ b/report_xls/utils.py @@ -0,0 +1,51 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# +# Copyright (c) 2014 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 . +# +############################################################################## + + +def _render(code): + return compile(code, '', '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: From 232b9ddcbb1dea56b48ce7f35aa5625e7e8a25b1 Mon Sep 17 00:00:00 2001 From: luc-demeyer Date: Sun, 5 Oct 2014 17:45:49 +0200 Subject: [PATCH 15/69] rm report_xls --- report_xls/__init__.py | 25 -- report_xls/__openerp__.py | 82 ------ report_xls/ir_report.py | 36 --- report_xls/report_xls.py | 249 ------------------ report_xls/static/description/help.png | Bin 31176 -> 0 bytes report_xls/static/description/icon.png | Bin 11645 -> 0 bytes report_xls/static/description/index.html | 84 ------ .../static/description/open_receivables.png | Bin 50405 -> 0 bytes report_xls/utils.py | 51 ---- 9 files changed, 527 deletions(-) delete mode 100644 report_xls/__init__.py delete mode 100644 report_xls/__openerp__.py delete mode 100644 report_xls/ir_report.py delete mode 100644 report_xls/report_xls.py delete mode 100644 report_xls/static/description/help.png delete mode 100644 report_xls/static/description/icon.png delete mode 100644 report_xls/static/description/index.html delete mode 100644 report_xls/static/description/open_receivables.png delete mode 100644 report_xls/utils.py diff --git a/report_xls/__init__.py b/report_xls/__init__.py deleted file mode 100644 index e100e7861..000000000 --- a/report_xls/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# -# Copyright (c) 2014 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 . -# -############################################################################## - -from . import ir_report -from . import report_xls -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py deleted file mode 100644 index 1ca25bbc2..000000000 --- a/report_xls/__openerp__.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# -# Copyright (c) 2014 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 . -# -############################################################################## -{ - 'name': 'Excel report engine', - 'version': '0.6', - 'license': 'AGPL-3', - 'author': 'Noviat', - 'website': 'http://www.noviat.com', - 'category': 'Reporting', - 'description': """ -Excel report engine -=================== - -This module adds Excel export capabilities to the standard odoo 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.odoo.com as example. - -* XLS with multiple sheets - - Download the ``account_journal_report_xls`` module - from http://apps.odoo.com as example. - -Development assistance -'''''''''''''''''''''' -Contact info@noviat.com for help with the development of -Excel reports in odoo. - - """, - 'depends': ['base'], - 'external_dependencies': {'python': ['xlwt']}, - 'active': False, - 'installable': True, -} -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/ir_report.py b/report_xls/ir_report.py deleted file mode 100644 index f35e60690..000000000 --- a/report_xls/ir_report.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# -# Copyright (c) 2014 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 . -# -############################################################################## - -from openerp.osv import fields, orm - - -class ir_actions_report_xml(orm.Model): - _inherit = 'ir.actions.report.xml' - - def _check_selection_field_value(self, cr, uid, - field, value, context=None): - if field == 'report_type' and value == 'xls': - return - return super(ir_actions_report_xml, self)._check_selection_field_value( - cr, uid, field, value, context=context) - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py deleted file mode 100644 index 13fcb5bb1..000000000 --- a/report_xls/report_xls.py +++ /dev/null @@ -1,249 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# -# Copyright (c) 2014 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 . -# -############################################################################## - -import xlwt -from xlwt.Style import default_style -import cStringIO -from datetime import datetime -from openerp.osv.fields import datetime as datetime_field -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -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 - hf_params = { - 'font_size': 8, - 'font_style': 'I', # B: Bold, I: Italic, U: Underline - } - - # 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'): - # use model from 'data' when no ir.actions.report.xml entry - self.table = data.get('model') or self.table - 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 - self.context = context - 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.xls_headers = { - 'standard': '', - } - report_date = datetime_field.context_timestamp( - cr, uid, datetime.now(), context) - report_date = report_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - self.xls_footers = { - 'standard': ( - '&L&%(font_size)s&%(font_style)s' + report_date + - '&R&%(font_size)s&%(font_style)s&P / &N' - ) % self.hf_params, - } - 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 'evaluated' 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) - 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: diff --git a/report_xls/static/description/help.png b/report_xls/static/description/help.png deleted file mode 100644 index 3e7338c8a713379f50f669d091c9785b82ec5b14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31176 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 diff --git a/report_xls/static/description/icon.png b/report_xls/static/description/icon.png deleted file mode 100644 index b8a2fcdff9d1ae795a22cc4608a55d3c290b6e0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11645 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 Hu0mjfi6p;w diff --git a/report_xls/static/description/index.html b/report_xls/static/description/index.html deleted file mode 100644 index 1cdf4263c..000000000 --- a/report_xls/static/description/index.html +++ /dev/null @@ -1,84 +0,0 @@ -
-
-
-

Excel report engine

-
-
-
- -
-
-
-

-

This module adds Excel export capabilities to the standard odoo reporting engine. -

-
-
-
- -
-
-

Report development

-

- In order to create an Excel report you can -

    -
  • 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 odoo. -

    -
  • - 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 odoo 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.odoo.com as example. -
  • -
-
    -
  • - XLS with multiple sheets -
    Download the account_journal_report_xls module from http://apps.odoo.com as example. -
  • -
-

-
-
- -
-
-
-

Development assistance

-
-
-
-
-
-
-
-
-

-

Contact info@noviat.com for help with the development of Excel reports in odoo. -

-
-
-
- diff --git a/report_xls/static/description/open_receivables.png b/report_xls/static/description/open_receivables.png deleted file mode 100644 index a00c3ba4497e9c8f48f61915feb5c9ef42c6b672..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50405 zcmbrl1yoeu`|m$Usgz2Kw9;K8t$=`ZcOwkl-H3$J-67rGHOSB)-ICHE3=D%q^B?r{ zjl1r>zjfF8pS4)bIUMGkv-dvFd%xb#vp*{-NZ~ww{ulrN;JlZXPyql?H2?sVtA`lK zBV%uIoshp!oK>X60F|RJ_K-VhpttgG0f3rltZQR*Df!02gxqgVJwb`~i88 z+U1>=i@1}Sv5Td>9hI7;tr>uegOf*qgG+$xH5C`904J9K_v`E|sVe}WNB+IUTQv`Z zUpI;tFW$H9gp0zwlK7S)@s^uqIBHX7%2iH(+QksPvK7Ll<;DT?dB`PdCi%UPfV89g zr8i=vZgUi5s(WDX2fCMAIZLLi;0%o0u9Pe;1ya7){CL1L71_}4?nn$;J_{cR8;FP) ziMUo(*U*?jOnqqMqUEMZJn6wd>B*)~x;@{Be)|450#Ra6HO!5Ch(DWUGu_?&W8QBu z_$0`-`Ll7*ag_h_(MZln3+3@v~YbMe~;fPe*z zOKcxpDo`)rLJ2ztuh(eelzJZuc6*%w7NKQ$0toO#`9Gatc*#zAOLCYH;&Kj6+B0Ml z^Jyh9)_JQYa>+$064<{&covTh}#4LWkJfLpwPLI7O*F`MG<2_zY=oOQV*Mk+rj zH=LS!cd$Bj;T|QF%4*Il@a_HB|{ujg-vH@8b9Qtf~n?+6aowu(#uB{afP>lrMWETVjUySbQiFSg_;Oov} zjPmB@TM_O~!q=IdXRsSW;mbotTcPaldfCe`yCg7lZ#Wrm?_{t5JDYWzo^qPgTas{R zA4YvC=y!CRRt(N2)i9~wzY}~#-XB?dw{v&lznN;}hJ}{$h|L>5-0ylh1vDJ5Xz~}& z!;vH0t>bjK?J4bF-F4#o;g+4akWfbJ3mK}bk#RIrR`%5{TOgNPA#}k*yUBK zv3hRi-C5x6@TbtRpAk&BUeCMWyAy_n&x9`}AJ(51xn7>jMjPn_TB7(SB{jiaOoRB~ z8+*E~XXxdnJR{{9%a->FHK z+ei|EItlO@2--v8vvZrpX&HzCU!V6{R_KN3aBUr$?H;g~1l-A=uIQ7m-9IDG97t(0 ztM$E@JGpE)Gz{v2zRIx=&2BbM{GjHTlhWn#M1eVsR0XfYAfl+Ja{LGQTkV4>luH|I zyOn%)OFr)rQ6M)nh|)j1j1=`$eT97w5#3}#gceXEF?g3$r)Bv4@9YzaN+`AK^-8Dxe|hX>d8V^j2dtgwt;Gg6qr5C5L0 z3;(@mT48z+r$8ZUc5xfFsWP8xg6#R@d5?n$pu;JSQ{A6VmxYu{443`Xq^KoPe?jn& z!_y1i)YK$YL`YYOA5QAZjP(Tv*?8UIk(8a+ah?8-{*@VIa^c#Eb?FGhftw+dN)Z7uxec}LI4EEmx`<$EBtD1mALhJou>}pkW z?*<4ij@MCH+1c+SREmDiSl14~N#i~k+{XkWVanXwTkA^YUdF=#$|=wUs~({18sLi| zf*Dh165zvtF%bH%aofA>_3VF$879QS$wXIISMbe2e1zoDUbhgPYsV^dnBbq7X7u|J zsYbSGrd=J%oE32Q0r2 z(0!-X6LI+cjw#=!iUNh`f0vEXxL57GweHav<7t8hVSFa>uD=2hLSH`e0l8s6=Ypn< z^#M&}1*cs%gmb^N^XLn>SPhM7tLq)#QfQP2;{*94->%u)iC_&SS#K8Am2#=YSxzdR zx^IF!&I8oi_+svuB3Iai87%NZ7^|+2MZXj7hK#Xo)O{ZrT+!Qv#w@blPLP*s6jY|> zn3*LFex9X9r;$N~nSQBNR=P;vZeqxY!gT(g5~3cRCF=8@a%T=o zuBxNCGK}(>2IQN`7xn6Ip83fNg?dpfwQkROc8`?a8@N4^b6gAMEZqvOJ0QO?;Qv7G zVL)j&xB7#mFlv`i*+1MJ&CB4dS;AiZyt97QvNgeN|D}so^krx7h={7gzQVEobX%Rh zPjR8f?&^<}zL*41oKYHMp_ufAX=&;a(?!V=&aE(nuf-O7c3^(xS)AJ9B2h@&-1{5^ zgTUs1n;1{$QX!4}rR8lDgNt@FVIuAggKlH{1FVF)q1d^u!^2w!uzRCZjDmvA7L{OP zTkR}wH8}iJG=&vHq9c@-0-BC>5y4!RF*xD30@A1r)2y|9&J;byro+h z-V5(ZuQ<(jwShvL%}aqiCmpml^k&=aTivJ}haXS)e{;%iF7G&jZjG0yQm&qVzit zB!%sr!&~`u3=Jt~aB(FY&VrOR4HO|`qIWF*jH8lrJr{mDdHbp7-?6EIAI2T#&-QnJ zmG6hNfjbUnIBJKI@9;y^8LAR@5IJ`^=E;#;IVBnjH!Wz4M9FlMD_=$R?rObmT_; z@&~0nk!h%`6;!tczpJWF;0jZjG%rXh9+2mGmruxN=$U3|jj)t4U`!i#_PW;TC9C9_ z_I>KFE2h`^k!O{chV8KBWMYN>NNJUc%Wwp&9j>V9^A(f_t@HDSigeVDul7oQ62slX z=~qi1g)S^V=hwGaCkB-}nJ{Bg*YB9%9kzjd zh(!%&8nFFU%Oj-PqY=P#Xt#6;_cO3}rTgqpa?MVg%_QOBCb?6D*Uqnp*S`Ee72^$T zUR*0#mVnh|y_r&06Tk`*21S>GuJ!iM-91jE{O@8`qNTa7E7!sFm->-HC!={A&v&5M z2Y7B4TokkRPyNL=uQ*kX13PUgcU5q8@1ol+fLX>vC)*wMwa+mlKN4t)91;67BimtKmXYB zX6|cWR(n-DO`-ViRLzp;%jY-bkf|3;E}NsMBDr7E_Kmb1^7;v%uVnSP522_=@u;l2 zjD!3ci{7WN)IZoNrfz@Oy8m52oXR%q-HK*zX6s2u`K~J4$31xrqC3>}4`!{N+=pOV z;aR5`_50VHwz55{wLXtV?vyPpVx|LAT|3Fq>^dEd@R`3tw2m$Rg_HoULu1qCYK9@G zN1;w7-*ab@>yhows_N0)as#$O3p*NP=a$wbqr3rW{$;01G(u-n@<0T#pUvH=p|zdP zC#+DXl6hTY>*PG`AC=c*)G2o9|lty2cS=Ur4_Pi|{3`DVS%_+%Whi)B^e__7_W zup{-<|BR(sLxP^ko~ywALa^`Env0}V!p3pi{P0^o{=sPaDv+OP0Z5CaWaX6xkuU64 zK2dnxrfANp?ggA2vsmGUf>);T-4Wg1N{g5`4(+p}-bk?GqASV9ZYFkZGMu%4M$r-` zL$B{86OBbHBUC3dOgOPN-Ivg&LjQLz=Lr>I24iYTlfY$R<)Dn5=$AD?CCXkPyY3l$R)%lPt$NTD5XU z_GQH)a@CupOMkbFbuB_YtkfyWpO{i(;nluG9cVs!_0>@PSCxmOk5$|v5DDJV~Qxnkdg@~@BR z9zpdM_b{f^<)-3wDHdcelfJY&CG+{k=R=u6Z}fb2ADVvut~oKkDY^7@nsU%}`Za?% zXV!Z!%!pUw`oc+ig-lS-{e%Z9Juq_d<{-hXMj=ZVA%LlhG(<)%shqQAnECPWfQ)5m zPqBAA-?Bz9AMaQ%WyRiMQc%{3hy9l;Y;TRT{9VZM;&;C-8_(nj9-!-(__y;8ikF4E z)7cxmt*0^Pnb<<^ zy8{qjuFfRg8o4k!v&+x^Qg+kuqA;gEnEE06CeLp%%f+E@5?e1{6=a&KoY~nU0_TjR z&)CvfE+c*HMqu8bk}IzL<0sAJ1#Xmimf$6CwOvCF+tTyBKv17Z++isdkuj|Z(t!eZ z*g)2eZWO!w?{j&TBKpVygM(D-^3So1sTx)9ST)hOrjIyQ|+H)+M0a8C<#Vi0iXG-J`!dOuEkg(m63mBHu%B1O`O=5_RY@JpxN*{^1SL zE!lKkNCZ)-59op@lGkJHi~xO>6b?WRVi$KV`%UAE5}a?KUV)F=5A_t z;+f<_n{^27jO-2ffyR$_W#{?&M_7!n`>MXl>hH>ll5$#-%#F5iXL^-u4F2-*kObdLqxMxM?X?5%q zc~DWGgxZH3P^|4m>aP}0fywfxY)3As$MSMx=d_$i{N6JE6pY_yY1Cjz;uaPZyobnOcC+@*^S zyL72}iDv4Rx;hG24tviEnCVE|;8-%cUCgd4c+so`sS3-AG7;r%proM5`sB;p7T($u>t`veKs*lpMGweoYHWp?>E2XFl$Cj9lA zD}>9{Z(Gks4~d%_bn+URK-lZN_NVFx97MTY?hpQ)Wwfv7jW{ANF?B1MR6pwF7F-kj zOK%)F_ioqSbm4>Bv4!-<5C2lwdASjj(K!^4R6{XKXg%wJFLCN3XUV~hU-s=8_;!v< z{U2CMvE?v9T4JWvyTWd3rvQHDd#b#L%kV5bq^s~t;NMjD5dgV27Vo#dmtbR8PqAnq zpM^NZSW(**!{6fof~1UpXu#0VUtwqG*STNjiiocqeKHrxKc^6d`GlxL?cu*?X)pU? zcSeEm-C3zi@({wme>XxI1ZnZFPF#dvxu|A6)1QP=ZYloruUZ^*Kihx8HjCCC68&?C z<%uboMlyZ$-uf{6-c{Fsj^mn=YIGOVc)|AtyN&+!-O7WuTAsA(JM6n6A*KI(1C|_8 zo?)MUbE^Ey8Dwk0E?Y4)oRs>#Ivje7@ooO)Klff;yvbFb0p0BD>N_D1`B%eW4KyUP z-}&eN7a9NFouF00{O1AzzgTWg`YCBI(vf!L-e5d^PvwLK&tvlV))gpXuP>79UwzS4 z?s#+QdV%F27NXu^kPMpI_?`}QfvzOq?9XpE@8qs^3Dh6Y6$yTy=Nl9Y;;sP&*bO8V(x)gd}zErT8 z$N9Lo+aZVhI~1Zc676zVGmqyVURd8{=Jq1uaOXVQ?@|R|kBwYz>rR&GZ+u6fB2^b0 zk9;4KW}?GLk^KNa-?NABCN4qtuh{ptAUiy>w_T&6<3@0>(|}^ysz@A>1km+;RmtU zuu1j9X?C$!;n3Y(PROZZ{Y8u5mu*JMT(~7^$fQv0Saqvswm}9s=c+WyLldn(sRUyK*1-o|F)EUJW*HrRzx zsp~&jKIbPLtNy9_0LZtyE}JXm->Dd1SN1{i(T(u3Ya5H!HjDzx{vofqz+-0LAE*=Q zlft){PUpM`(uLD+dztu$C9k!#eG7lCJ?MFkvO+*XG)jhANabm1;rIaXh6UM@BOw8- z}1ph%vY6=t)goN+y$ftcj1jAKh%IRd`C=aq79 z8$(MUIYziX#g()V!HT;PVLHDqEVel)^-z^bnSTjRfWP*g_){5clC}=bm^GHnF+xM_ zuFE4pS}m#%=Mv^D z`xb89Rl2s|<^007JV02m1YR9UpuQpfO-zso`lUCY6nj=ffx?*?rpnh!c^ zJ4G4TQnx$_r*+*V?kAoCqwa8F*IdaUj<@4U`!dWljnd5W4*Jl_Lce6xx4**>$BgG=j zhdxk+OjyyE4~q0--jP>L#XET^eeMskqeu5dYwqsIbd1qK8SN6Zq92FIIc|@8g4<2uJ3ROD3PK2U~U_tH_ z#+Ed;{0Z{DS)SG+qTzYofx)61A*ni77BLMY-^r~__BEa9yT66@eic&@NmI@<+$mGV9GI~Vl5GKm{y#%8+vJi4?* z?0hra_R6YxO2vkasenutk&0Hu#O703s+X~Y>wW93F@KjeO5`xX1TgwCQY)g$R=JmL zF?B|f$osczy0dn?DN1&L+Tw*?4Bj3^@XRik7_&a%>D(*Ytp+wT1A)6Y%tcHYh~NFg zR*hUh{{%OK7F?XPbXdCg>h!jqFe7CI%;U0vc-ER|x?sG1qUr=CQNvJ5l z1P+7>!fZ}F?rw#bJAHMt9#wXDBcgms9RH;U1EailmZhlw?7ndmQ$@LpzT3Eq0sZTY zNrzzT@}t2&e)KudewE*iOx8t2mRBiZK+U>SNa`z1yua7FclS4+GheU+00ECs^6qB_ zT0-}^vo5qi<$EP%q1(7uX1LZW%WGzcxQ?z)Zb5-gPoXJ!X>UmHS}#4GKd5bQtuT50 z1^{pl=O_(vX)bh$DIqiI7U1<{^(a4)*R?Tbvz(gBo(RB2%?{_OHJ@F#4wd^t`hJV* zNoP{^s&W+9W0knAD^?GY9HEg{$e92#Gmsw*bN74Ul!0`V`DghH3L)K_mNa@XL%yZa zk~~h)D7tUUR{$eBC0g}Nom4l?F+Fk@4cp1WLVptolavZ*5VCw$ zBjeg>ozEH(n?k{B(Uncc6Xr%qLdfDma;}%3(C{|fOPzY`XkwXKbJBJzU~VFvc$%cR zT78kVANasIQ90%d%c_0NDaw|y-TEl=^a%t{wAwy7@aUUGhX&cqES{vkCnJvN-F4k{ zFA0w%uDVoY2)9!T!7c|ssX7_WwVXltj!jRGl^UtlL+BKOPv{n0+#h zzri-6%Z(Uy5|PW^gznTeUj)E7MD_Ia1`I_^Bee7<2Nfk&?=Gx8MJR@Ke@Ij`lKgbp z8@H^Jac|mW%v05U94Uf>H`l`}(%M^o)<#I0!*HnNKK-i6dG3WL*BEW6f_l}1y6Lm^ z>8;iuSWC;gu}GE28aqbSA7^51)Bh4N$tBp9gQsUd-^~*eI##^LV=*>u7NMk-1eeQ! zbnVbIl;a>hc<~>)j$+K!iBF>*t`-hh&?!m2#n$dzrP)Cpg6PHnsBRTnC^GE4rMuzj zcqyY>FVAXKhP@G@a>lmjq_SWgGbJ<=!W}wocUD|l&Ik$~`l*SVf?|f7DYE9Q&FsQs zw8xcqC`W1$p;AB-Vu}JVGLg>INS+dYFyBqHrYfDH@N(tirCMpvVHagqY|H?F53Y>>Yjo$q;Uu@f@9i}pFrJ7L)gic9 zcvIp>e{GPnT2W^UUylav5-C%y>2z%&T0kmb%b0G^OzSL5^ws5W5&6mTp9$@I0Zz}2 zq?MXawv<$LG$3x_sq5ai#Tr#(zpmQVb}d{a>r1>aK6bPrCp@;z3J0XLXODHYSQLVy zW;ndfqMt|2dZN9V7%w{VMk}HyV8TpnQ|nE5#c_aqu9y7S$mcp_o%m5iR$uciDs+i? zP1kG4Pn2@5fgl^aV6EH+DTk+6`W9pgs4wpY_?*XU!>lHYnO{H6LSM*SGV>Oo{Y<7= z3+g&t4i3fx3SyQKVZ~!#3Oi}!((_1#Ec|4tis9A(;RAXh8j>oU=A`IEwLYSo z5Q#UQ3>5s>%%PovqD9WG?Oqa17MYzOFqq8yn|kn(hIf5i%P?zUilxUjh4~xu#*%lJ zChd<8d0&{8Vj;lbJ@$R);2vP7o~+^Z2YMg2Qrol$Iz0g3$>(Wf(Z{c*E*v!v-S+coq)Ot z>G?0!IEl`U(x#rHvAb6VmKPAWY>nKVw}E31|AhX+;RdAi5Z3YaDtj!sDlO69F=di} zE5E1IfXvC!*mZ3jJ@h{inZhRPe|_R_g;W`OVNcknsG7rfP5@<@7Cyx{s+-0h!#g*(0o7 z0^2$#_c|%pWzpGoGMl~kOU<@>HOQQ0FIpGRxS3k=@`a+?ciTOo=&oPpmpCP%q+X** zS#NBQEqeMl9xY>i8Dg>T*&8aDGUDYDpS_qkE2yv|G`2ETZHhPtnwQ4gn$eXA83!2& zzF$@Bce7U!V!&g-dq-szw6+wYjxh37^S*hXWkzeGr7nd-NAN8(=N+8}S)nq1D7EzC zvhl(mp7Q3HQn7U!>}I7!KtKzSv`k#r<24~uR=ntf9mWcr1cyDo0^#JrG`-C zYWb_OEyI)$o?MHvs-u7!GgKwIx3i>DJ<4KwncAN$0OiaZMh`G@B=yU|(kU=2cp)?V z>(@sShYn*bX4{FzseILfh-MMz#=#|sqsMuKzWsRCq;unI{$W=WR~lD@#wL1ijELUr z`bc@2Z}psJ(~>K#yu;3($iAiq9wq;%6O4j{c`RBGtNqN z4pD=8$VtdMq!hWSm{q-1I8{sE;Lmp}lTtU6=Vw+U0y}kK7k|exNotw5Kd?!3qdev> zBNTXE?UFj-R*20Lq-2UT4!D$aGmio^vIe6vLCic4)goS$K=-%LJY*7OMmlnHZG|}m z;{YifK_X{b%X01bYHoUqbKd7@`x;HkJMJRQ#?yJXZ!!*Y6flC(w~UXFj*7m)r2Vh# zsyiQ~DRX_EB_6U6m?0B6!e92JW^;N+ zY+kDW$XIdBPPEn8C4q5y(WggyyAjflEz=CpsH=YwVo2Qz0swC`^u&u~T=`!bESA*O zttDVpLcRJJ{OsV2lN2dGQ3^jz<(R&*Nfe$1L>y^r_RLmPE`1pV%qryjjeRAwO?$lm zfdd~IMR_llD-=;h>i?+Ob5hQ!%O#9ZLGl>IaBBa^H(PAhVHSDX6y0XttzR|l^d30ZWc4rG%dn>03dqejN2+AwM>-s@hw;?%D~#GF9JWHMAd+rUc3mj-#Qa>Ha#X^ z6Tv2goW103BWEwy@x#ni?hT!O9I%WEe=p(E8H4D_%ZCxL$Hnat4vOl(8hq^4pBhM? z3s*LE1~O%5Zc_#E~G0EdQlB=#IFwa|_MQgCDH zIE`#WbiBi(=s4^tFF5}Cn<*sFRdCUdCi`LTZw>d(8j_Mm9^>ox;M~hti`o3q)Z^ba zZBBn`aX9oV9c5L?gZr1q-QTke2!7Q5V-*S(WYWG)Wdi({)`jvBI%H=Fd5Zl?JB|Ro z_@6Ak8M*QrWEtScuALPBvja`o2nar{S^kW)bg4yTBfh=OBOk(jnwY# z-f&E8e zj1`YU##Zirfud;_trQ{oPgrSf;6$ec&YZs9?eo4wMx|??(~r+lQ%4t=4wI-I04!Wq z`K7ANQ%C!$Wn`nJ@{7}OMH?-d%*hMKWG`4#e~w~DY1kCf4a|QbvgJh#pnE@h$@TIe zk=C$p*`xHAN#g!whqjmJky1swY#E3q>unItiqsN5lQmT=iQyRgE5g98!qnx)+BKS6Z`D+=@Q@f+zFC>5ARzQsofA&Em zB8uy%S2=IVJr+$1Q2;6LKMIbKwNm8>+8EP46$pmbLOFyjh45G28FH4mqr9VH0M*HT zvPth5XvK>buYz(9I5V)E{|ab$r!{4t^qS)MEi5+=S4wwVyx5 zZefE!6Iajo;E80H(?X$az#sPFyP%CB7qHdXJ4zBVbFq^BI#IN~p4Qwa)s)iUH@CD> zS}(q=ta<(``nl&9avgQZ*C7=Uk^jh(z2jq2-b~wWKWhNXYY3jp+p_v{m$#Ownfl|H zl|UO~4C73$mu{a&qf)c5Ir8y{ac!j-vmbR}0pee_bVkIxQa+Um`mN@h{Vq#Zx4vR5 z#u3zK0j<-cpDoNkTKUorP>FvNAL@|vH2L{H=G zPW>L4+~cWQipG+eaFT){`RGg3i}*3anNQ5m+CDNml)67$UNoq8 z{@_Lm9*f{7N!NXG;y{|X($4yUR+C>|VDW|mF6(mKp0v0_qQn{oN*_C9<0M^&%v4Ch zcdne+a!zrCThC(sD2JvSfU zJ+!CGYI;+|siL6xy4(Bnk-iy+)oT_JXlKq2fg?EL#~)cUevjX`zTSrfRwC3X=D0FQ ze%yq=vk7E!pFow8PAK_6{|$AjHdNR1Kvs2DUm}y@Rq5WAcF)&El0dfrg%6j4 zrmerAP}ToV@d$e+p&rTiE+dlT~Bi!_Q<@A;p-LFD*cabBFCe@Gs!$hL(;8uLfC@@?r@_mo)qSTNW{ zJa=`~6j6a4*icMBitZ<6-h2peL6F?C(Ialxxkc(WU6W7mN*fsT2|Vl~an(O%rV%Im zm3smKPXgs}ZDD0$aT7>7i{>5qpmH)oVhI}%zrzi&s~!wADfQHU0`m*)a!xadqRA=a z-gOYhu@-SAsuz;O>gbI5}%OE^U%X2?*|wV!!Y_;T$fFCR-h8aeary+L<@k2gj5t4<8iOAh zX$9ky)y!$>XI)F!+Y9H~zwo4{|7$h!=`DPnGEY3R8&s7nY9vTo1PbZl&mm_MFWtZ` z9qN{$t$CHl3--8!X*;pvmE@&~?j`*utJv5JxO>N>6APT)_5i>dw&*x|#boUwZGQd8 zDBCXT99bhrq2fKxkvCYHZiPuMlf%R@rlEt`8CwE1UeT@I%&?F7_3wTt#499z+z^;t zpyU2bt@=T}=&kqs^C$DwLto$d>8s2pdk#l8*pSLGWu?+GM)Qn7bK*5AQ%Af+UiyZ< zPE{;P&U@18)h*y-JPd)8OjWn$udq{E-uysju?+@si21CPbj{SDssBbU^YUcNl5DXY{5+f%z{s4WJie+p2v z&lhsZC)j1f6gM!(%v+vqi^LoWlW~~@_^9O+FomG7K=j|Vcr4cORDk@lra``czV$hP zv9UG&a8A zlivaR(<#z|l#vFzGkPcxjTnd}Q;7*cy)x62`Fi6u-=EiH>iMt<%aUty3r$$MhGc!TNzy zl669#hJU}NWqgR#WBaHd;`lS+U6CRt#*+ZkS2E>bnRErNs$xv{inGE*(t>=#ZgI1lB2#2S^Mrg1 zVBYB0JYM?4+c-*3w+NLyN!Jam=dCZYtZCX~j=lBl15u(`t~HJ#B@sR}%N5=Tbm1bj zCB4=?i!mpAHb-NDr^ukJ^+K*nx)L{DM)K}E(!)B#2|XYzXtKGKqR$WibgSCRlLOVG zY1azucG_BLvT?GAH_VKQ2pLrOBWdJ_cl?>E&2C|ND&2kORz;rFD4fsT+rCZEDzy8l zQ#{UKR(hoFH8}-*oA?YF*#synQy#VH?%2&*=gnnrT4LCQ5DxRxNvBmy5-K3FQZNFs zPjNTswjL)?;&P9ceyyLW^9qbb;wXg-_dYVageBf=@|66wSaeD&wTRrDyjO~Ds^{Dd zZ6cdfRS9TBGGJYXm>{jp4hcTS8P^zLG0I4yLigx|nPR2uJ+E7isozpQC%ZVL@GJJA zkfQvqDiU!@-otw)r-YflI$%(F4*W8)?&Uqpk+Z`{`EQjEYWY8^JaxfR(WOPr&hH0+ z$}D&2%0#B1J96396p9lcM)`LoYx{`_wCxF+|EtJ%eYC#U_y#rr{?$5ObhGWq^+E-09__D z5vA1Rm5j?q`)sl)m8xQw68?6dXK~NQ>-R{<-i8K_1yfu9wpyU6%S?0kwX}?E&*^f# zdzgeD*>z=c(mX$efA7%AmbGoOP$DPbWcPBu2Yk4lj#?Fs+xqj@)WIThVj7??FXa(r z1lp81r1%be?X0EMQ}`janJjk?F@$p@_7wEeDhQZ0+kEl>U{2BbmgXk-YU^n;nUu_O z&b=S|S)OF3F!HD{X4}~+UaEw9<~Hi{UawI9160IE6Ye%L$Hxo5-(}8Cosg;a57{JK z_ch?y824R;;b`4>+ircX+a=4Ti-q5~*dXY7gJ1zLCA3_=hoc}ZU!)m?j?HLiF(X+e zIyi;a9ir|!ZInv7^hoD}Ndt9v9=cfn6OCX-AJ6+F;P=(LCH+H_x$My9+V7;k8mIwIf-Pg116bg-fgU#|Om&xBg{1B>;uHM@A zN|7{kQN_Y)_1HmP?;t}Ze*R|rw?+Z^epG8W2rRf%f@6l1Xh`Y5BpP1%H>o#ogagRo zzJ5boFEcE|n~=1yfbP5I1tBtr_$biJdZ8dQfS_C_iM%7+i#WcVPAkC_Ei=A>zckDB zPD{^z++ax~xqx-iEzPBr$#2C?>12~N?@i3smDz;;X#&#Vc2*qa)j+QG3)h7ADmq2Z zC>oBJ5hE66IM@DQDfY%6EM-yq{d|*0v4KBJ+^We-O?@_@PR#6t8FM6Hv7O31HTsc31Hvm9|X&1buX=i69ExeC6KbWH^T=*l*on|m9 zTTND~;(nBteOnxtr9E&GmiA08b}R=f@z=^L0jmxYEQ$nk5dguP6J^a z{+m4IUT~ic>KoV@vw)~*k*49D7#=XIu59B^BGC%*kKmdK|4VQeV%9%-5%TCUXIWfn z*?Q>}`H_ZS0sx-K7)P9*>*W2$)#^1@*wl{#kPC~TFH%Y6EFbm?W0GT30z@GB_nvNJ zh;CeYTb+`}JkwzsS9+;@SEfl~NrSM%6p2k!am2fewa0HfQxxxOx{~2!6;n-!UXq#b zSF5#ktLIxa44zXcTRdGc91j1H*9pY6a)m*5f_?(|Y&B`-)4>q=4`Tsh*#^6T>&k#G zlH$j4Oi!oz}g1fa|fr`xW&zl`#AyLcZ?Ld(7`58?`x#p3|OpNvl z-7YD&zF2eSWA)Nu9vQ(iB_V(uSwiT=D{$MHWmgddt}}g99TAl}yJOypG;Q%5aOt?q0>)+NE^q+EYvmbpJyYX z5uQPuW^PCs-8hrnq>?2?H?H*ozuy}oMcXnS9o!;xfLp>_8%0Xr0|PeNbP*j99Ny+V z2Ezyq!u8-!s~cWe*d7||O-{hrR!xFEyX7|aUGi6J8k0z%)o@p|#M(G^*w9D1cY*(H z)xL$wDvx;zOpJ@3IO=2t;imJO&t4^I`_si3n%l0-xb*lk;WQ^_vHULy93!WrS^mo5 zGgot^6WWe4*C-^%cIQ6IBpr2xett^w$CnkekVt_<)LAjuo|UtmPrfmoQ+M$aLRrTE zun??&dFQewae#pgul{0^QB^_bPt~j&;XkvH*Jm|vK-u8y6XHL`dleoSk|%4BisPoR z`)ShQ2O$&1DXFffm^fGJIh1(R`bSgNm=AJ@u;*fzy|PoP!P~9{gB8!b^t0rnf%Yro z$ikCNZQN$!1gGr%yiYA9NC(plxv)^m{b(r`1dziVSRkq96R+raCpGJ-x%WmS;%^BH zUEdV~y7&9=2TaZ((on6xJjUwnU!={tN7`)+(Y3muKPw;ioFa8~_8Ek-z_;oG|Lw@O z#RHDD|2L!++93TO@{S97rbv$!iOZaK`*T&Bsbjk(MLH-XWaW&E|0;*oG9|3(letAM z?KTqYD!xy465-u1Am@1dk1_k+@4k$b!ro>aLzE(Wyr4aU^sn5Jdkz+fH_TU)w4ZM( z%iesDL>9Vi7uC5HHx?p0gsHoD(lw~0&BpkdW~ilcW?H+b$GHH#tyf{zM_g3=o^tOw z+|*C+9~fJIAm$Ah`Z1}LvPB&bGa|(Gd8CeX4D-N!;JAtOM1s#U zuhTHeXB>*N3@q9*PCMq+*p~*sb#Qm7?i48<)DINxsEnM&YdwPUd;&gnlZ?V&Iv)wP zPuZ)k4wE0GaRkm!Lleo$f@wyjd+2(P=I>bJH@1Bi6P_VivU2Q_iVNLuP8hBwGxXTRVIxnuDn9lU7M{Rt<9E(^2pEU(AW0#- zzI{tJVb87D_vTMWE+e0v4!jf?GZ&#`S*6~yKYg?vLOPBC&)P3JNXVAlI6~d zG|)b zw0`}Avf>FVC zYh7F@9JqO3Vb&9eLI3W;aZts4giC<_6FlrQ>sa3)ZG5v(`UP^4e<+K;?q4`IB~=2` z`?q3$HhYyxUiaHMAmZXl|Hp*T_wo1jweg+#O>ki9w`6+Jn!0GX_ffD0wn0qEJKIF# zb}CJ1oWT*ZH;z+U(c+6S>IPlmQYPym?XR`xiKympYz>~&jr98Ig39Wz0>*eN#wP*_ zh9ere&2n%qr%f%N0{8Q&`5FrJ00F@{Y9$tg76dA*GiP6BKgug2MfEeybDs)K^Zu7b z5`xPp6+-dGV;UAuELaMAlO)q%sA!gO=@p^6o!G(aZS6*A4AB%71%*h?>~DjdOqPL| zPHUD6$@+ARvtrRFJ5+-FQspl$k7j5u4@(>jY7|!|^K`iwfUlkfY0w}9i{w|0R8l$^ z{#CUz@riL-)oA>WoJLk7lbiIa4#;UhoY3WHVG**~^Bue!KQ~63^=}p{+|;JwbZ06h zYNI-=h2S*ms*Tn?TuH-_mS+-<;~tBV`pD>mDI!>QufYaw_2Qz9jeGNlhroe^P7>9K)KXPQ>{}}0i=qcB?TYN9V|y9o>__-Fw&G4bXo31Ppg%iQi6VvT*6{gYRQzsjb5P2nS5d+#w5?jxzz4#m9)6}E0;9$UtrpV(HuX8lhN z)sk(gIgY6FM-Z9mda~vK>2Me-VC2>I3zw!qnw{bw-6|ZrhyhM(LEVf$xB6uu#q1@a zDobA7n|zAFZuOms^7s?`Q_0c?AsfM0rd^TyH`CfPBAM1$^3;e%xcJ;ay*U(rHSbHaVc%h4p723yL&PJ~WB20~ zWiT+*2m`-EvlfK}J?>aNtj63#e}4wFBwzeSPXSnb`+t zc4##)8+mcs+H17W@ajsS=wDay#PP?$Vb(gqEPsY`i5+ z{J?S-PvF;}&M~{%}Nnrl4@97h|(-f2ekFkt@8xpvV1 zC+BLMgoNB9sU1Ts155zRvp?GYaM@^=%{t|!%udT#uX=UkYg@0!0yaquUZ|2)y5df| zL-mibhB)oZJuy0E+l6$elq3?eSuE!v<{i|eRZ?QB{~vE}8C7-O_I+MnXUd z>24H|6zNhxLXhqjMYF!SHl8_Jy_4(}$&Wv-;T=)5`b>G*#o3(UF?7ffw z@r}>X8E#Zzq2H5cDJZFD_tcg@K8^`9x>5G0_(dhT&#Sk#cC9Bpk(kaiT>2h;HckXd zSDN;}Y`ZkT89eJuvH%OF>c@YHA#G6qOAIMsD;Ce1aR~~qX=-LMJuxw{syxL{8 zmZPJ2QA27|_QKUV9j=wac368UJ_&Chc1yg!FTCLmd;e4N=>N(SfZ*j50Dop8Kc3cb z*E0I_3OLlNF|HH{nv$Ciy>>7$Oxna2`?*{5G#c1)*CClW!5d{NR&Bz;~=-H)xI zr(wtkmaJwsw_R!?o89U(MZk5ByU9Hosqvc!B#MPJT{cgMRHoz=5a76$D1NG?;6 zEk#>Hhd%c=)A$vS60evP>+46~&orIS_{)vkEIX~<*_y1Wfiw*UT12Mbe!JiA?rd39 zng<(3!wiu^)Rtu_d{c9(cn)tl>rO#pb4J{sGutk_=M`$}`AfZ>uU=Tg1#iW~z4h&y6HJ;QLs^*6y zR&Pk!nLq7A26nO!U*Y2pOreXW25tVNrxW7aK)jy*_%W-(oq4N!o9gSfPCchH2rIce zo4rLoJzY;)z3)BCQZr;J%4dBDT-%^5(&{v94suxoAH zl3}4Gug%sC+y=gemLvU7gf8(WCvKS<=pXQ=-Ng8TvvY;kTKmgil5ABTaSf9n_*eOr_w>2~;5$iv^TO*+c zebvd;Q3eY$obu`E{pY`}%tywKRWt=(h8Xou-p^=8*=O->p(cG_>x^(Biw!QNaY0nF zfRv8bzZATh?LKDZ5XhBaFsi;ZMn{!{W0^J5N_UVG&Y&wK-doClw>Dcg==4%d_%X1Bs=^YSSwj z@4;qw14Zk#)VVDMWp%}V)Tp*X5@rOG!#A-8Ox~gc(7iaxNaN4F_%L*Hg2f97sJ^Lo z-TV7+9hJx+mfk>w>z1R|Hj>S6*CNwC1jxFN?j+OpYnVv#ryvlRY~OEnCxB+C+fh zzx!p*@^^8>o6A!VeJ%bhNi>%Iti4&E8U4ZfN?XQ@DuHBP4pX#Dm)fJiU;|{MsCVy; ziJS?CJwAKVgK*UR-B8`O9U2Z;{)mua_M3IqUB+D9+J~cq&Q>mNhA!(~*O#E_BTAw0 z@r^G>F6~)F5Xj0MsZ3jw_YWe-@Fp@PU3C1BW3dbB{Jb?Ce6kj7UfeNv_8Zyfy9xAR z5rT;k^H5IoPQfY@pt8K{B2Za-ch9CzS>s9uE=_)(aU5m8rDO+2=3TblZk0n(dI!I$ zgC8%VB|7Y;67{LCS6SV;rf_s)GXE>EUgb=Up{dMW+#C5%hM_}~S~sDYFV1(9a%yoo z|0I%^@nJj!7xjH$G#IeCo=V}BB+2|i6485H8Gu}4`ii)_MTRxyCcV#(vL9hje<*Xi ze*S)Z2Po%?&Rm;hcr}_kL%VBCm9cuxFHQw|J$OwO11h?nS*WJedq2MsVg2*`{SQ$~ zRmn1^%sK}(5U!mK^D^^Q#$meE@G0a49yStWKa}^gM2f!F$;LcNBx4}X2gR~}GIz&q z0SVg`!B||Q8wF`(`OChlvB3rMaIv^`HY0_a`K%h#DZIH0NvB*R<~f^R35Sci-TM!& zQG?tRca3#4^o3DUrFr6Oo7t~jP*}S=Il)*k&I2N_0w+PUA1vR%a<>6Cmork}A9A0a6h8k7}=iCB-AAP|W9-Kp(#^Kfb%vxSdz<<7h448e^evGKK-hhMB?m zLp;<4nL7tw3-lMRB&>L>TiXIwK_c1p!08n?LX(=!7b^R|F1XTU-R#T zYhtX}>mdc{lWUxGho9cZw|PMeA0ZlBw52yCJ1UBqAvy|J;&=PO3d#e(G-f7ew48a?)OOMQ{jhLR!8T281Bk0(4M;fH zPc=in&7wG^w+nZk*#~qRo~9t7I}%!vw|2WdX&fImG zKUxVM3i5vbnUm4pr+|~vJ~H^hXAIKmsOg{uLP7#(?rnV&p4s=DuGT&ONko{?d2r;EgvU1LBnn+xwdf3t|&w1%5AG zZ3BK2d3g@>6Ol8Pvr-ht(s~%|?DBzwdWQ0zucShD(gNKB(dgT2rWaAsQA~7AhGQh+P+>AilxvOnB!u80CX<}}^7aAx^)`LTSlO( z=J&a@r3yltsvc~yZx9#J`3Cm}$PV3h1w!pcK;GqKT8B=i>0a!}o6HU%!9$72npZWK zo`KM9>_Oq__m^c4*6xt+X6nYk`7}frhWjb`*y0mLNRHR)=Y0FE&o^4qARb>n@#Y%^ zj*2aKcr_<9+&2uib?`kC@KG66!p#)vC?R=l5W>M~U_{rA5~~V|48fv=D!WBSFjvwa z>PmW40ar4TFx8C1xXe1{dNAj^)sKu00b#fv#HykmUdNbd#3!)&_~peh_9U$`vrbE=tI;VE2H%+1~F zq4m*w6wJ+Ne(Q{b#(|Ng*uneOA6x_p1BoyK25LiGxWCk!`JEfgn)6PV8jXEpCMC#g0I^jl9MO_Vna)E*!Q5270yMC0P8N#~ul<7c)S@&oBhS1lGP@F^y7LZ-8J zZ;0@*`CY2lNLkYJtp48mtX~!|PeTzReME5#)ENJLV$7x262p-rPEdla$7pS?*1>kY zpVSGd$<`x22<|1@=(MJ7`gj@;Om#0wam+09jj!Y@Ef%7U-n_g>x+MLg+3;mAxTv>=Z}yA-<2zB$@k+<%vY6rV;OXR&!@ z*Wv?-!Hq1dVx9&oJQGJ|&q5HIAgJ2B0aTvEXgH8kx;`qYoT-vAH^!#Q6|elhEPE3P zsG}SBrO&$0W}#~micQEah7(2-=rs;)!dY^lIB5w)95}0dSX(2C+xgfZ;@ztf>`E9j za8o&(Z28Qzjuq7u_E|^`-lW*Ob)uv~7yAh3_Fr6`wTbf!3qwqhk<+HnK4#GuhSVUZ z9Zzo$(K>bSl7O6B2u2<8n~RIXfrDFOyJrh8xZC9h^&B=##NNKI74W%PD~4>JxK{NA z*&(4n&{g8T$>FT*-^WZ~67MW4G0#|zb|b-nm4NY%{pV5~?A4`IBk(m!-j$AAy>BCm@%~Dm~yNcC`W%k{hn+wkjlry`_e#Q>X0DS{L z#Lm5Uzimu*-;|C~v&gg!vt1^D9QS4sxh7dzLxt4ygMq!h2zk|fk(X8UcI(#tt=;@i zH0*5~u`|oXLHiZMkP2pTEoX=pUR|aDa}IB^dosPdq|bnFf3Ug>g*QcQ3MeJsH_z>{ z>ZU7-fWBhF!7JtwE1?P(sRUc%QZ@gn-~7DugN5|OmJYh#7N{OkSmR@q>uv1ge-b}e z2RHBS2!7hWdGF5>Vu9tU;w|lu1{tcaZui4MZqt8|YKzT?K~fm1Ad4jxCDBxyKZ1O7 zjSXsETtqN2+e4g26w!a5Y%b@7sPh$kVdylwc>4z{ECQ~lcihMcK1zxCwI)YponFeA zmdn!50yusdhke|r$G8A4w5_Meu15Aafk-`261e`?%tI)_>hwWU$A-uN7X?~F6uSrh@lb}RZ~}t$7|w{c z10aCXpeam$HND#W!Sw372b*5ZpBTLsy6d%!$z;-z@fLEW2Kjzzi#-DQtzOk%zOC=_ zb<}FXL3&v-RH?PeV{1O*uiZ>Lq)Zw)#`O`(JxXAdp`{df%#YuEu98&{MDfIsia(=G zhu-J!gKH4BchtelwH9rW<<*)`_E81Y+TG8cZTFy&RDfTe$Dv|o?d1!69~q$Q&fUwM z>piS8u+_|f&Tc;!AlJy11JWI8D9v~Q8%;OQKIVNk)eY~IV*SCRooR6W&{qZ%H|XJu zCh!C#|MWYR(nLpe*oHV=2v^mP@DEnhmV18R_IzABk-Ejc__5|i|9b<`e0VO%~_P?v3(X=Ch&M zQ11@>XBf0SQP}g$gYErQSHO&sK_P(7%%&wcC=7=N zCGZ~jKOGim!zyubIcR+}B9p0py{x#HBBaCC`fedDSc1SLlbm7J7*5eJRlD)j^p1(^9rx%-aDO(#zhQRTA||S}l{N@bGyM#+dmDQ2O#oOJZ?(Mid`xF+*s4 znHu4yI8XrfNevAbslDpheD~m5c-O)A_EGIp)b1A7mI<|NsyU>Ob3?Cd;ffkXiYA$> zV7i$D=9@*NMf5ibVL0Vt>ufcl>d#`i*-R9UUivZI2c@N(@kX7cQm#0`D@?|EwX_=$X3T5! z_?o8!sl;zithHZkNr?P7TCyXcqhy=$xZYJw5mUT>K5Sn#Z7@gu%n7@xs3hzTx#>y2 zmEhYS_{$bSUJE`4kJ*83ZK?=o;VxYW9F{h&XPsz6TChN>n{2 zw98+bi1GvK1vn8Lu(=a{HI`hiUB+FQ#m(U$Hoo~6MuX@ujj!U-=SA+z5{B6Zx16&T z?+p+5+ug#RU*YY}LMIJ)`3!du6J(Cfff@q)bq{Hyp>(%=4u!2n`sGox<*LL-`*()Y zgcd3nUOt&lHG^eMxgv8M9>t_|KlO}X%jWE`k^Ec#;{B~w2AkrAaZtq({B$Vudo5n{ zrXJu(orc-W_0&EHS_af9keIz$P8ORWxC}>%B#37IJnvCv$&x*>FRW@PLACw0<{rYE zKStD4oSYN>%BoK-X!9f_gI+#;w51#_tezid*;$CvwASTbD2KPabUYoC;v$bj75ix zW%IdHocs|B+qiMI!{^f&h+qPTcWdY5+`V;XYltb+ z0bzT)#jGQ7sq3||*1w(D5qRD%1za)xqsevn)$3Xu;G68Ty^&kDPci&7V4BoWq(f$# zerGg$OAdKOEDa{;&3XHR6^cTuB9ko~h>ePJx%%nEu;5-#eop|+XhV72mpTBvk-1?o>oQrfv zeJ=k4VFu2jd-j|bh`SMi!bSc?hj*O0ZS_+H=N|m_&ZtDWw-^X*&+Y3i5CsC-x>J0z zDt9Ua750J^61n$LU(nhK0&NN!WlMg2U}+GzV?Niyo=4D)SIcc$FuCI$)v4d->xV@E zYVpZk%zMn{Pnf(<7nzsyh@>h_mS5rt)LC#da?8 z4ugd=Ss3pP+@Lt~rVYE?@fXYc?HYq)Y%x=@!7>lqohM|dUcq81N2fYJ$8F-F88ETG zGzDP?07^H%to0GsIP4|z_rRUREAJ}0+2Y39fKTwG_x~u$4OO9iC;aDbJk*p*#Xo?S z@gT7`X52u5HWS%Z<(D4-rtYzM!arVu6#ghBgx`9r`yu?Q z?pmj=+VRFI?C3z;(GMbW{E*9f;5y$Q^+1om8c@o4I;<^)b;QpfOgBRyhu%clTch1& zwa#l>$)zR{oE+bA$Z3w+w&F&$jg(EyyQKoRllst1ZYY5WTeO>-zO+0<0MXF*Pa@02 zYxUL6#&%2~11S8g=s1m60ekBv4xl1D~S&PBO&A@|%^%b~Sej z5jO-fGaKjpF-l9MW@a$7T~XdOCydZ2S5~g7W6OAOPJK5z%lLR_n#VkJ#Ff*E-o>%1 zJ?})eCQC)kJBblYr&r%be)8H*+h!HhmI0Y%J4sRfrh4gF9(x(^tFE!~pj6N4P{4A7~cPe_P>A1ETis_xfL zPCdIrO=#uve#5Eg)4}TWh$P>JpNA6%{KP$)y&fjzZCIIT_tpLkXdZRpk+}zP$*4sy zC_niyh}d$;XyK1buEu{A!FjAZxXOzji?{&Aw6L4%LOf0lzP(oF19icBsQ}axffvRY zctVtuC>aBH{Je(H$7dk+W`*qTk6=)i2d{$+0$APzhx0z3f$+BPrb+Lt=aKu*UPUynmjbnLG_~R$_w3`G`7!aNRA)x-KX5JK zpU)htKA7Kd`VH4oIx8LJKaPT`hXyJT)Rgqxl~leKp>m_#<3UA^$(c>26uh_^n5Hjz z_3NKuM!)`iKtdWK9bAIcv;wBV!vmf|l%cf79|s)YPu=6EqP(&FMCd6vcG-n(z5#LV zOe0VCy+*ySLe6YwHdp*(OjhuyM=pp8?Jck={;W#di(SW9BXCH60}_|^mcd)2Tn27W zjvE-sA~_yk13N;v9|7R&$@?{c&#`w=(N`Oh0rMZzhUL&{fP8H0IL<9829-3Z-Zs#j5slkTQv=uOGV)#P)^zD1AxJdz}kXh%|8?K=5v? zzvFyv>&eMEx#)buRa$;(Z(T)|`sThs=w&GX#Mwk8=r$2wwhnAmU zO)HzDzPb+>3`wv5arP)|@l<^I_mW@isGb0H_VjW=)jj4{o87GOZOc zL;o17!ZeT69u|@e`ft+*tCq%X4LRhI_TI_4?Q%OhsIkNT(;2UF|6X%}$X#094#p!Y zeA4qDV};MU*ph}6<;&rjIn@-O{5nuk)P7GdDCzL=r}e3wgL`Fo&4)4VE#0jW7_i8vzn zz+$VQOm|I?D!lHDW{bz+P~te=%*GbbN97Yhcar==ie?5IJvI{))5`rM8Sk;o2@b=@`7m|wovxZ4=HxM`y~HIH=_-?Q zsI~KX>wl5S<6H_P!Vy!$3qy|#d_bTg`q&sW%s(`$JX7jdLP?0QTn1%#Q8y>v z{#xbH)JsM z@=MF>#5N4qT)1)B^BXC|_x_xbQ7SvhhAk7HXQvmR_Y%d)Y%nor4;0pS*f*7w*XjhY zXb1j8wt%;#3rxZDNtMl%KKo8JbpPBWXzV%fqw*#x&wnzN{twg^k`mm%h1WL;+B>i~T`4uRzV`a3q*scV>9m-GdV#67FxgzlY5ceB4duu^FmFOVd-6cd}hm z7kya_D0zM~g^CUzqzduB4#)*8goPf^`2G3%wouZ@DUVRYhXrz?en#xOVFYE^(aKj1 zc2x4eHrUxlb3;i5->Eo zK?Z5WfYMG+zkYyJ8`c5n8Gs^Q7|M_lkymvE;{tW<4h8GEUx}O`w5T^)xzOX>vEWUP zM4ho4+&eFFq6B zre5-?l(T&xaUnjT5HfDnN-qPh>x}56+ARSv5mx>?M!sqp;V5#4IVn>FW+OtxyPXH$ z@!6p@o+Jhki2!0{wWeypWSj11D+8=m^EsP^PHhF6DhJ$?)~d`%-Fe;FbS>3`7Grkt zvB%R}>T?_)W-$Y_F+UjBC2g-S1hfb(X&Hlc@IA;%ES{-Ex`{ccx3~)6EU`M<&U$}%hk0VEALFnbOslU`nejwtC@P!Kz z6UfHY-E(KyTv?OIHd6}=na=f00~sB^E)FpuyV_=htRPC7D8wTd051Q*_F{isefsZ9 z8Re~Q*iuFSUCKW4PtYS@+F7eam1Uy}j^8B5uorB=S~lx-sQ`Wf{r69jIgr061rKfs z{4XUXwu#|uuX2J|9m^5YP6BY7p_E1HA0&KCigj5WzjUQVpLMxbZ4&OSn>S?7f2`O9 z$%(|vHD3n;6LHaE*fENNP{Mz)FAvc@IKJrNAK zH@)$=DwfdiMqCZU_qDQzm%}h3`L~CNuWu%XV7{a zH%ttkRP7u(RVfg*kk@oE{jv#xZpb@Its;Nfyn8I8v!x#jzOhbCcbS7|g-{tltt*1E zKlk)m^UQfM9!|@}G>&v=_D=FTearoX7>*!PF)vtT2hg~2pU z^34+&Z@kL)qp;?=KC{&y)i?#Q?d+Z7M^g`{vImDY0$(Uq7G! z*r{*K-7XSXrxkR+UGzbvM;csUh-txDfh3`^NAH$ugt#_>IC0;>r)U=q7>e`OVqjav z^cVn_y?XhQeK+q;5w7GTCOpo>f;`jJ;)09j*DuK-&vzg3ZPU!pYXe6ms#8e?6Q!%f3V9k87|pnf-uRk$|r*0RwOjV zG25e}6&rl&er7{LW?lz4eeTTll-0Zp?E+@5v4hG|d+K(IBpFP@uM^)nfkK=Lje2P$ zf_T9Jj)*J&*S{q=irlCoeFoYl!OR11dwZ*+n)Z`Q><*E5<+dV16^8X#$mATKzYC>S zuFZdBI&NMT#FxGwv70vEfW*4N{4hVJpV7>!F32m67ZTLwL??Jfy-{xFf9ZW)VxC+A~$E_}48*UYp zi`iF-!iwPEu@0*}x=X$80>OvcFPt{ll=#}NHkJ0My3Urhjw}_Hyf-Va^n>V^Jtt^k zC&UJ)?h*c(9s~yjxiR_H-Czk2_(nu;mp*g5ijUWl3yzZuih5CNX(7JGN7dmsxgTyZ zx&5mI_iYT6;LgQ&spH{JNYmy;%;NFCySw64R53%ET${Kg(;F=t`Qa;PrJvEf zHSN{;T>JGHUd7LQbhe=vPm%`eOZ0lJeGpgXU2L*~of zP$O@V`bMFrS}uKeQBE+CjLsW?F$2T!fn$jy!J9nE$7I>is(8MAzkSZw#>uj1LK_`m zj8cE}_u8hAUASiJ!zi=Cr-+OixNfT@Kt-=|Ybb7IOGJ}ENP1W|`El<91e%QbfEY8+ z$J)6)4IfCDpJeK`N2CN=`zK<4S)Kmz{msgEagg&cs&hn5z~r1GKGO_rRPJytT;&1U z>iU0$J4#pr=i$7c(82m{7dWd1A4-9O@oft!Ia)S21z3&Bi9o>T<~pNnL; z<1rF5O@fY`7D%U*R=rV}S;R2D8J{e#aujRfM1q+$d3ls4gYgPe8k?wAb%nc-XWar_ z&&hiw{)g0$PBK7%oaPUGQ^Mv^{WHPgv&s{k-2e|XtjnO{vNTA}` zPlixsL$55gdGM}Zx`}78{d%gn#S`@9YJwbT!#{uX)1q%9D2X;3F&=#S_JF0u6xfG( ztrITzQy9M(qAs9rF3j;bfAzLB(V?tqb$_x%lm7LMrxV;?g6?nyf&uA!c$*Hhv=caW zPrq)&glr#W2Bs;$fl8!d=LsVj7R+~Lw%sN(RPO6V-s5^_>Elc?daIIIB`u(oRr+dA=qVBiU}nh$O>(kzw=l3O`~i1aEy*SL=k+u6_^elM z01f9WG!ywX`8X*NjL@v$b~L~UB`?85zUwQqFs6CJyr4jO_E zw%T?*_0+f(@mpp5sH*BRMI)oT-6GNK_;J?+;Ay1ucI{S`2?ToQQ$c$@Dr-{(WIwy~ z(Q_AshV$>U%EKQmilsx`#rm*Rxzi-DU2Oi+P%qZ4 zX;`$JOWEXdFU-VY+sue%CIypF$FR;K?jZ8^jL3t8e0_n(5^Q=sA{p!t_~UxA^bHSR z-#q_L*?ArW5Q)pJiW{z$jc)O~=+4unufV8IWvi&Q3z2N`uT?J$dip9n6W`Q33JXQ{ zHdUl&RR>ond?(ByERg)uA-siZoLsz}!k#uS4`RZQUc}3mn}A;Q&u`4yTwY0sc347@ zWY+Y&dvQ8dj0}*YQ5ohQbm#Y`Hay2fJ|rhjc7;>iK}^W;GR8%N{05P1J*PV7?h)tW zUvvY)G5u;(EnX5KVRpBe-uyqrGGoq_$aVVwV#>q`9AcWQwvkALVZ=LERIAT099rnm9znBHlP&3P+Z1=5c6 zQtH>82)J>zhk3=}KfxXxi*-d3MSvb)lEJ!6x9Ej~sxN;^2k}5Qt-SW^OX9LcGT7lD zdC{vbTg2Btx@-?B-9wsW2ONbCNyjlBnQ5UzVxL{&+LV9D!id^I9K54Z;zSA99|Cwe zei91w189pH8a1jg2KM#?v5MtR$^oVdYoqWAl9K0%TYC)P_cNY-NFkEBv4?ZkuYOS4 z^q|Wsqml7L-n)f?cM>cQ>R$>QQVzrEUJdhDi)(d)!27{sC?M@b0}+$J{oAm-%kl$L ztu8KwUxb(U1%U9dDPjmO7EbpzFKs5y0*I(T5_=sBk9Iu zDHd0!WvF7?NxfwXuNj*`W=$vO0O4Y+-gfbL1v|YrrX8Q6Lv9?H(mT+MyWn(Be>V4h zM?r47z$+`5C2P^=g5Z0iKs;8nm*#eoei1)+0E*+AtqU(t-PLUyC5~!q1~ld|iLd!^ zq4evkUH3G0Q{v+$7_)8J9NGR<{qm9nqv#1+bYmZ1b9jR=hTLO1|9KC;sFrI} z!Ed?Nu=$ja9RsmMDT^m0-ku6X+FJe3xlENTe3DgVH|-_%L+OXaEBZeL_l z1@la83GI40zl<+H#moRy%wOsQD;Z+X7M!JDDcT_X$&Hb}IovOu#^O^VNI=Kc zD!laUA6Ma0SY$dcdPvDr6iC-Sov@iOsUfNmv+2!$xwO1I~|Jk3@W%@e9tNCFL*_FxI zD;SAE+psL*E<%t(juM^5DAK4AlHvAi<7DvOCAr#=M~OkjeQ+*}x4l4d$u8_ce;7(u9n4a+gU}x49_DKuWOn4?k?HAI{f}58V`JzJ#Xvaziynii=C}*tJ=D|%M~{K@FIKn zOKq+m=P^4&ifI09MR=t)2hl_V&Ed}71iD!=Bq;5T!Wz)t(%bw1me`wb>uxF1YSS38 zC*Yxuy*R7lZ6hs@NqD$sgENr=iB;3*d*I`4YBo~5^)axS27J*I#}3i1p_2`ET51+C zi9^NT+@U6aJ4gG&%wtgED-!}GJ`%!fPePZ@gEUnJ6Z-)AIJZaoENFal=3FmuYwf$m z)Re`AHO)Vv-&)EO0h(rN!bw+@mzOGB)MUrFcoDTksN_y;F@-6lX}vAKDMu;BKMgwh zpIFF9Wh(8SSxHr0V&7uvIHx!_fLc8lTOh_Jn%H5OxV?Ad6s!4bejjt@B5^xlj89eu zie`HY@zwA>fQ4H#Ng2tE8=uXWy!mR%M8mGfLGf+S5L&NLgg`fRgHPZYK~XLCPX3yE z&6~6!L#O?x?_d* zdH6lygDGASNk2NE*&Ntg-qKQ)3pDP!w^GF{(_RG59l3|USA|g)H>GRhjKlCUMICYl zrsrl?@fBqX@fv*yAN!b@gf$Qpf@7^61omq`esXnb#_phIbJ9kgjQb~jemn56ypMeB z?Tq&}M2xMoswh+IHYy0@remBdyzuA`$+OxBLQ!vCVP7|T0_S&nP!isotB9Snj27Xi zqJZORtQWBZ(deKNsw?lK;$8A;y*B_p<@Kr^z|U{=w5iAq8E9BOTX}97!#dJLWU0wx7rk%_8oTPZ_r zeTR=|{%yTg9KkuIj3uPo{X8-J5WBn!uV>jQW^6tr7}+RG`X^R zbY7K=Q-F7Q_2WUMK^XXIOIL?hx=Cj9C8{-_Eg|8Z>oEQ8c$0&C;;KgoQxtfNAZ=4S zg+TV59RKk_0`XSB3j9Bj$~oK>NK4`IwV++U+vxx7|2mN3o^&-z8SSP+bq0usBeW4{ z^pB0eP?P_~M&N4I1m_D+@(_7RQ@nV1sLp*o4tW8>$OMDEQkzc+(m*q@UxNI;JE$r0 zRRQ~wNLOidbs>W|kXg*(VNL;^+w-luaTE zywfV~BCOD6IVd`#LDM~dcH(h-Umk7XB_t({CU7#@K?w zvH81oS5v#P{EIjGKy90;?}UPjWt#s$mop{G7rsn8K0a31`J|(yDMVnmYKM=-Z`6o! ziA2o)c-1GA8wm2onZEvR4@2AR!Hx#0*JOgm!s<+0kWf{Nqaj1B0^eB9&Gv=oy+@!? zCO--#_?xN2EZ3tRV59YVo?(S1ou;ga5Jx-!D-&I+)GHA%2X+EUlfy zRI25We-2&mZ(kkr_Yk7&oYjgl*+2R77*UJSjw13pzyuR>4qKW#TU#yLqP%%b+b z(qWsC_h6(bB%GaTWUFb9rc6JHBYZhc;tI@T)!>cJx=CF0I%K6x@tlX_gh^tg=!Wo- zKI;0S-gwJvZq8QjyS#;tR!JKtA(a~eos0R7S3~-5DtX(^@!js*^2j)?&=vlH`%1-h zg7*SS&S#d_!JBu%XBoP#(ie^niNvSdU>a|+B=AGqBW5|`@l4j}CHDeUfRzgRkBa=l zaFzxdwzyZF$6=X=T?ZsrKOvF`wme&=`E)kXNl zGKG^0&N~m_@}k$E^zyC|g_KPumPQ4o{kH~s_udQ7u1cgPl!b> zMS>ba`(m=Gb@2D@Q}rg^d@AQ!NlS5``hsjiQ>Q5)`XDsbh3jS_xC){Pz5Zb=J$rxI zeq<XI;AWgHn&JV5aVwG6WEaXgMWji)v&)P@t^J|R+e5sM#5XZwG z^hyrB>_oDbUO2wpnu{EGwAAx(e=5uI6W*ZQ@X1K=Wn3R9cTs=|3y#p@rMY~n(Q^oa z28%Q8ZoSvN!>asJ=j}D`#d(}XjRdNex|XU()tM_kzgTl$F}kn9yh--2R@lHD4GOJd zUknljum@t1uBoWa+x?VGv#?FTmm5e8n5|NZE!Y#jss5`nNfvD{GHL_3a{>9m6;aRF zsU!S;x-YtR7SMwnIT5I8ivN$fUWbTX^J||A#lXzV{q&Fi@ybAPANqg4p}&!xF8EvB zp6f^(8?7$nufg8<>;D<-ZDvcflG$=@T*w4bpibD~4)F3VZ9m?gSiRpA*T(_CagVXS zksSVZU-9F+aM_i*eRPq< z7=osLBqgWK13eVSK?n}T3B39i-f;0FzJFlq%Xi*-x3gF|CWDPbpQD4Gy}6>jiG|^j z=A zSt^?Eo14+_ao$~>;!2Y*5Z-^OrN%*Vc$FiL!}9jwkHT9>mvtkst=|1g=I{$j_YwXY z00qu=n@2?}KU4x+dI@ET*O#!}CdmDsl^RpS4T{(h_hx^0^ECT6w4D6KCJl)tN2gva=+nV(vr-2 zXr^pg6{@s{B<|c?kN+ptHQIgV+P95xmX%YFLglPq#JN7}`^xt;KKhC_XbSK%0!_~J z8n3}OC83O&&K+f3QX%)B;aQQI;NT<0$4gV1Sq}qM?1|6wy56g(){}9lsQxn9FzmSX zY-Yz+JN1C+l&TFP0|zn!R58H$I*27}xCn2+4AcpdhBh)^0QU~kw@5o8`YW#$#7RQD zrQF%sx_}NTLfKdNcCdRdVe>2#I*+;eX%}{;?o10tlZvMC9@q2iUmDZ%{1}jeXvrxI zoro!nvFPr+Vs1+Vx1zb!60T+a=91lh+c45RHAVQ0YoHMpK2?M*jyII1x^Sm|rdO)# zJ?!pCAhx+H&uC=ySkau`TB%{!$(jE943kVzi8QuUS>x}e&*RI(FXo)veTgBENh~}@ z#g_=rwKGQc=^wetHSi$MKa`1sX1Aa}645-!2-X=9~<(M|RV%=;8QN!s%1#FE###(c-QFq!iP{iRK=iEbneWpn(I{utxPgWrj%AmqgKl1xpVtD;=C>>eeN!TM<#IsWiehY#npvTZR3{c7qY4(8Bd`T znO<+{^XSESc}sw52pbkTnIj~gB{R6?%k)~BN9)q zs&J;6*B!h)%_v3y7whl`?x6A_35GjJYGpPV^w?%nwc_9BGk{LvHXG;^ULC#5+20-Z zN=WDmtiwl_l9|O~*@85~crPTE7S1;h2S=aJyFcV>O;nGK4cjcZ-N*zl?erwwANg(3 zCU?XGw^-K1r8fsfp_c1-Ti+Ia$S1jiQe2od3hT+-!ZQIR&d$uub}-85_cghzC+rsk z;b`^+cS=I?>F(f|c&qtHn6Gxt*zOpa`2`E#lgzdf;&R1&q!!VWgA*b)H;AzA^<938 z$P*92PAo<EzwqIa2qe4Dysr!{Fq2ypTy=mJrhffIukXd`%YOlFxFG28boW3hNRA zw?tf>!qt}UItyws8Xw;Tr#GRezJPy!dFShqklw1R#QOc+5m_fG+eeMtVv1aiA5;OT zZ|VL06))5O|JXR*G=^7W%Y$GHYb;pL?U-#qaR=eR#okV{*v*>Z)VJZu$C3N27Jj?@}q(f+^*;-%w1GY2Ads3*U0?wV_~G z?Yd*efBS)y+e-LD^690!_vkRuq|Mjy01|d+Ml)oCm`8puWBG-+Dn^xvD7XdorUgA5 z1juIKqUw=&`V<$i^a$sFKh0e3B+~mLrX>dkhGqK;7#19(lzNe+^!V}`<|Bk1kdLPH zRt8tbJ>}bU&H@}I!amP_IS+l{H3i~gXXy{Ox@t0fX1)Cv;8uv9!m6nVp;42(L-%HM zueR%~^E75Yv|+&*rNrS51Yz*I36N$2;Kn}*3Pdq5=R57v`R*M`{#6ZnfgbSKLRcgA zb%m1Qo2-28rZ++mVeEQsXYr23(*tu%S}1?+h;S@B$zaYfJrS2w9N^el-2mk${b?_% zu=vtnW$)JmN_Y1PhT~KvGF|%HG!j``5rm&Y330uDvK18lBCifScj_#)$B4-qtE0~_ zrg|GbIyvk8+K=|~MPR?Lz8F>ZEed>9$uW4VukCzx_MFefCLtaJAz)+|0mizmzC?&% z!W31#+@f=dQQ(i-sxSWCkz*aBdpu>~uR6&+9GCQGk`-sDRh%^V*dJ1bpKAlS6K|gl zS@c2VT{6hQ8*sA|gakoK?C_TDD{C=jL04xfsF=+r2Q|FC{h{YFEp$~X-rdru?zs_* zGVtcJ+-`|LLRf1My>WSMJqiH`6YDo=p(PJwR(eILc6N=h=m#jW0#*A_Ni1a|f(~VR zwAn9hdw$xm@yA!^H3amJE9=87*^cy6o@@-Ndo^e_y_h{O z0nkKnV$1;>oEY;7N3meOT%D=>51LcK;moH`TnFyX5(n&YJp8pfKYa{VXVE>{VWmBC zNdyS9!=JF%2;8+z0qvjQc9Yini<$xa%wY*Y77`gyT3i<%iY46BWU;ePH=SW#E*@*) zy}5}jNheRn@}-GBnY30A_3GxBd+>qf7&v?;V+N`l;}Ww8HBNL6+hos)R45}jbp5(z zP7<@);l;e2e5@j@Uw2{p5E{W!*xEWDUbh#(?n9qwC;`Ti6V=uF+~?nkzqU=NII-bO zuNg0baL=P)FZJG&$8?6(6vh!d!c>YKz8z*-^iE^J>~jFW2XRyWrUCzpT%=T9V;t|2~|H1Bzk!Yj*D)!F(OIuD|84 zXVKkaTJ`Gx!W?3)OMIPsdCKfB%%LMAa()`S`h~rbAYv~495C;pBVT>_?0Ea&)~DVz zB6uquMpQ%+2s7jB*RMChl6(#rAZ2MQzuc9t^EMjrb^OdeZ*(PM0%@ zIbkO_UXKb>br0h)S*%I?cIIQ5!QDr%!r~`BW}cshviNuhxvuR(PmX~XbGX>fMzx+dLHV5@0hJ9Z@TT!dr(O)c<8KPR(3A-I#GyPU${M4$%u$Ie? zKczw#SK+u)MhrjjF`nGLr0UA(BmGWh*1u zdy^4m?@MHlC_5v2uPF0EHYwRazWg(^cK~^E~(cyzYOV*X!pmuS(Z(9_Q)! zp2zq2eBPg%>Nkh1Ij=c!z)QIX&cZIm8MT=}m9;DY$D6cfR#5jNq!6=vYkaSNnb z^wY4?{ziE3_u(_yfEUg6%cGH_mF4K@5}?7(O|fPtrxwkG`nuUP%omtp`JD5Uf!`)$ z)}NS(gjVm3>B!uoKVjgye+Y_kSOH6iIVeSGY2zQ*t}FXwob(+cAu?WAHrgxO&1uJ? zzV__;d2|(h5V;UFT1>4T%Qs(iSNov*6wgb3j$rMtCXK}v>Z9IGBW%Ek)6{P+GnUBx zkqpFy*~Hwh_8ZAk-F;Sam#3l#V9L3}&1W@)ZE!-! zyZ5a(Z<%+PuXu=#A@XPBYt7-uU8?Rp8F<-Q_I0O!Ol-^dw9MKvh%NAeBvIWB!=*n+fT zn@+0dyq~{hZUKH&V~-PTYpywfMj9R}RCM7I|(->X2mK4QjS^kiCUYjMccYS(O*5j_~0=}L)DQgTk)r0G8E(u= zkclhI<3)O5*{-}s)7F{jtoys_%#ZDn`nwC=jg9DK~S1O-3e#N46AB{p6g7nxKkLin!ajN5b zxYI~roMS)-w!L8wQoShE&ygEq5kA7-6LFyTaJkok({v+7vr~QyX;mj6E&S+JY7$pB zixIFqs`keQ0GHO0{5Rt7U}wwzA)uE1njgyv1fs(7iR;DrK*r|tIE@|KBxK*MvxQ$Q z8r;jOa|VfJeQVmbLbKxCDcbiX`B@nOm8n*@ z{fc&I>F}JIaX=0saZ1u2phjoArayD5!NYiGx;f4oochM-Ek-qlkY9f(%m|PW-v!Cu zO?ZH``N{@r(ds+Eg>{L)!{I|4D%Es zCCQ8G@VfOp-X6|hjpQ=#W%m!j!{H8usXBjuLR%-#fyx(ct_2$FzuBcGH6?5QeI3+_ zf<#{EO=`#}1OC6WAx=bI)uMkEdENg{k=GMT;o1K+w8#PtEy5d(>ex)J|9})hmDt+~ z=RigzoWIU4;SYE6>%s+mhyZgpS6Gt*LpZDMZq<{-TAOcC6YL=a0x3!GMwcQg58Zhi zByxmYg9=Mr=Jcy-nc6%|GsXC27F*OPz-xO1UqaQcJ>I){lmE~XEPZ!`F@eMV(X>s` zpz#>38}Cvi{+1%V4p?@#M;Eu5f9!)tTk-H~wei`Nko(OiVr?z~0n_gh98@GE{5UQ>ml&+m)bjzddE-}3oo?$;=y=N z^V7B>O!924Ri2`Mb_sE>c`(r>KMDI*@+fv0ar}Uyd_4Jyh%$!xWYe0FBrLIr=i8iJ zs8Cc>VBY8Qr5hONXZ)q`rN;nlG(f2_u2q1bzB(47tqSVm`;X_EA~@fFDKk4Kzhvprz>s;4xJz|E`rS1YRIr`?v&YDnPTG+W*+XL+I-48Cn7fk}9ht&nPVLeALSK zBS6l{1BDZW5-pzwm8B8%l<r3^i$l;yv;acxMVI+|*Lj+9 z2~dKcCd2}V(zP}O1Wp*Uk*oAB;=fm_+IZQ3<#M6@jFa(+ZffOP!97PF+cGFHW%|Q~ zXsYL_2caLj0B!a@6brUU_3G5Uamqw2hJYeZvl>$0dT4Nocx=XMgd!f>RBhdUo6I`7 zQ;Xcimg4e18^*Xye>9Al_Na+O(P|A&i2(Bu5OHFwnYebi^4QuX-cLYnGo;LjssziCh!f)ahQN76P5TZ(OR4QN9^*9M10}O%{Wy2OR&9g}(3LzfsL%ML z!>kiT7=)u(NUso18tsUbR5kf(vdtr;PRSaSE?})r(GvB+ zF<@T_e&UtLOC0p6GPgAR8jJ;6J+@}8+-e7N2E?bYR-)Kn9K0&59pS1_I8#1uqbNB} zd3BjEt9FE)VR{+RmSlT*JPpRy6hqI>pPEm{4xhI}c$R$#&;6c0UJUopA1LWqGj1y< z*PvH@eNK#(XTsA&591wLQ_b*)rW)aITm5}mSV*%Pv47B_e4FgyjK>q-)3C3B58F)G zj@Ln%?QiKvQtBHbcWUpznFN*Y@ z3tdkp*mb&s9+?!m#+~t(CN%x#aCq9%cCGLWYWvChbWhZ3SHYEP}!nIj+vVrm5RHD4@JJcb=n-by+j?{l z#@Ym};K8a*HinC>Qa3wwWgzJj8jk+i|Id3%{xj6h1Rz!8b>Fh`h8{it#SgW&%6J@- zLDtt!`~6>`)(m`LcZt-Vv$G_}eh|*YUjmg-K7mEG7h~C8P_1wNWqE4Y&FB{dIs=o0 zQDFCNd#M!tWGlJJTQn?nW>v9c{Iw;Ukz?M9CP}lE8`2kNxKP@5IY5vpxVR z!$t?}Ok2Rrn`K=p+d#Dq;`MUKbuOb)NAu!7LvAKK3vu$A4?N|g!IR$v!|CMC>0(@A z&oOQb;4Q;=!}&#R)LOtcS4VMo6*LE~Xq7({$%1k|Ixmpi2~Tx&e%6N*`x3Z%`JAt_ zTtD9jBCECIFTbK@v?nKqh=2!&UQq-gpn9kNWo<+{$TN!(dLJ*8&b#x+RlNz(d9ab2 z;q;G0%-LVvjk@1cge3C+O21SaVU^`wT8(PNYx3RO8}9%cO2I5h(*!n@*grRvSA%{D zn#^#*i~%P-NeKV|J?!LGxKpTjs0vJnR8R1={QMzjr0MhR-d|b9`boEt6lDbG)z9Z# z3?6kA<)Jr@LQ}bh4M%}XOml&9=`6Ba1b~j@uLnRd zU8Wv88+Z05Dc>&j8Nzpyj=ZL5db^cQXtfi+QGfvAZ~YMWCq_L-_VDom`Kz7GX$A{u zXL#ok2MpHQj}EqU0l}8M;VE+7BPT?Q`>V^a zo>RczBx%udn@nBmse$)Ggwl4Z*p)qvG^T{7KUq!W3V+W2DB3>BvP}7jY9g?EMoN68 zY=sz0HbeyE5gD~v?Zld)VHPc4_DU?nXIw!XNIQ0%d{$~dIuV2gONIM|E|`HwUq;s8~;j^9(1DZraDLh zd|>M)*f)AK+s{w#@Decf6NRo(1$f?A7s(9%$z&fdysekuPA%J72kP5-P~Qq$vYz$9 z9iKG|BXXB%NMC3&hvY-+Re0Kz&7$hvYY^OavX<|grWiKbih{0Vg!Pe7rd9 zFuz&0X+p?Sp(S))@8b>b7C~^R8G(xq_aGMfBLFKs2AFt1WI6`CtLqh$SR>j7t3eI@ zN!K);v(JVhOP{1#{zIni zJMV(0oc5Ps4`2T^)0T;ohU9OxyAp%+c2MDhJ%^aKpDMon8@$O?a_UMs?HaZPB=Ui+ zK!l3=?EfHCJo9{U=6{WDvPGktKE1?q7hdaHzRX}dyN;vZP0NNh@6HAGQT&n@5xmCR z&f@CFVKjyOS?tYu6#dgltj9U4ubv=1Ou@!5-*}0SCwX?#G~_weE&Z&mqF475aj#@&rddU zby0lkLNz$|GlcwO{U0RjFlC7*X+Mz*Dz)ZVdbJn?1xVZ8CJCu~Tk2Cf{*dR=BN++G z!%)0|`uh`3Ltj&X39+gE?XODa>L`;B+nP$=;Y=481ewlX5u=VJ)P21S0GnN$J9nt$ zZzUf%*z!@eE&C(oqofU@ys(PFkhh*~OvBnUgea*8W=qd65Qw2pw?O?;@B$S6`=DNY z5bG@xkInAJkipX!Dn64&gAp&uLLm1maMy@KlbjNofT!o{ZWni*F$LcvvgUMjW+f>f zDen;NBSNg?7jGb-pew-LmIU#769Ik&r)me;3G}uDRx-&YlwMTZ=|+0j&7$yBW|wB1 zP@e|=#Tmi~B@Lwlbq0L@CvMNWDL~Vdgo?EJ_c0`GciC+*;*YzL9}LHHUu?uIKJz(d zfxnuHZ=A>|r4r+1`=q}rHp@pse%gCt zwk_Fsv3d8rdfdP<=bX3vnc4DqJ{b37aLPT$4di{)11<4}{V3}*%Nx&pOpm<|7aWDe z7CNf7_FEQ;w4(w}jwx;a!=PJ0*MhCR-uIob0umHSkK2G>OBhYxW5@EVtYtu@8#o5S z;5_Xi%P!M0u8Kq1=zuAL?UXNR(?id$vlFOp1j zlpmD#t}NG=1-=IfTl}^=(S!bMPofabUswipZNaF0FBDv~I9oUZWoiWwyj>nh6ltwY zfPB4nr3$9b(F5P{UV#(Nxyr~Zu=aF^_=5JqVm0TCv8Y1sXc7nQ(foxlX3Xeyt!@e! zfM#g100P*goO)+)(giOeg_E?C94PI?s0Q%kdBrW{3v1F!zF7Y`t1_+zWL4THo~JX5 z^KRV|*Cm4)MRJXtt|rdZ1Ck`3@&Nbm_$($Kw_UKxbySKlx3~3+uCta#@o30if*}*t zQnV`Fd~cCZ30&0|^an~w!F9zRD1=?SRTNDsUrxefi>&SK($3g4u)ou!q zixM*O$QTXsjpC+8iN*w3amCd+uPS+ai+F+aOJJ+4TuzE6ehdPq$@9$}V(x-a%HJ@l zyBjUPl}5af(nwgSD#{bJD|-*j*b!Q36yseXQSk)1*O6E6gdmPoHCgO7)1O;@{l)pN z)XDC$k5O=L`jqR3lFNnr2Oc+^Cn)+XJ`Tu*GUpe)ExS!`&=IgP>QE|zJ5P6&8t|m@ zFaB4>MaKHBUhcUx)Bl$UR-x4dAiML^DBYl^Q=n~5WvlES z3Z|r0%~C>h-OooI&>^y9w?cyu9#UHlQ_!*mYqI4zXnLOvmyCFOoYNOfQ_8z$ zlJV%x=a}4clj4&H%o$5`b;t0#Ep*m)fq2}clh|e`p@?I;)ogf{Lua)KQrL9eD#pd2 z3pE`i=fAjyw9DEwI!ZlGbwSeHZ(0>AeD&kqsw&F6TBT{K(SvDb#<+Y;+Ly69+M;6S`? zN9T6ns6$;6ycMXvW{)m6>_)f?lpapY42P=}$|8GXW4c?5y(lD;7{WcxAi447{wfZ1 zD=c2zv98j}OuSur*Y=obJj9u`sO9( zU$Yn8m+Hz?|H^KDr8QxmHNB|1$I-o-{-;z}fYNZE9 zcSt2Zy~-9n$F3=FF8zvgetPp~y+6Vpo?qKqA10Mzerh^w2%ufF9Wdsv?U+CBlg)b>vN%1R>i~{=}+S;^$o5 z>i4vxugSRY)I+5^@znPB`S>k_sNeSbl|w0bq#hiy>HCt)C~YpS2J47zSJIB)JhnA+ z^taun$-i7>l$OnP|A2Do+mUD0W7LYDvi-fXb|FK!IT!X5x}2PgqdS%soCh7)7=5{_ zrfWS%iah!yZr^1D_Q!$+YW?B5d-i5^0?7TgTrn}NIj*ptg|;La2W=egH859y$bd0} zNbN;3#0sU=A8%`d@xZjC0>3%B7N~5Okkwqk`YhIGrNE}@n*6ai%z`e>gID;Vw8$8R zQi~7NPq>~^+rma!X>O$$YGwX)|4eRTzVyDAlTtB9qI7e+eXTuXaD%{mlc6dBOc%Xe z*L}Rm&l;laf6p!}C~eX2xNgt&hr!-P{0~0nB+Hw8LITTfSn{Lxts&6WN*nP64{nKy zl}@OV>V6t}CP7^q9=`U*ciwmusQ0fc-xssa3U%BR^{zXL^V#q!J&rZ-(aRMxXZMCa zmW6tpSjZ+XU&OAJw|vfEy?;1x%;J7(s7y+d0TV^n_z;ib2wg5GW`7i3>k>RGrC)Ir z8LDy(Mb?SA>qQZx)51VHeV)TgNQNRCE<3g|y@(=I&D(J|Rc^m2nJ1FfBfK%(nsq3X zTSD`bX!!|-mVDR^{@;$`c4#)3P8$YuuQSlPl@d6h zV9;^zNR#3kUPHTN79Hta#|^Pg0(-Xt>EpN1&~6+n)R|B2TncJ1wvL`2U^!fir+dmx zczpOOyR@T4KlE56#rfF(GF3&udziMFCg8JAHJ5OB4UVV1SY!fn?4v50I0CZSQh*7#fQ(@=|q?gv4UYeox|4%yGz`+Xuq0% zyWN$mn0ibXEL1D8dT*_@^C=8EbU6<0q~@pyZC#)Rt|P-Y1R7nmwT<~|?2OUEGMCQ# zfj$NJQ+$nC8brbeF~pt+(HHaQUq-6V#7Vpb?FMdUda+wnd~V;-TqYnOP`MsI zU=IeiGs^F$UN@m+Nwe_d2fvcC@&I|$$o2-=>rgS8_s1)a4{1ctqNW``q4vKIuk}vQ zf+_KJh6dSW*iBmW>=y6TiZNf6O9{C@qdqvL|MW=EZ|FwJ5`x90pIQUDDHX8SJ|$DZ zi$kSf3!$mA-#6lGc&d_5XsIpvrCXc#_JErQj)wfjrz#P%TYBj(qG=)x*T8$sO8qx~ z^=#snWt~g?NMXe`a8s_S&1CV0?iWdIQ#31OY;2lx z#eORnDrGl`69I0rCebe9qPWC$8%=_at$FtrS8lViNxz06% zBbTzXf7~#S;WKVrB#M6LRrc3jyALgowrBc*>%M8bY@}|L9q|}WUVKf2*!mGd50ys? zepC#f>AAcaxM1G{{K&{i^esev+ue1Kqn522iwFw~ODJu#|??W@WcUW`ss$0f7 Z1CO9$UFG}$2bT_m$xACql}H%*{U547X-)tD diff --git a/report_xls/utils.py b/report_xls/utils.py deleted file mode 100644 index 943daae7c..000000000 --- a/report_xls/utils.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# -# Copyright (c) 2014 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 . -# -############################################################################## - - -def _render(code): - return compile(code, '', '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: From c5ff5db10522f400bce22229819d411bb894f2e1 Mon Sep 17 00:00:00 2001 From: luc-demeyer Date: Sun, 5 Oct 2014 17:54:11 +0200 Subject: [PATCH 16/69] remove report_xls from __unported__ --- __unported__/report_xls/__init__.py | 1 + __unported__/report_xls/utils.py | 1 + 2 files changed, 2 insertions(+) diff --git a/__unported__/report_xls/__init__.py b/__unported__/report_xls/__init__.py index e100e7861..6142d4597 100644 --- a/__unported__/report_xls/__init__.py +++ b/__unported__/report_xls/__init__.py @@ -22,4 +22,5 @@ from . import ir_report from . import report_xls + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/__unported__/report_xls/utils.py b/__unported__/report_xls/utils.py index 943daae7c..ad9887ea4 100644 --- a/__unported__/report_xls/utils.py +++ b/__unported__/report_xls/utils.py @@ -48,4 +48,5 @@ def rowcol_to_cell(row, col, row_abs=False, col_abs=False): # Zero index to 1-index return col_abs + chr1 + chr2 + row_abs + str(row + 1) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 7480f51e99727a80e57fc4b4c7414a6b9f508e3c Mon Sep 17 00:00:00 2001 From: luc-demeyer Date: Sun, 5 Oct 2014 18:28:25 +0200 Subject: [PATCH 17/69] 80 - report_xls port --- .../report_xls/static/src/img/icon.png | Bin 11647 -> 0 bytes .../report_xls => report_xls}/__init__.py | 1 - .../report_xls => report_xls}/__openerp__.py | 2 +- report_xls/ir_report.py | 36 ++++++++ .../report_xls => report_xls}/report_xls.py | 0 report_xls/static/description/help.png | Bin 0 -> 31176 bytes .../description/{icon2.png => icon.png} | Bin report_xls/static/description/index.html | 84 ++++++++++++++++++ .../static/description/open_receivables.png | Bin 0 -> 50405 bytes .../report_xls => report_xls}/utils.py | 1 - 10 files changed, 121 insertions(+), 3 deletions(-) delete mode 100644 __unported__/report_xls/static/src/img/icon.png rename {__unported__/report_xls => report_xls}/__init__.py (99%) rename {__unported__/report_xls => report_xls}/__openerp__.py (99%) create mode 100644 report_xls/ir_report.py rename {__unported__/report_xls => report_xls}/report_xls.py (100%) create mode 100644 report_xls/static/description/help.png rename report_xls/static/description/{icon2.png => icon.png} (100%) create mode 100644 report_xls/static/description/index.html create mode 100644 report_xls/static/description/open_receivables.png rename {__unported__/report_xls => report_xls}/utils.py (99%) diff --git a/__unported__/report_xls/static/src/img/icon.png b/__unported__/report_xls/static/src/img/icon.png deleted file mode 100644 index 44c5db163b5add8d42862f23593457fb657aa963..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11647 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
71jZ54I{ z7*7xsL@}0c#HNgCWJJtNN0V`+9;FU|ZpRmn6qt|a3D2m-M|(-MyBjonac3`V11xME zg*m>?d%d`=IAZ04ez5$^`b%!S+cWJN+{rq@_SvTIN{_ z$!s~pnrD(^S;}mNZl6XBvmPU$pnC{*=+I$78Bb-Zg4FOm2m{QY1TTrU*^WwB?@;e} z-tVMjZ?D^IZq>$W1~$lX>;=1x-K{|y#5#g+1ON57Uw}d}ij-^%kLMK$$NfUFP!b4% z!}Nv|H_lGan#L?DLt*}ad77I+7~6Ma+i(ovm~CA|jw#Z%SZ%>*I6Y16fLg=%V9jhY zVuXxW_6nQxGnL&|Elp2MQ#43w@Fel8Q@q>k1dwVWlDIF zYJzJAsSZOq7-WGazede-#{a5eW2CTEZ0HNrA>4_Yd#&nNh5kUnh76Maa_dqz?M7LQ zPkN5;D^7(=DqUhqK)wp3QxJZUegmq#A}dzhO35pa>JZQ^s2{urs06I>N{kroF&&B{ zQ0&Oui88=8Gx`p@6e>yvgSekcOhK9=s;Og)n)CCGsKP=GtY@k`2ay|fR1jsD4D6O( z@{fjJ*;6y>i?NMjxf>&xOjIx!Aa*rZunlclFYGq@^=8=Y#{+Oy0J&%L5H=s-kWM** zG7-YZ_|jw$Z9cp4?cey;)3=R@e++n}9l^!0FKIJCu^3YqqE^7(HO z4(BGP&37mWXE+M(RLIF#% zRfYT^T8A*iu^Xldn@x`Zeqt4mk55p@V2m}y@+1*6kun9ZGFKvF#={NhCZ-& zGjj#gltCFlokej-1zqTnLO_dwX08nna`$fg*)bq^F+^=~(C)=aRz>ujS1K$4Ca2Tu z_HBR2D9|85#4h2iuYDI`66IEDgokGiL3JY@!wN}QGG1L@JA3X~ggvk>pSY7py%o4J zWr0nGnb3--a8BS`X*@YW869r6fyPRPI*B!+d_|UK>a%wLhM}xEjkDz~8#2XNGBG~s zLG3e20(0_N2+JInIX-p6%7iMU4&jt_8~OJJXzFqSflFOi7E7vDaL55&ZqTHDG`_pr zLkMmYmX`&=huw>$UV`xNREf>b%yH8|obO`iVtR7+o||92wzjst-Pql2t+u`bI|RFB zg#x&3u~Y�E9#BBY_0D&Xu`ufJk_Fj8) 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 JM6N<$f(rRbzo7sC diff --git a/__unported__/report_xls/__init__.py b/report_xls/__init__.py similarity index 99% rename from __unported__/report_xls/__init__.py rename to report_xls/__init__.py index 6142d4597..e100e7861 100644 --- a/__unported__/report_xls/__init__.py +++ b/report_xls/__init__.py @@ -22,5 +22,4 @@ from . import ir_report from . import report_xls - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/__unported__/report_xls/__openerp__.py b/report_xls/__openerp__.py similarity index 99% rename from __unported__/report_xls/__openerp__.py rename to report_xls/__openerp__.py index e53584ffe..1ca25bbc2 100644 --- a/__unported__/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -77,6 +77,6 @@ Excel reports in odoo. 'depends': ['base'], 'external_dependencies': {'python': ['xlwt']}, 'active': False, - 'installable': False, + 'installable': True, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/report_xls/ir_report.py b/report_xls/ir_report.py new file mode 100644 index 000000000..f35e60690 --- /dev/null +++ b/report_xls/ir_report.py @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# +# Copyright (c) 2014 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 . +# +############################################################################## + +from openerp.osv import fields, orm + + +class ir_actions_report_xml(orm.Model): + _inherit = 'ir.actions.report.xml' + + def _check_selection_field_value(self, cr, uid, + field, value, context=None): + if field == 'report_type' and value == 'xls': + return + return super(ir_actions_report_xml, self)._check_selection_field_value( + cr, uid, field, value, context=context) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/__unported__/report_xls/report_xls.py b/report_xls/report_xls.py similarity index 100% rename from __unported__/report_xls/report_xls.py rename to report_xls/report_xls.py diff --git a/report_xls/static/description/help.png b/report_xls/static/description/help.png new file mode 100644 index 0000000000000000000000000000000000000000..3e7338c8a713379f50f669d091c9785b82ec5b14 GIT binary patch literal 31176 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 literal 0 HcmV?d00001 diff --git a/report_xls/static/description/icon2.png b/report_xls/static/description/icon.png similarity index 100% rename from report_xls/static/description/icon2.png rename to report_xls/static/description/icon.png diff --git a/report_xls/static/description/index.html b/report_xls/static/description/index.html new file mode 100644 index 000000000..1cdf4263c --- /dev/null +++ b/report_xls/static/description/index.html @@ -0,0 +1,84 @@ +
+
+
+

Excel report engine

+
+
+
+ +
+
+
+

+

This module adds Excel export capabilities to the standard odoo reporting engine. +

+
+
+
+ +
+
+

Report development

+

+ In order to create an Excel report you can +

    +
  • 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 odoo. +

    +
  • + 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 odoo 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.odoo.com as example. +
  • +
+
    +
  • + XLS with multiple sheets +
    Download the account_journal_report_xls module from http://apps.odoo.com as example. +
  • +
+

+
+
+ +
+
+
+

Development assistance

+
+
+
+
+
+
+
+
+

+

Contact info@noviat.com for help with the development of Excel reports in odoo. +

+
+
+
+ diff --git a/report_xls/static/description/open_receivables.png b/report_xls/static/description/open_receivables.png new file mode 100644 index 0000000000000000000000000000000000000000..a00c3ba4497e9c8f48f61915feb5c9ef42c6b672 GIT binary patch literal 50405 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 Z1CO9$UFG}$2bT_m$xACql}H%*{U547X-)tD literal 0 HcmV?d00001 diff --git a/__unported__/report_xls/utils.py b/report_xls/utils.py similarity index 99% rename from __unported__/report_xls/utils.py rename to report_xls/utils.py index ad9887ea4..943daae7c 100644 --- a/__unported__/report_xls/utils.py +++ b/report_xls/utils.py @@ -48,5 +48,4 @@ def rowcol_to_cell(row, col, row_abs=False, col_abs=False): # Zero index to 1-index return col_abs + chr1 + chr2 + row_abs + str(row + 1) - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 47800d626cc700de7686045448036e45c1760133 Mon Sep 17 00:00:00 2001 From: luc-demeyer Date: Sun, 5 Oct 2014 19:13:01 +0200 Subject: [PATCH 18/69] remove unused imports --- report_xls/ir_report.py | 2 +- report_xls/report_xls.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/report_xls/ir_report.py b/report_xls/ir_report.py index f35e60690..332f32f08 100644 --- a/report_xls/ir_report.py +++ b/report_xls/ir_report.py @@ -20,7 +20,7 @@ # ############################################################################## -from openerp.osv import fields, orm +from openerp.osv import orm class ir_actions_report_xml(orm.Model): diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py index 13fcb5bb1..4746b4b91 100644 --- a/report_xls/report_xls.py +++ b/report_xls/report_xls.py @@ -28,9 +28,8 @@ from openerp.osv.fields import datetime as datetime_field from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT import inspect from types import CodeType -from openerp.report.report_sxw import * +from openerp.report.report_sxw import report_sxw from openerp import pooler -from openerp.tools.translate import translate, _ import logging _logger = logging.getLogger(__name__) From 38935975ac66943b5096dd18faeb131ff63e3924 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 16 Dec 2014 11:18:08 +0100 Subject: [PATCH 19/69] Separated Lint tests --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index efa1f2775..9fb09fab9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,9 @@ python: - "2.7" env: - - VERSION="8.0" ODOO_REPO="odoo/odoo" - - VERSION="8.0" ODOO_REPO="OCA/OCB" + - VERSION="8.0" LINT_CHECK="1" + - VERSION="8.0" ODOO_REPO="odoo/odoo" LINT_CHECK="0" + - VERSION="8.0" ODOO_REPO="OCA/OCB" LINT_CHECK="0" virtualenv: system_site_packages: true From 63bc58ae9860f8babf25f026db59f5e23c6b90ba Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 19 Dec 2014 13:19:55 +0100 Subject: [PATCH 20/69] [ADD] report_custom_filename --- report_custom_filename/__init__.py | 22 ++++++++ report_custom_filename/__openerp__.py | 53 ++++++++++++++++++ .../controllers/__init__.py | 21 +++++++ report_custom_filename/controllers/reports.py | 53 ++++++++++++++++++ .../i18n/report_custom_filename.pot | 34 +++++++++++ report_custom_filename/model/__init__.py | 21 +++++++ .../model/ir_actions_report_xml.py | 35 ++++++++++++ .../static/description/icon.png | Bin 0 -> 1817 bytes .../view/ir_actions_report_xml.xml | 14 +++++ 9 files changed, 253 insertions(+) create mode 100644 report_custom_filename/__init__.py create mode 100644 report_custom_filename/__openerp__.py create mode 100644 report_custom_filename/controllers/__init__.py create mode 100644 report_custom_filename/controllers/reports.py create mode 100644 report_custom_filename/i18n/report_custom_filename.pot create mode 100644 report_custom_filename/model/__init__.py create mode 100644 report_custom_filename/model/ir_actions_report_xml.py create mode 100644 report_custom_filename/static/description/icon.png create mode 100644 report_custom_filename/view/ir_actions_report_xml.xml diff --git a/report_custom_filename/__init__.py b/report_custom_filename/__init__.py new file mode 100644 index 000000000..cba0a4dc2 --- /dev/null +++ b/report_custom_filename/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +from . import controllers +from . import model diff --git a/report_custom_filename/__openerp__.py b/report_custom_filename/__openerp__.py new file mode 100644 index 000000000..a98083902 --- /dev/null +++ b/report_custom_filename/__openerp__.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +{ + "name": "Custom report filenames", + "summary": "Configure the filename to use when downloading a report", + "version": "1.0", + "author": "Therp BV", + "license": "AGPL-3", + "complexity": "normal", + "description": """ +This addon allows for custom filenames for reports. Currently, only old-style +reports (ir.actions.report.xml) are supported, it should be simple to add +support for qweb reports. + +Acknowledgements +================ +Icon courtesy of http://www.picol.org/ (download_settings.svg) + """, + "category": "Reporting", + "depends": [ + 'web', + 'email_template', + ], + "data": [ + "view/ir_actions_report_xml.xml", + ], + "test": [ + ], + "auto_install": False, + "installable": True, + "application": False, + "external_dependencies": { + 'python': ['jinja2'], + }, +} diff --git a/report_custom_filename/controllers/__init__.py b/report_custom_filename/controllers/__init__.py new file mode 100644 index 000000000..329fca162 --- /dev/null +++ b/report_custom_filename/controllers/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +from . import reports diff --git a/report_custom_filename/controllers/reports.py b/report_custom_filename/controllers/reports.py new file mode 100644 index 000000000..1ff58ed0b --- /dev/null +++ b/report_custom_filename/controllers/reports.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +import simplejson +from openerp import http +from openerp.addons.web.controllers import main +from openerp.addons.email_template import email_template + + +class Reports(main.Reports): + @http.route('/web/report', type='http', auth="user") + @main.serialize_exception + def index(self, action, token): + result = super(Reports, self).index(action, token) + action = simplejson.loads(action) + context = dict(http.request.context) + context.update(action["context"]) + report_xml = http.request.session.model('ir.actions.report.xml') + report_ids = report_xml.search( + [('report_name', '=', action['report_name'])], + 0, False, False, context) + for report in report_xml.browse(report_ids): + if not report.download_filename: + continue + objects = http.request.session.model(context['active_model'])\ + .browse(context['active_ids']) + generated_filename = email_template.mako_template_env\ + .from_string(report.download_filename)\ + .render({ + 'objects': objects, + 'o': objects[0], + 'object': objects[0], + }) + result.headers['Content-Disposition'] = main.content_disposition( + generated_filename) + return result diff --git a/report_custom_filename/i18n/report_custom_filename.pot b/report_custom_filename/i18n/report_custom_filename.pot new file mode 100644 index 000000000..fa2ec82c8 --- /dev/null +++ b/report_custom_filename/i18n/report_custom_filename.pot @@ -0,0 +1,34 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_custom_filename +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-19 12:17+0000\n" +"PO-Revision-Date: 2014-12-19 12:17+0000\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: report_custom_filename +#: field:ir.actions.report.xml,download_filename:0 +msgid "Download filename" +msgstr "" + +#. module: report_custom_filename +#: help:ir.actions.report.xml,download_filename:0 +msgid "Fill in this field to have a custom file name when downloading this report. This string is evaluated as a jinja2 expression.\n" +"You can use python expressions, `objects` is a browse record list of the objects for which the report is being generated.\n" +"Check for this list's length to determine if it is a report being printed for multiple records or not. You also have access to `o`, which is the first record in the list" +msgstr "" + +#. module: report_custom_filename +#: model:ir.model,name:report_custom_filename.model_ir_actions_report_xml +msgid "ir.actions.report.xml" +msgstr "" + diff --git a/report_custom_filename/model/__init__.py b/report_custom_filename/model/__init__.py new file mode 100644 index 000000000..b7da7af10 --- /dev/null +++ b/report_custom_filename/model/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +from . import ir_actions_report_xml diff --git a/report_custom_filename/model/ir_actions_report_xml.py b/report_custom_filename/model/ir_actions_report_xml.py new file mode 100644 index 000000000..27cd638c0 --- /dev/null +++ b/report_custom_filename/model/ir_actions_report_xml.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 Therp BV (). +# +# 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 . +# +############################################################################## +from openerp import models, fields + + +class IrActionsReportXml(models.Model): + _inherit = 'ir.actions.report.xml' + + download_filename = fields.Char( + 'Download filename', + help='Fill in this field to have a custom file name when downloading ' + 'this report. This string is evaluated as a jinja2 expression.\n' + 'You can use python expressions, `objects` is a browse record list of ' + 'the objects for which the report is being generated.\n' + 'Check for this list\'s length to determine if it is a report being ' + 'printed for multiple records or not. You also have access to `o`, ' + 'which is the first record in the list') diff --git a/report_custom_filename/static/description/icon.png b/report_custom_filename/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..df9ef01c1a78075d2df5de33a105ecf917d5a580 GIT binary patch literal 1817 zcmai#X*kpi8^-^$G{!O*>!`?UDN2$hOM@_DhM~qe6spVEmzg<@tXanrBAHUi5^6?< zW2vD<(O8akZUC`ya$&j??24hLf3tTxGDP5)o>fSOUKdwL+pq6V>!& zpjeUyJe`Y|mP@#+ss&cJ`u`95peH)&M8BnYV+`YxF$wjz)IXAi2X;YsAR-ANU=52f zP#MS=Gy<8YA1AyiJP-#9hZH0nB;~5;Vg-?+Pa!K{oI1Sg2MjHB`$5^p@1}Yl?AUPgRmK~K;a%4SXEKx1kcH>N-XY}*iiDJU!TmsRHQ<5vs$_<`m;T> z%k3^Z79>QGPSH>%rPHgAxI~hn9w-mwFnrM+^^+CL6bRTph^~)LC|nTC0=)chz-Y{d zNJW@>s`kpOaIXXSf+6sqK=tj~)L!t5l)_7XVvO}bMLkX1J^rqAlcOxH9&oxt%2MOu za)HZ-!%TrI&K2j}>BllC8lec!KjQg^8Y;Rv+hS|{z0U(pdK$&BuTY(cp_+yvI@+B( z9?Q|RoF-9BYlrVmBS|DS;HEH#SUv(`*o%~_+(6Ej)V-CX$JC4%QS_#U&(F++18lG` z;QJXZ?}Y3WbRUrGtAyvY*7Y`bPEIS>yQ-R^w#)}$p92X^gy#~Ul%-JAQfKff_q9h) z8@FHRwVY!EanB;ZnTu!oeh4}{j=Lb=H9>(@gX8qMk%Y6?tjx+2LL=^fI~MeFN=`3E zHZpIfFwQ>_=+cm1|KRt67`S01|DV4U$tg4J> zKZ?w%!+5lxhP7if>OtaNP1X4Msj%yK{AD!dXwenH)$e^FiQUi)>D=OME-rEmQe^j4$<^xGe1S zR}t3VcY>iZQ>F{CMcSg3#u8f;d+!V7254T0%8gLaE;lY96cL2*KD~TZgN!aN9cJ_` z1wz*nW<6uj8^?KM*_%u=5~JfvL_4ETegE+4bJenFbYDl5K#F(1FTFM0xAaR_e_1W)R zIWOZ~rwUYj1dSB-I#J-CC*01-zFx&KoB($)UbX>{$ySFgK3P@q4|?=pPSY+qF^ay; z2F2;I8GM$cRu7o;9q7V}e6iu?kr(?@)x~x+tczU_)+p4APr_Ic-04{Q=-h9nh>kcv zAEeR%o}k5&57Xd|(7qk;8U$uUQnBI9iLPVE?p>jD`M6~cW-mr%!30dl!7+MWmu!0! z>G5ooI8CaV@RohibTzZp_`CGxoi;6_iGBn3u-iJE22{QLP`!Gb1M-W$;-+b9zE0k2 z-K$CN?e#`&c0=t+>8S1_iDn(6GA-FPOibtreh6wSu=q}NSY`N#O(0Lk*yj(v~f6 z; + + + + ir.actions.report.xml + + + + + + + + + From 4bc87ce4b1cbdc119969d3542c9ecceba17ce7f7 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 23 Jan 2015 08:57:13 +0100 Subject: [PATCH 21/69] [ADD] move description to README.rst --- report_custom_filename/README.rst | 40 +++++++++++++++++++++++++++ report_custom_filename/__openerp__.py | 9 ------ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 report_custom_filename/README.rst diff --git a/report_custom_filename/README.rst b/report_custom_filename/README.rst new file mode 100644 index 000000000..933573605 --- /dev/null +++ b/report_custom_filename/README.rst @@ -0,0 +1,40 @@ +Custom report filenames +======================= + +This addon allows for custom filenames for reports. + +Configuration +============= + +To configure this module, open the report whose filename you want to change and fill in the `Download filename` field. This field is evaluated as jinja2 template with `objects` being a list of browse records of the records to print, and `o` the first record. + +Known issues / Roadmap +====================== + + * Currently, only old-style reports (ir.actions.report.xml) are supported, it should be simple to add support for qweb reports. + +Credits +======= + +Contributors +------------ + +* Holger Brunn + +Icon +---- + +Icon courtesy of http://www.picol.org/ (download_settings.svg) + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/report_custom_filename/__openerp__.py b/report_custom_filename/__openerp__.py index a98083902..212a6b91b 100644 --- a/report_custom_filename/__openerp__.py +++ b/report_custom_filename/__openerp__.py @@ -25,15 +25,6 @@ "author": "Therp BV", "license": "AGPL-3", "complexity": "normal", - "description": """ -This addon allows for custom filenames for reports. Currently, only old-style -reports (ir.actions.report.xml) are supported, it should be simple to add -support for qweb reports. - -Acknowledgements -================ -Icon courtesy of http://www.picol.org/ (download_settings.svg) - """, "category": "Reporting", "depends": [ 'web', From ae1ef0230214f0103b2ac0f0449228206dd8e116 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 2 Mar 2015 17:27:27 +0100 Subject: [PATCH 22/69] Add OCA as author of OCA addons In order to get visibility on https://www.odoo.com/apps the OCA board has decided to add the OCA as author of all the addons maintained as part of the association. --- __unported__/base_report_assembler/__openerp__.py | 2 +- report_custom_filename/__openerp__.py | 2 +- report_xls/__openerp__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__unported__/base_report_assembler/__openerp__.py b/__unported__/base_report_assembler/__openerp__.py index 03b4e7d43..897565518 100644 --- a/__unported__/base_report_assembler/__openerp__.py +++ b/__unported__/base_report_assembler/__openerp__.py @@ -37,7 +37,7 @@ the folling module(s): - Invoices: invoice_report_assemble (lp:account-invoice-report) """, - 'author': 'Camptocamp', + 'author': "Camptocamp,Odoo Community Association (OCA)", 'maintainer': 'Camptocamp', 'website': 'http://www.camptocamp.com/', 'depends': ['base'], diff --git a/report_custom_filename/__openerp__.py b/report_custom_filename/__openerp__.py index 212a6b91b..6777ec55e 100644 --- a/report_custom_filename/__openerp__.py +++ b/report_custom_filename/__openerp__.py @@ -22,7 +22,7 @@ "name": "Custom report filenames", "summary": "Configure the filename to use when downloading a report", "version": "1.0", - "author": "Therp BV", + "author": "Therp BV,Odoo Community Association (OCA)", "license": "AGPL-3", "complexity": "normal", "category": "Reporting", diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index 1ca25bbc2..4c56fa770 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -23,7 +23,7 @@ 'name': 'Excel report engine', 'version': '0.6', 'license': 'AGPL-3', - 'author': 'Noviat', + 'author': "Noviat,Odoo Community Association (OCA)", 'website': 'http://www.noviat.com', 'category': 'Reporting', 'description': """ From 27a61abccf56de0ce185fdc64b346b229496c981 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Fri, 27 Mar 2015 16:21:13 +0100 Subject: [PATCH 23/69] don't use travis container-based infrastructure --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9fb09fab9..472990355 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: required language: python python: From 3ec96ec45428cfb25a754184ecabafd158af88aa Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Thu, 14 May 2015 22:14:39 +0200 Subject: [PATCH 24/69] Update travis configuration Follow OCA/maintainer-quality-tools#187 and use addons/apt/package directive to install our build dependencies. This allows using container based Travis builds (which should be faster). --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 472990355..ba6f095cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ -sudo: required +addons: + apt: + packages: + - expect-dev # provides unbuffer utility + - python-lxml # because pip installation is slow + language: python python: From 4c4c4b1f95ba9fb6ecd73a32b9c5850ed5f1bc49 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Fri, 22 May 2015 19:45:34 +0200 Subject: [PATCH 25/69] Add bug tracker link on README.rst --- report_custom_filename/README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/report_custom_filename/README.rst b/report_custom_filename/README.rst index 933573605..ff4f92db8 100644 --- a/report_custom_filename/README.rst +++ b/report_custom_filename/README.rst @@ -13,6 +13,16 @@ Known issues / Roadmap * Currently, only old-style reports (ir.actions.report.xml) are supported, it should be simple to add support for qweb reports. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 +`here `_. + + Credits ======= From d603677db90fe479ddb3089d936de0f1fc29f192 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 3 Jul 2015 12:05:44 +0200 Subject: [PATCH 26/69] Add LICENSE. --- LICENSE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..3ffc56789 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. \ No newline at end of file From de92678f4f480d2fae3268d4c4ade03bf485b2d4 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Fri, 24 Jul 2015 23:04:29 +0200 Subject: [PATCH 27/69] Update travis configuration use sudo: false to enable container build use cache: pip to cache pip packages --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index ba6f095cc..867e15e13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +sudo: false +cache: pip + addons: apt: packages: From 2d5d780db0e0347a55ad4400aa5f360f9d614ebc Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Aug 2015 16:12:12 +0200 Subject: [PATCH 28/69] [ADD] support qweb reports --- report_custom_filename/README.rst | 11 ++- .../controllers/__init__.py | 1 + .../controllers/report_controller.py | 68 +++++++++++++++++++ .../model/ir_actions_report_xml.py | 4 +- 4 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 report_custom_filename/controllers/report_controller.py diff --git a/report_custom_filename/README.rst b/report_custom_filename/README.rst index ff4f92db8..ca2d8b61a 100644 --- a/report_custom_filename/README.rst +++ b/report_custom_filename/README.rst @@ -6,13 +6,7 @@ This addon allows for custom filenames for reports. Configuration ============= -To configure this module, open the report whose filename you want to change and fill in the `Download filename` field. This field is evaluated as jinja2 template with `objects` being a list of browse records of the records to print, and `o` the first record. - -Known issues / Roadmap -====================== - - * Currently, only old-style reports (ir.actions.report.xml) are supported, it should be simple to add support for qweb reports. - +To configure this module, open the report whose filename you want to change and fill in the `Download filename` field. This field is evaluated as jinja2 template with `objects` being a list of browse records of the records to print, and `o` the first record. If your model contains a name field, you might write something like `${o.name}_report.pdf` as filename. Bug Tracker =========== @@ -22,6 +16,9 @@ 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 `here `_. +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/143/8.0 Credits ======= diff --git a/report_custom_filename/controllers/__init__.py b/report_custom_filename/controllers/__init__.py index 329fca162..6eb84a902 100644 --- a/report_custom_filename/controllers/__init__.py +++ b/report_custom_filename/controllers/__init__.py @@ -19,3 +19,4 @@ # ############################################################################## from . import reports +from . import report_controller diff --git a/report_custom_filename/controllers/report_controller.py b/report_custom_filename/controllers/report_controller.py new file mode 100644 index 000000000..536d2d698 --- /dev/null +++ b/report_custom_filename/controllers/report_controller.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2015 Therp BV (). +# +# 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 . +# +############################################################################## +from openerp import http +from openerp.addons.email_template import email_template +from openerp.addons.report.controllers.main import ReportController +from openerp.addons.web.controllers.main import content_disposition + + +class ReportController(ReportController): + @http.route([ + '/report//', + '/report///', + ]) + def report_routes(self, reportname, docids=None, converter=None, **data): + response = super(ReportController, self).report_routes( + reportname, docids=docids, converter=converter, **data) + if docids: + docids = [int(i) for i in docids.split(',')] + report_xml = http.request.session.model('ir.actions.report.xml') + report_ids = report_xml.search( + [('report_name', '=', reportname)]) + for report in report_xml.browse(report_ids): + if not report.download_filename: + continue + objects = http.request.session.model(report.model)\ + .browse(docids or []) + generated_filename = email_template.mako_template_env\ + .from_string(report.download_filename)\ + .render({ + 'objects': objects, + 'o': objects[:1], + 'object': objects[:1], + 'ext': report.report_type.replace('qweb-', ''), + }) + response.headers['Content-Disposition'] = content_disposition( + generated_filename) + return response + + @http.route(['/report/download']) + def report_download(self, data, token): + response = super(ReportController, self).report_download(data, token) + # if we got another content disposition before, ditch the one added + # by super() + last_index = None + for i in range(len(response.headers) - 1, -1, -1): + if response.headers[i][0] == 'Content-Disposition': + if last_index: + response.headers.pop(last_index) + last_index = i + return response diff --git a/report_custom_filename/model/ir_actions_report_xml.py b/report_custom_filename/model/ir_actions_report_xml.py index 27cd638c0..5ea043006 100644 --- a/report_custom_filename/model/ir_actions_report_xml.py +++ b/report_custom_filename/model/ir_actions_report_xml.py @@ -32,4 +32,6 @@ class IrActionsReportXml(models.Model): 'the objects for which the report is being generated.\n' 'Check for this list\'s length to determine if it is a report being ' 'printed for multiple records or not. You also have access to `o`, ' - 'which is the first record in the list') + 'which is the first record in the list.\n' + 'For qweb reports, the variable `ext` gives you the requested format' + '\'s extension') From bfe32e96cf99f57e7104b7ba9d7dd958c7926a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Wed, 23 Sep 2015 16:27:29 +0200 Subject: [PATCH 29/69] [ADD] addons table in README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index e1e94d847..56febe96e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,22 @@ The convention is to use a suffix to each module to indicate that it's for using It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). +[//]: # (addons) +Available addons +---------------- +addon | version | summary +--- | --- | --- +[report_custom_filename](report_custom_filename/) | 1.0 | Configure the filename to use when downloading a report +[report_xls](report_xls/) | 0.6 | Excel report engine + +Unported addons +--------------- +addon | version | summary +--- | --- | --- +[base_report_assembler](__unported__/base_report_assembler/) | 1.0 (unported) | Base Report Assembler + +[//]: # (end addons) + ---- OCA, or the Odoo Community Association, is a nonprofit organization whose From 822b6a0f054590ed3214cea72d2cfe6a546cf44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 9 Oct 2015 10:02:57 +0200 Subject: [PATCH 30/69] [UPD] prefix versions with 8.0 --- README.md | 4 ++-- report_custom_filename/__openerp__.py | 2 +- report_xls/__openerp__.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 56febe96e..36a44f28a 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Available addons ---------------- addon | version | summary --- | --- | --- -[report_custom_filename](report_custom_filename/) | 1.0 | Configure the filename to use when downloading a report -[report_xls](report_xls/) | 0.6 | Excel report engine +[report_custom_filename](report_custom_filename/) | 8.0.1.0.0 | Configure the filename to use when downloading a report +[report_xls](report_xls/) | 8.0.0.6.0 | Excel report engine Unported addons --------------- diff --git a/report_custom_filename/__openerp__.py b/report_custom_filename/__openerp__.py index 6777ec55e..6f698812c 100644 --- a/report_custom_filename/__openerp__.py +++ b/report_custom_filename/__openerp__.py @@ -21,7 +21,7 @@ { "name": "Custom report filenames", "summary": "Configure the filename to use when downloading a report", - "version": "1.0", + "version": "8.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", "license": "AGPL-3", "complexity": "normal", diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index 4c56fa770..f7204248c 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -21,7 +21,7 @@ ############################################################################## { 'name': 'Excel report engine', - 'version': '0.6', + 'version': '8.0.0.6.0', 'license': 'AGPL-3', 'author': "Noviat,Odoo Community Association (OCA)", 'website': 'http://www.noviat.com', From 03d0839be5fc488040a42b523d8d5c5fb1c331e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Tue, 13 Oct 2015 16:58:45 +0200 Subject: [PATCH 31/69] [MOV] move addons out of __unported__ (they remain not installable) --- README.md | 2 +- .../base_report_assembler => base_report_assembler}/__init__.py | 0 .../__openerp__.py | 0 .../assembled_report.py | 0 .../i18n/base_report_assembler.pot | 0 .../base_report_assembler => base_report_assembler}/i18n/fr.po | 0 .../ir_report.py | 0 .../report_assembler.py | 0 8 files changed, 1 insertion(+), 1 deletion(-) rename {__unported__/base_report_assembler => base_report_assembler}/__init__.py (100%) rename {__unported__/base_report_assembler => base_report_assembler}/__openerp__.py (100%) rename {__unported__/base_report_assembler => base_report_assembler}/assembled_report.py (100%) rename {__unported__/base_report_assembler => base_report_assembler}/i18n/base_report_assembler.pot (100%) rename {__unported__/base_report_assembler => base_report_assembler}/i18n/fr.po (100%) rename {__unported__/base_report_assembler => base_report_assembler}/ir_report.py (100%) rename {__unported__/base_report_assembler => base_report_assembler}/report_assembler.py (100%) diff --git a/README.md b/README.md index 36a44f28a..384dad989 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Unported addons --------------- addon | version | summary --- | --- | --- -[base_report_assembler](__unported__/base_report_assembler/) | 1.0 (unported) | Base Report Assembler +[base_report_assembler](base_report_assembler/) | 1.0 (unported) | Base Report Assembler [//]: # (end addons) diff --git a/__unported__/base_report_assembler/__init__.py b/base_report_assembler/__init__.py similarity index 100% rename from __unported__/base_report_assembler/__init__.py rename to base_report_assembler/__init__.py diff --git a/__unported__/base_report_assembler/__openerp__.py b/base_report_assembler/__openerp__.py similarity index 100% rename from __unported__/base_report_assembler/__openerp__.py rename to base_report_assembler/__openerp__.py diff --git a/__unported__/base_report_assembler/assembled_report.py b/base_report_assembler/assembled_report.py similarity index 100% rename from __unported__/base_report_assembler/assembled_report.py rename to base_report_assembler/assembled_report.py diff --git a/__unported__/base_report_assembler/i18n/base_report_assembler.pot b/base_report_assembler/i18n/base_report_assembler.pot similarity index 100% rename from __unported__/base_report_assembler/i18n/base_report_assembler.pot rename to base_report_assembler/i18n/base_report_assembler.pot diff --git a/__unported__/base_report_assembler/i18n/fr.po b/base_report_assembler/i18n/fr.po similarity index 100% rename from __unported__/base_report_assembler/i18n/fr.po rename to base_report_assembler/i18n/fr.po diff --git a/__unported__/base_report_assembler/ir_report.py b/base_report_assembler/ir_report.py similarity index 100% rename from __unported__/base_report_assembler/ir_report.py rename to base_report_assembler/ir_report.py diff --git a/__unported__/base_report_assembler/report_assembler.py b/base_report_assembler/report_assembler.py similarity index 100% rename from __unported__/base_report_assembler/report_assembler.py rename to base_report_assembler/report_assembler.py From 293e78dcd583d118f8f437c20614df075a438753 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Wed, 14 Oct 2015 02:33:57 +0200 Subject: [PATCH 32/69] [MIG] Make modules uninstallable --- report_custom_filename/__openerp__.py | 2 +- report_xls/__openerp__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/report_custom_filename/__openerp__.py b/report_custom_filename/__openerp__.py index 6f698812c..1b78fd7ed 100644 --- a/report_custom_filename/__openerp__.py +++ b/report_custom_filename/__openerp__.py @@ -36,7 +36,7 @@ "test": [ ], "auto_install": False, - "installable": True, + 'installable': False, "application": False, "external_dependencies": { 'python': ['jinja2'], diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index f7204248c..4148c032c 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -77,6 +77,6 @@ Excel reports in odoo. 'depends': ['base'], 'external_dependencies': {'python': ['xlwt']}, 'active': False, - 'installable': True, + 'installable': False, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From c06464a646e04a158b3c892a08a82bde61c7a607 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Wed, 14 Oct 2015 02:33:58 +0200 Subject: [PATCH 33/69] [MIG] Update metafiles --- .travis.yml | 6 +++--- README.md | 17 ++--------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 867e15e13..ab42259e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,9 @@ python: - "2.7" env: - - VERSION="8.0" LINT_CHECK="1" - - VERSION="8.0" ODOO_REPO="odoo/odoo" LINT_CHECK="0" - - VERSION="8.0" ODOO_REPO="OCA/OCB" LINT_CHECK="0" + - VERSION="9.0" LINT_CHECK="1" + - VERSION="9.0" ODOO_REPO="odoo/odoo" LINT_CHECK="0" + - VERSION="9.0" ODOO_REPO="OCA/OCB" LINT_CHECK="0" virtualenv: system_site_packages: true diff --git a/README.md b/README.md index 384dad989..4928bc0c2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Build Status](https://travis-ci.org/OCA/reporting-engine.svg?branch=8.0)](https://travis-ci.org/OCA/reporting-engine) -[![Coverage Status](https://img.shields.io/coveralls/OCA/reporting-engine.svg)](https://coveralls.io/r/OCA/reporting-engine?branch=8.0) +[![Build Status](https://travis-ci.org/OCA/reporting-engine.svg?branch=9.0)](https://travis-ci.org/OCA/reporting-engine) +[![Coverage Status](https://img.shields.io/coveralls/OCA/reporting-engine.svg)](https://coveralls.io/r/OCA/reporting-engine?branch=9.0) OCA alternative reporting engines and reporting utilities for Odoo ================================================================== @@ -16,19 +16,6 @@ The convention is to use a suffix to each module to indicate that it's for using It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). [//]: # (addons) -Available addons ----------------- -addon | version | summary ---- | --- | --- -[report_custom_filename](report_custom_filename/) | 8.0.1.0.0 | Configure the filename to use when downloading a report -[report_xls](report_xls/) | 8.0.0.6.0 | Excel report engine - -Unported addons ---------------- -addon | version | summary ---- | --- | --- -[base_report_assembler](base_report_assembler/) | 1.0 (unported) | Base Report Assembler - [//]: # (end addons) ---- From 92509d8cdc6e976db1a868b92ed7f2a22b6e3c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Wed, 14 Oct 2015 09:57:38 +0200 Subject: [PATCH 34/69] [UPD] addons table in README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 4928bc0c2..d5614e937 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,14 @@ The convention is to use a suffix to each module to indicate that it's for using It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). [//]: # (addons) +Unported addons +--------------- +addon | version | summary +--- | --- | --- +[base_report_assembler](base_report_assembler/) | 1.0 (unported) | Base Report Assembler +[report_custom_filename](report_custom_filename/) | 8.0.1.0.0 (unported) | Configure the filename to use when downloading a report +[report_xls](report_xls/) | 8.0.0.6.0 (unported) | Excel report engine + [//]: # (end addons) ---- From 8d3d5d0459302706a629fd24f350b5131db83dfd Mon Sep 17 00:00:00 2001 From: Nicola Malcontenti Date: Tue, 27 Oct 2015 14:53:06 +0100 Subject: [PATCH 35/69] [ADD] Add Module report_qweb_element_page_visibility --- README.rst | 77 ++++++++++++++++++++++++++++++++++++ __init__.py | 20 ++++++++++ __openerp__.py | 37 +++++++++++++++++ static/description/icon.png | Bin 0 -> 17479 bytes views/layouts.xml | 48 ++++++++++++++++++++++ 5 files changed, 182 insertions(+) create mode 100644 README.rst create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 static/description/icon.png create mode 100644 views/layouts.xml diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..4d8282daa --- /dev/null +++ b/README.rst @@ -0,0 +1,77 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=================================== +Report Qweb Element Page Visibility +=================================== + +This module allows you to use 4 classes in QWEB reports: + +- not-first-page: shows element in every page but first +- not-last-page: shows element in every page but last +- first-page: shows element only on first page +- last-page: shows element only on last page + +Usage +===== + +To use this module, you need to: + +In the QWEB ``ir.ui.views`` used by your report, +you can add an element with css class with any of the classes described above. +For example if you need to improve invoice report header with +invoice's number in every page but first, and sale order report header +with order's name in every page but last, add this code to external_layout_header:: + + +
+ +
+
+ +
+ +
+
+ +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/143/8.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `here `_. + + +Credits +======= + +Contributors +------------ + +* Nicola Malcontenti +* Lorenzo Battistini +* Alessio Gerace +* Alex Comba + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/__init__.py b/__init__.py new file mode 100644 index 000000000..c3e52dcb1 --- /dev/null +++ b/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +######################################################################### +# # +# Copyright (C) 2015 Agile Business Group # +# # +# 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 Licensefor more details. # +# # +# You should have received a copy of the # +# GNU Affero General Public License # +# along with this program. If not, see . # +# # +######################################################################### diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 000000000..b2316761c --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +######################################################################### +# # +# Copyright (C) 2015 Agile Business Group # +# # +# 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 Licensefor more details. # +# # +# You should have received a copy of the # +# GNU Affero General Public License # +# along with this program. If not, see . # +# # +######################################################################### + +{ + 'name': 'Report Qweb Element Page Visibility', + 'version': '8.0.1.0.0', + 'author': 'Agile Business Group, Odoo Community Association (OCA)', + 'category': 'Tools', + "website": "https://odoo-community.org/", + "license": "AGPL-3", + "application": False, + "installable": True, + 'data': [ + 'views/layouts.xml', + ], + 'depends': [ + 'report', + ], +} diff --git a/static/description/icon.png b/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4f946b5db1e82521a704e35b4cf8d9d28cf2d8a9 GIT binary patch literal 17479 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 + + + + + + + From 5cf7ea187205102fcfec8e97235a1e6379316593 Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Thu, 24 Dec 2015 15:11:52 +0100 Subject: [PATCH 36/69] [ADD] Add base_report_xlsx module --- base_report_xlsx/README.rst | 49 +++++++++++++++++++ base_report_xlsx/__init__.py | 5 ++ base_report_xlsx/__openerp__.py | 19 +++++++ base_report_xlsx/models/__init__.py | 5 ++ base_report_xlsx/models/ir_report.py | 16 ++++++ base_report_xlsx/report/__init__.py | 4 ++ base_report_xlsx/report/report_xlsx.py | 33 +++++++++++++ base_report_xlsx/static/description/icon.png | Bin 0 -> 9455 bytes 8 files changed, 131 insertions(+) create mode 100644 base_report_xlsx/README.rst create mode 100644 base_report_xlsx/__init__.py create mode 100644 base_report_xlsx/__openerp__.py create mode 100644 base_report_xlsx/models/__init__.py create mode 100644 base_report_xlsx/models/ir_report.py create mode 100644 base_report_xlsx/report/__init__.py create mode 100644 base_report_xlsx/report/report_xlsx.py create mode 100644 base_report_xlsx/static/description/icon.png diff --git a/base_report_xlsx/README.rst b/base_report_xlsx/README.rst new file mode 100644 index 000000000..cf6d51a64 --- /dev/null +++ b/base_report_xlsx/README.rst @@ -0,0 +1,49 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================ +Base report xlsx +================ + +This module was written to provide a basic report class to generate xlsx report. + +Usage +===== + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/143/9.0 + +For further information, please visit: + + * https://www.odoo.com/forum/help-1 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 +`here `_. + +Credits +======= + +Contributors +------------ + +* Adrien Peiffer + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. \ No newline at end of file diff --git a/base_report_xlsx/__init__.py b/base_report_xlsx/__init__.py new file mode 100644 index 000000000..9a384baf1 --- /dev/null +++ b/base_report_xlsx/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models +from . import report diff --git a/base_report_xlsx/__openerp__.py b/base_report_xlsx/__openerp__.py new file mode 100644 index 000000000..f6576b3a5 --- /dev/null +++ b/base_report_xlsx/__openerp__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': "Base report xlsx", + + 'summary': """ + Base module to create xlsx report""", + 'author': 'ACSONE SA/NV,' + 'Odoo Community Association (OCA)', + 'website': "http://acsone.eu", + 'category': 'Reporting', + 'version': '8.0.1.0.0', + 'license': 'AGPL-3', + 'external_dependencies': {'python': ['xlsxwriter']}, + 'depends': [ + 'base', + ], +} diff --git a/base_report_xlsx/models/__init__.py b/base_report_xlsx/models/__init__.py new file mode 100644 index 000000000..521aa82b5 --- /dev/null +++ b/base_report_xlsx/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).+ + +from . import ir_report diff --git a/base_report_xlsx/models/ir_report.py b/base_report_xlsx/models/ir_report.py new file mode 100644 index 000000000..fad54f5a7 --- /dev/null +++ b/base_report_xlsx/models/ir_report.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, api + + +class IrActionsReportXml(models.Model): + _inherit = 'ir.actions.report.xml' + + @api.model + def _check_selection_field_value(self, field, value): + if field == 'report_type' and value == 'xlsx': + return + return super(IrActionsReportXml, self).\ + _check_selection_field_value(field, value) diff --git a/base_report_xlsx/report/__init__.py b/base_report_xlsx/report/__init__.py new file mode 100644 index 000000000..37c505120 --- /dev/null +++ b/base_report_xlsx/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import report_xlsx diff --git a/base_report_xlsx/report/report_xlsx.py b/base_report_xlsx/report/report_xlsx.py new file mode 100644 index 000000000..da5e9ffd1 --- /dev/null +++ b/base_report_xlsx/report/report_xlsx.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import xlsxwriter +from openerp.report.report_sxw import report_sxw +from openerp.api import Environment +from cStringIO import StringIO + + +class ReportXlsx(report_sxw): + + def create(self, cr, uid, ids, data, context=None): + self.env = Environment(cr, uid, context) + report_obj = self.env['ir.actions.report.xml'] + report = report_obj.search([('report_name', '=', self.name[7:])]) + if report.ids: + self.title = report.name + if report.report_type == 'xlsx': + objs = self.env[self.table].browse(ids) + return self.create_xlsx_report(data, objs) + return super(ReportXlsx, self).create(cr, uid, ids, data, context) + + def create_xlsx_report(self, data, objs): + file_data = StringIO() + workbook = xlsxwriter.Workbook(file_data) + self.generate_xlsx_report(workbook, data, objs) + workbook.close() + file_data.seek(0) + return (file_data.read(), 'xlsx') + + def generate_xlsx_report(self, workbook, data, objs): + raise NotImplementedError() diff --git a/base_report_xlsx/static/description/icon.png b/base_report_xlsx/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 From efdd19ffdc9ee51f7ba2cbaaf6e743a2693bc289 Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Mon, 4 Jan 2016 20:13:42 +0100 Subject: [PATCH 37/69] [IMP][base_report_xlsx] Readme file --- base_report_xlsx/README.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/base_report_xlsx/README.rst b/base_report_xlsx/README.rst index cf6d51a64..0763ceb44 100644 --- a/base_report_xlsx/README.rst +++ b/base_report_xlsx/README.rst @@ -6,7 +6,7 @@ Base report xlsx ================ -This module was written to provide a basic report class to generate xlsx report. +This module provides a basic report class to generate xlsx report. Usage ===== @@ -15,10 +15,6 @@ Usage :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/143/9.0 -For further information, please visit: - - * https://www.odoo.com/forum/help-1 - Bug Tracker =========== @@ -46,4 +42,4 @@ This module is maintained by the OCA. 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. -To contribute to this module, please visit http://odoo-community.org. \ No newline at end of file +To contribute to this module, please visit https://odoo-community.org. \ No newline at end of file From 5596dc46c5454805de51332fe7bc13c136b6f996 Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Mon, 4 Jan 2016 20:13:59 +0100 Subject: [PATCH 38/69] [IMP][base_report_xlsx] Protect import --- base_report_xlsx/report/report_xlsx.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/base_report_xlsx/report/report_xlsx.py b/base_report_xlsx/report/report_xlsx.py index da5e9ffd1..9b96fa7d2 100644 --- a/base_report_xlsx/report/report_xlsx.py +++ b/base_report_xlsx/report/report_xlsx.py @@ -2,11 +2,18 @@ # Copyright 2015 ACSONE SA/NV () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import xlsxwriter from openerp.report.report_sxw import report_sxw from openerp.api import Environment from cStringIO import StringIO +import logging +_logger = logging.getLogger(__name__) + +try: + import xlsxwriter +except ImportError: + _logger.debug('Can not import xlsxwriter`.') + class ReportXlsx(report_sxw): From e96135b145cfd5985274253edf380b6993a70fbe Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Mon, 4 Jan 2016 21:34:04 +0100 Subject: [PATCH 39/69] [IMP][base_report_xlsx] Add exemple with partner xlsx report --- base_report_xlsx/README.rst | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/base_report_xlsx/README.rst b/base_report_xlsx/README.rst index 0763ceb44..657f38eee 100644 --- a/base_report_xlsx/README.rst +++ b/base_report_xlsx/README.rst @@ -11,6 +11,38 @@ This module provides a basic report class to generate xlsx report. Usage ===== +An example of XLSX report for partners: + +A python class :: + + from openerp.addons.base_report_xlsx.report.report_xlsx import ReportXlsx + + class partner_xlsx(ReportXlsx): + + def generate_xlsx_report(self, workbook, data, partners): + for obj in partners: + report_name = obj.name + # One sheet by partner + sheet = workbook.add_worksheet(report_name[:31]) + bold = workbook.add_format({'bold': True}) + sheet.write(0, 0, obj.name, bold) + + + partner_xlsx('report.res.partner.xlsx', + 'res.partner') + +A report XML record :: + + + .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/143/9.0 @@ -21,7 +53,7 @@ Bug Tracker Bugs are tracked on `GitHub 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 -`here `_. +`here `_. Credits ======= From ebba6c6816f66b5760f3dbd4b5c0259e675cb703 Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Fri, 5 Feb 2016 20:49:23 +0100 Subject: [PATCH 40/69] [ADD] Add xlsxwriter in travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ab42259e3..96e5adf1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ virtualenv: install: - pip install xlwt + - pip install xlsxwriter - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly ${VERSION} From 32b4e8c63567090f8a2ab1d97e44238d69f0823b Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 15 Feb 2016 10:15:10 +0100 Subject: [PATCH 41/69] [SEC] report_xls: fix unsafe eval --- report_xls/__openerp__.py | 2 +- report_xls/report_xls.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/report_xls/__openerp__.py b/report_xls/__openerp__.py index 4148c032c..cddc757ea 100644 --- a/report_xls/__openerp__.py +++ b/report_xls/__openerp__.py @@ -21,7 +21,7 @@ ############################################################################## { 'name': 'Excel report engine', - 'version': '8.0.0.6.0', + 'version': '8.0.0.6.1', 'license': 'AGPL-3', 'author': "Noviat,Odoo Community Association (OCA)", 'website': 'http://www.noviat.com', diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py index 4746b4b91..c45e289f5 100644 --- a/report_xls/report_xls.py +++ b/report_xls/report_xls.py @@ -26,6 +26,7 @@ import cStringIO from datetime import datetime from openerp.osv.fields import datetime as datetime_field from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools.safe_eval import safe_eval import inspect from types import CodeType from openerp.report.report_sxw import report_sxw @@ -160,7 +161,7 @@ class report_xls(report_sxw): row = col_specs[wanted][rowtype][:] for i in range(len(row)): if isinstance(row[i], CodeType): - row[i] = eval(row[i], render_space) + row[i] = safe_eval(row[i], render_space) row.insert(0, wanted) return row From a1dc8533e859533bc1e1964b83e3b4c3e70c2783 Mon Sep 17 00:00:00 2001 From: OCA Git Bot Date: Thu, 18 Feb 2016 09:28:59 +0100 Subject: [PATCH 42/69] [UPD] addons table in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5614e937..a6c879f38 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ addon | version | summary --- | --- | --- [base_report_assembler](base_report_assembler/) | 1.0 (unported) | Base Report Assembler [report_custom_filename](report_custom_filename/) | 8.0.1.0.0 (unported) | Configure the filename to use when downloading a report -[report_xls](report_xls/) | 8.0.0.6.0 (unported) | Excel report engine +[report_xls](report_xls/) | 8.0.0.6.1 (unported) | Excel report engine [//]: # (end addons) From 2409e90ceb430f92a15f35dcaece54ba0e743330 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Thu, 18 Feb 2016 09:55:07 +0100 Subject: [PATCH 43/69] Revert "[SEC] report_xls: fix unsafe eval" This reverts commit 32b4e8c63567090f8a2ab1d97e44238d69f0823b. --- report_xls/report_xls.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/report_xls/report_xls.py b/report_xls/report_xls.py index c45e289f5..4746b4b91 100644 --- a/report_xls/report_xls.py +++ b/report_xls/report_xls.py @@ -26,7 +26,6 @@ import cStringIO from datetime import datetime from openerp.osv.fields import datetime as datetime_field from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools.safe_eval import safe_eval import inspect from types import CodeType from openerp.report.report_sxw import report_sxw @@ -161,7 +160,7 @@ class report_xls(report_sxw): row = col_specs[wanted][rowtype][:] for i in range(len(row)): if isinstance(row[i], CodeType): - row[i] = safe_eval(row[i], render_space) + row[i] = eval(row[i], render_space) row.insert(0, wanted) return row From 0285fc33ec4f1e1ef4005c85fe5b33f12a217afe Mon Sep 17 00:00:00 2001 From: "Maxime Chambreuil - http://www.savoirfairelinux.com" Date: Wed, 9 Mar 2016 09:43:33 -0500 Subject: [PATCH 44/69] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ab42259e3..f1b0578a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,4 +30,4 @@ script: - travis_run_tests ${VERSION} after_success: - coveralls + - coveralls From b2a9f7d431008943f46acc3fdd1b141f62530b5d Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Mon, 4 Apr 2016 20:28:23 +0200 Subject: [PATCH 45/69] [CHG] Rename base_report_xlsx to report_xlsx and add xlsx report type in selection --- base_report_xlsx/models/ir_report.py | 16 ---------------- {base_report_xlsx => report_xlsx}/README.rst | 4 ++-- {base_report_xlsx => report_xlsx}/__init__.py | 0 .../__openerp__.py | 0 .../models/__init__.py | 0 report_xlsx/models/ir_report.py | 11 +++++++++++ .../report/__init__.py | 0 .../report/report_xlsx.py | 0 .../static/description/icon.png | Bin 9 files changed, 13 insertions(+), 18 deletions(-) delete mode 100644 base_report_xlsx/models/ir_report.py rename {base_report_xlsx => report_xlsx}/README.rst (90%) rename {base_report_xlsx => report_xlsx}/__init__.py (100%) rename {base_report_xlsx => report_xlsx}/__openerp__.py (100%) rename {base_report_xlsx => report_xlsx}/models/__init__.py (100%) create mode 100644 report_xlsx/models/ir_report.py rename {base_report_xlsx => report_xlsx}/report/__init__.py (100%) rename {base_report_xlsx => report_xlsx}/report/report_xlsx.py (100%) rename {base_report_xlsx => report_xlsx}/static/description/icon.png (100%) diff --git a/base_report_xlsx/models/ir_report.py b/base_report_xlsx/models/ir_report.py deleted file mode 100644 index fad54f5a7..000000000 --- a/base_report_xlsx/models/ir_report.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2015 ACSONE SA/NV () -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp import models, api - - -class IrActionsReportXml(models.Model): - _inherit = 'ir.actions.report.xml' - - @api.model - def _check_selection_field_value(self, field, value): - if field == 'report_type' and value == 'xlsx': - return - return super(IrActionsReportXml, self).\ - _check_selection_field_value(field, value) diff --git a/base_report_xlsx/README.rst b/report_xlsx/README.rst similarity index 90% rename from base_report_xlsx/README.rst rename to report_xlsx/README.rst index 657f38eee..eb3cdb24e 100644 --- a/base_report_xlsx/README.rst +++ b/report_xlsx/README.rst @@ -15,7 +15,7 @@ An example of XLSX report for partners: A python class :: - from openerp.addons.base_report_xlsx.report.report_xlsx import ReportXlsx + from openerp.addons.report_xlsx.report.report_xlsx import ReportXlsx class partner_xlsx(ReportXlsx): @@ -53,7 +53,7 @@ Bug Tracker Bugs are tracked on `GitHub 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 -`here `_. +`here `_. Credits ======= diff --git a/base_report_xlsx/__init__.py b/report_xlsx/__init__.py similarity index 100% rename from base_report_xlsx/__init__.py rename to report_xlsx/__init__.py diff --git a/base_report_xlsx/__openerp__.py b/report_xlsx/__openerp__.py similarity index 100% rename from base_report_xlsx/__openerp__.py rename to report_xlsx/__openerp__.py diff --git a/base_report_xlsx/models/__init__.py b/report_xlsx/models/__init__.py similarity index 100% rename from base_report_xlsx/models/__init__.py rename to report_xlsx/models/__init__.py diff --git a/report_xlsx/models/ir_report.py b/report_xlsx/models/ir_report.py new file mode 100644 index 000000000..37928be73 --- /dev/null +++ b/report_xlsx/models/ir_report.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import api, fields, models + + +class IrActionsReportXml(models.Model): + _inherit = 'ir.actions.report.xml' + + report_type = fields.Selection(selection_add=[("xlsx", "xlsx")]) diff --git a/base_report_xlsx/report/__init__.py b/report_xlsx/report/__init__.py similarity index 100% rename from base_report_xlsx/report/__init__.py rename to report_xlsx/report/__init__.py diff --git a/base_report_xlsx/report/report_xlsx.py b/report_xlsx/report/report_xlsx.py similarity index 100% rename from base_report_xlsx/report/report_xlsx.py rename to report_xlsx/report/report_xlsx.py diff --git a/base_report_xlsx/static/description/icon.png b/report_xlsx/static/description/icon.png similarity index 100% rename from base_report_xlsx/static/description/icon.png rename to report_xlsx/static/description/icon.png From a132e61d51996ed02ac65df3313405a69cd73814 Mon Sep 17 00:00:00 2001 From: sebalix Date: Tue, 12 Apr 2016 15:28:46 +0200 Subject: [PATCH 46/69] [IMP] report_xlsx - Pass the report instance as well as ids of records to print to the 'create_xlsx_report' method + Instanciate the parser and make it available for the 'generate_xlsx_report' method --- report_xlsx/README.rst | 2 +- report_xlsx/report/report_xlsx.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/report_xlsx/README.rst b/report_xlsx/README.rst index eb3cdb24e..149e117a5 100644 --- a/report_xlsx/README.rst +++ b/report_xlsx/README.rst @@ -74,4 +74,4 @@ This module is maintained by the OCA. 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. -To contribute to this module, please visit https://odoo-community.org. \ No newline at end of file +To contribute to this module, please visit https://odoo-community.org. diff --git a/report_xlsx/report/report_xlsx.py b/report_xlsx/report/report_xlsx.py index 9b96fa7d2..601a7c7e3 100644 --- a/report_xlsx/report/report_xlsx.py +++ b/report_xlsx/report/report_xlsx.py @@ -24,11 +24,15 @@ class ReportXlsx(report_sxw): if report.ids: self.title = report.name if report.report_type == 'xlsx': - objs = self.env[self.table].browse(ids) - return self.create_xlsx_report(data, objs) + return self.create_xlsx_report(ids, data, report) return super(ReportXlsx, self).create(cr, uid, ids, data, context) - def create_xlsx_report(self, data, objs): + def create_xlsx_report(self, ids, data, report): + self.parser_instance = self.parser( + self.env.cr, self.env.uid, self.name2, self.env.context) + objs = self.getObjects( + self.env.cr, self.env.uid, ids, self.env.context) + self.parser_instance.set_context(objs, data, objs.ids, 'xlsx') file_data = StringIO() workbook = xlsxwriter.Workbook(file_data) self.generate_xlsx_report(workbook, data, objs) From fb606e1c66197e759ab57df4c1f3f75a992d2155 Mon Sep 17 00:00:00 2001 From: sebalix Date: Tue, 12 Apr 2016 15:48:25 +0200 Subject: [PATCH 47/69] [IMP] report_xlsx - README updated --- report_xlsx/README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/report_xlsx/README.rst b/report_xlsx/README.rst index 149e117a5..03821ebdc 100644 --- a/report_xlsx/README.rst +++ b/report_xlsx/README.rst @@ -8,6 +8,13 @@ Base report xlsx This module provides a basic report class to generate xlsx report. +Installation +============ + +Make sure you have ``xlsxwriter`` Python module installed:: + +$ pip install xlsxwriter + Usage ===== @@ -31,6 +38,9 @@ A python class :: partner_xlsx('report.res.partner.xlsx', 'res.partner') +To manipulate the ``workbook`` and ``sheet`` objects, refer to the +`documentation `_ of ``xlsxwriter``. + A report XML record :: Date: Tue, 12 Apr 2016 17:18:24 +0200 Subject: [PATCH 48/69] [FIX] report_xlsx - Fix lint --- report_xlsx/models/ir_report.py | 2 +- report_xlsx/report/report_xlsx.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/report_xlsx/models/ir_report.py b/report_xlsx/models/ir_report.py index 37928be73..e3793ae68 100644 --- a/report_xlsx/models/ir_report.py +++ b/report_xlsx/models/ir_report.py @@ -2,7 +2,7 @@ # Copyright 2015 ACSONE SA/NV () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models +from openerp import fields, models class IrActionsReportXml(models.Model): diff --git a/report_xlsx/report/report_xlsx.py b/report_xlsx/report/report_xlsx.py index 601a7c7e3..aab372b44 100644 --- a/report_xlsx/report/report_xlsx.py +++ b/report_xlsx/report/report_xlsx.py @@ -2,9 +2,10 @@ # Copyright 2015 ACSONE SA/NV () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from cStringIO import StringIO + from openerp.report.report_sxw import report_sxw from openerp.api import Environment -from cStringIO import StringIO import logging _logger = logging.getLogger(__name__) From fba586d55902f0eea54c30c38ca8a28e7e610052 Mon Sep 17 00:00:00 2001 From: sebalix Date: Wed, 13 Apr 2016 12:33:16 +0200 Subject: [PATCH 49/69] [ADD] requirements.txt added --- .travis.yml | 2 -- requirements.txt | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 requirements.txt diff --git a/.travis.yml b/.travis.yml index 96e5adf1a..2520fc1a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,8 +21,6 @@ virtualenv: system_site_packages: true install: - - pip install xlwt - - pip install xlsxwriter - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly ${VERSION} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..3ccfc8e79 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +xlwt +xlsxwriter From 36ff29ac3e6910f5d36410bd3c08dd1cc1880313 Mon Sep 17 00:00:00 2001 From: "Adrien Peiffer (ACSONE)" Date: Wed, 13 Apr 2016 12:56:09 +0200 Subject: [PATCH 50/69] [CHG][report_xlsx] Bump version --- report_xlsx/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report_xlsx/__openerp__.py b/report_xlsx/__openerp__.py index f6576b3a5..07985d16e 100644 --- a/report_xlsx/__openerp__.py +++ b/report_xlsx/__openerp__.py @@ -10,7 +10,7 @@ 'Odoo Community Association (OCA)', 'website': "http://acsone.eu", 'category': 'Reporting', - 'version': '8.0.1.0.0', + 'version': '9.0.1.0.0', 'license': 'AGPL-3', 'external_dependencies': {'python': ['xlsxwriter']}, 'depends': [ From 8be07e8d302dce174d5d9012e502249643637964 Mon Sep 17 00:00:00 2001 From: sebalix Date: Wed, 13 Apr 2016 16:05:14 +0200 Subject: [PATCH 51/69] [FIX] report_xlsx - Minor fix --- report_xlsx/report/report_xlsx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report_xlsx/report/report_xlsx.py b/report_xlsx/report/report_xlsx.py index aab372b44..285c97b52 100644 --- a/report_xlsx/report/report_xlsx.py +++ b/report_xlsx/report/report_xlsx.py @@ -33,7 +33,7 @@ class ReportXlsx(report_sxw): self.env.cr, self.env.uid, self.name2, self.env.context) objs = self.getObjects( self.env.cr, self.env.uid, ids, self.env.context) - self.parser_instance.set_context(objs, data, objs.ids, 'xlsx') + self.parser_instance.set_context(objs, data, ids, 'xlsx') file_data = StringIO() workbook = xlsxwriter.Workbook(file_data) self.generate_xlsx_report(workbook, data, objs) From 2bddfdb761a25923acb5ddf3392032275b9f3740 Mon Sep 17 00:00:00 2001 From: OCA Git Bot Date: Thu, 14 Apr 2016 04:46:14 +0200 Subject: [PATCH 52/69] [ADD] setup.py --- setup/.setuptools-odoo-make-default-ignore | 2 ++ setup/README | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 setup/.setuptools-odoo-make-default-ignore create mode 100644 setup/README diff --git a/setup/.setuptools-odoo-make-default-ignore b/setup/.setuptools-odoo-make-default-ignore new file mode 100644 index 000000000..207e61533 --- /dev/null +++ b/setup/.setuptools-odoo-make-default-ignore @@ -0,0 +1,2 @@ +# addons listed in this file are ignored by +# setuptools-odoo-make-default (one addon per line) diff --git a/setup/README b/setup/README new file mode 100644 index 000000000..a63d633e8 --- /dev/null +++ b/setup/README @@ -0,0 +1,2 @@ +To learn more about this directory, please visit +https://pypi.python.org/pypi/setuptools-odoo From 477bda73b0f66f814b7b265956595063560eb8a6 Mon Sep 17 00:00:00 2001 From: OCA Git Bot Date: Wed, 27 Apr 2016 02:41:28 +0200 Subject: [PATCH 53/69] [UPD] addons table in README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a6c879f38..6dde4af47 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ The convention is to use a suffix to each module to indicate that it's for using It can contain also another utilities directly involved with reports (like merge/split utils, checkers, signing tools and so on). [//]: # (addons) +Available addons +---------------- +addon | version | summary +--- | --- | --- +[report_xlsx](report_xlsx/) | 9.0.1.0.0 | Base module to create xlsx report + Unported addons --------------- addon | version | summary From da1e961a2787c3672131d881e8792bfb9a6a2ec4 Mon Sep 17 00:00:00 2001 From: OCA Git Bot Date: Sat, 30 Apr 2016 04:39:35 +0200 Subject: [PATCH 54/69] [ADD] setup.py --- setup/report_xlsx/odoo_addons/__init__.py | 1 + setup/report_xlsx/odoo_addons/report_xlsx | 1 + setup/report_xlsx/setup.py | 6 ++++++ 3 files changed, 8 insertions(+) create mode 100644 setup/report_xlsx/odoo_addons/__init__.py create mode 120000 setup/report_xlsx/odoo_addons/report_xlsx create mode 100644 setup/report_xlsx/setup.py diff --git a/setup/report_xlsx/odoo_addons/__init__.py b/setup/report_xlsx/odoo_addons/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/setup/report_xlsx/odoo_addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/report_xlsx/odoo_addons/report_xlsx b/setup/report_xlsx/odoo_addons/report_xlsx new file mode 120000 index 000000000..6ef6301e1 --- /dev/null +++ b/setup/report_xlsx/odoo_addons/report_xlsx @@ -0,0 +1 @@ +../../../report_xlsx \ No newline at end of file diff --git a/setup/report_xlsx/setup.py b/setup/report_xlsx/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/report_xlsx/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From c4cfb8eb000577bbdf4c4b2d179fdf10f9235999 Mon Sep 17 00:00:00 2001 From: David Beal Date: Tue, 3 May 2016 08:51:32 +0200 Subject: [PATCH 55/69] Update README in report_xlsx --- report_xlsx/README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/report_xlsx/README.rst b/report_xlsx/README.rst index 03821ebdc..2ce2e1123 100644 --- a/report_xlsx/README.rst +++ b/report_xlsx/README.rst @@ -24,7 +24,7 @@ A python class :: from openerp.addons.report_xlsx.report.report_xlsx import ReportXlsx - class partner_xlsx(ReportXlsx): + class PartnerXlsx(ReportXlsx): def generate_xlsx_report(self, workbook, data, partners): for obj in partners: @@ -35,8 +35,8 @@ A python class :: sheet.write(0, 0, obj.name, bold) - partner_xlsx('report.res.partner.xlsx', - 'res.partner') + PartnerXlsx('report.res.partner.xlsx', + 'res.partner') To manipulate the ``workbook`` and ``sheet`` objects, refer to the `documentation `_ of ``xlsxwriter``. From e411f0c4069fe5c364b3f9ceb70852cc43648a16 Mon Sep 17 00:00:00 2001 From: Sebastien LANGE Date: Wed, 1 Jun 2016 08:04:43 +0200 Subject: [PATCH 56/69] [IMP] Migrate report_qweb_element_page_visibility module to 9.0 --- report_qweb_element_page_visibility/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report_qweb_element_page_visibility/__openerp__.py b/report_qweb_element_page_visibility/__openerp__.py index b2316761c..fcbbf3e4f 100644 --- a/report_qweb_element_page_visibility/__openerp__.py +++ b/report_qweb_element_page_visibility/__openerp__.py @@ -21,7 +21,7 @@ { 'name': 'Report Qweb Element Page Visibility', - 'version': '8.0.1.0.0', + 'version': '9.0.1.0.0', 'author': 'Agile Business Group, Odoo Community Association (OCA)', 'category': 'Tools', "website": "https://odoo-community.org/", From 6efdf5878b6a844e8231fc9fb1741c4826e298c5 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sat, 13 Aug 2016 03:03:18 +0200 Subject: [PATCH 57/69] [IMP] README.md: Runbot flag --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6dde4af47..916dc68c8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Runbot Status](https://runbot.odoo-community.org/runbot/badge/flat/143/9.0.svg)](https://runbot.odoo-community.org/runbot/repo/github-com-oca-reporting-engine-143) [![Build Status](https://travis-ci.org/OCA/reporting-engine.svg?branch=9.0)](https://travis-ci.org/OCA/reporting-engine) [![Coverage Status](https://img.shields.io/coveralls/OCA/reporting-engine.svg)](https://coveralls.io/r/OCA/reporting-engine?branch=9.0) From 41882ce0dd8c2f91564536eea4b4da1442944be4 Mon Sep 17 00:00:00 2001 From: Sebastien LANGE Date: Fri, 2 Sep 2016 20:40:26 +0200 Subject: [PATCH 58/69] [IMP] Change licence header in python files --- .../__init__.py | 21 ++----------------- .../__openerp__.py | 21 ++----------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/report_qweb_element_page_visibility/__init__.py b/report_qweb_element_page_visibility/__init__.py index c3e52dcb1..ad15c9c0c 100644 --- a/report_qweb_element_page_visibility/__init__.py +++ b/report_qweb_element_page_visibility/__init__.py @@ -1,20 +1,3 @@ # -*- coding: utf-8 -*- -######################################################################### -# # -# Copyright (C) 2015 Agile Business Group # -# # -# 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 Licensefor more details. # -# # -# You should have received a copy of the # -# GNU Affero General Public License # -# along with this program. If not, see . # -# # -######################################################################### +# Copyright 2015 Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/report_qweb_element_page_visibility/__openerp__.py b/report_qweb_element_page_visibility/__openerp__.py index fcbbf3e4f..af192ca00 100644 --- a/report_qweb_element_page_visibility/__openerp__.py +++ b/report_qweb_element_page_visibility/__openerp__.py @@ -1,23 +1,6 @@ # -*- coding: utf-8 -*- -######################################################################### -# # -# Copyright (C) 2015 Agile Business Group # -# # -# 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 Licensefor more details. # -# # -# You should have received a copy of the # -# GNU Affero General Public License # -# along with this program. If not, see . # -# # -######################################################################### +# Copyright 2015 Agile Business Group +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Report Qweb Element Page Visibility', From 56c8802085fed05fc567706a5eeee96e5718dee3 Mon Sep 17 00:00:00 2001 From: Sebastien LANGE Date: Fri, 2 Sep 2016 20:42:02 +0200 Subject: [PATCH 59/69] [IMP] Replace by and review indentation in xml file --- .../views/layouts.xml | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/report_qweb_element_page_visibility/views/layouts.xml b/report_qweb_element_page_visibility/views/layouts.xml index a216cccc6..1cde96c39 100644 --- a/report_qweb_element_page_visibility/views/layouts.xml +++ b/report_qweb_element_page_visibility/views/layouts.xml @@ -1,48 +1,46 @@ - - + - + + + + - - + From fbed7cef582083c3f07406f7ea4bd9d623c10f0b Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 6 Sep 2016 09:55:50 +0200 Subject: [PATCH 60/69] [IMP] .travis.yml: Coverage+Transifex activation --- .travis.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 80d27af7f..87c41ec56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,20 +13,26 @@ python: - "2.7" env: - - VERSION="9.0" LINT_CHECK="1" - - VERSION="9.0" ODOO_REPO="odoo/odoo" LINT_CHECK="0" - - VERSION="9.0" ODOO_REPO="OCA/OCB" LINT_CHECK="0" + global: + - VERSION="8.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" + - TRANSIFEX_USER='transbot@odoo-community.org' + - secure: NUsXwVrMntcqge1ozKW+DSkP7dq+Rla6JVvFF2c89/g+zJaIqQRi8EQBLoqNwCdMk+rjpQeZt/JPELjH+EzPcmGddhDxOgVB3nUT9LvFXGCHF+NjmHXqyba4tuc7BnpG1WDD+rSlxVCt1aIjNIhhaZ4ic0rCWpKNYu/yFTsmChc= + matrix: + - LINT_CHECK="1" + - TRANSIFEX="1" + - TESTS="1" ODOO_REPO="odoo/odoo" + - TESTS="1" ODOO_REPO="OCA/OCB" virtualenv: system_site_packages: true install: - - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools + - git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - - travis_install_nightly ${VERSION} + - travis_install_nightly script: - - travis_run_tests ${VERSION} + - travis_run_tests after_success: - - coveralls + - travis_after_tests_success From abf3a75deffc51b638f46047fb145ef395ede917 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 6 Sep 2016 10:16:00 +0200 Subject: [PATCH 61/69] [FIX] .travis.yml: Correct version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 87c41ec56..75e8bc4c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ python: env: global: - - VERSION="8.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" + - VERSION="9.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" - TRANSIFEX_USER='transbot@odoo-community.org' - secure: NUsXwVrMntcqge1ozKW+DSkP7dq+Rla6JVvFF2c89/g+zJaIqQRi8EQBLoqNwCdMk+rjpQeZt/JPELjH+EzPcmGddhDxOgVB3nUT9LvFXGCHF+NjmHXqyba4tuc7BnpG1WDD+rSlxVCt1aIjNIhhaZ4ic0rCWpKNYu/yFTsmChc= matrix: From b8ca37b8d119a1d384ce8a1e15cd7ff1dc483084 Mon Sep 17 00:00:00 2001 From: OCA Git Bot Date: Wed, 7 Sep 2016 02:40:50 +0200 Subject: [PATCH 62/69] [UPD] addons table in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 916dc68c8..d57e0d228 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Available addons ---------------- addon | version | summary --- | --- | --- +[report_qweb_element_page_visibility](report_qweb_element_page_visibility/) | 9.0.1.0.0 | Report Qweb Element Page Visibility [report_xlsx](report_xlsx/) | 9.0.1.0.0 | Base module to create xlsx report Unported addons From 9bd180c2ed621170ce8cd1d2b83234da488f94c3 Mon Sep 17 00:00:00 2001 From: OCA Git Bot Date: Wed, 7 Sep 2016 04:42:40 +0200 Subject: [PATCH 63/69] [ADD] setup.py --- .../odoo_addons/__init__.py | 1 + .../odoo_addons/report_qweb_element_page_visibility | 1 + setup/report_qweb_element_page_visibility/setup.py | 6 ++++++ 3 files changed, 8 insertions(+) create mode 100644 setup/report_qweb_element_page_visibility/odoo_addons/__init__.py create mode 120000 setup/report_qweb_element_page_visibility/odoo_addons/report_qweb_element_page_visibility create mode 100644 setup/report_qweb_element_page_visibility/setup.py diff --git a/setup/report_qweb_element_page_visibility/odoo_addons/__init__.py b/setup/report_qweb_element_page_visibility/odoo_addons/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/setup/report_qweb_element_page_visibility/odoo_addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/report_qweb_element_page_visibility/odoo_addons/report_qweb_element_page_visibility b/setup/report_qweb_element_page_visibility/odoo_addons/report_qweb_element_page_visibility new file mode 120000 index 000000000..44b835bb8 --- /dev/null +++ b/setup/report_qweb_element_page_visibility/odoo_addons/report_qweb_element_page_visibility @@ -0,0 +1 @@ +../../../report_qweb_element_page_visibility \ No newline at end of file diff --git a/setup/report_qweb_element_page_visibility/setup.py b/setup/report_qweb_element_page_visibility/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/report_qweb_element_page_visibility/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From c1479c64d25efe4d2907a6c45b8f91a3a7393079 Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Mon, 3 Oct 2016 17:50:09 +0200 Subject: [PATCH 64/69] Move to report_py3o after import from HG --- LICENSE => report_py3o/LICENSE | 0 NEWS => report_py3o/NEWS | 0 README.md => report_py3o/README.md | 0 __init__.py => report_py3o/__init__.py | 0 __openerp__.py => report_py3o/__openerp__.py | 0 {data => report_py3o/data}/py3o.fusion.filetype.csv | 0 {i18n => report_py3o/i18n}/fr.po | 0 {models => report_py3o/models}/__init__.py | 0 {models => report_py3o/models}/ir_report.py | 0 {models => report_py3o/models}/py3o_fusion_filetype.py | 0 {models => report_py3o/models}/py3o_server.py | 0 {models => report_py3o/models}/py3o_template.py | 0 py3o_parser.py => report_py3o/py3o_parser.py | 0 py3o_report_modif.py => report_py3o/py3o_report_modif.py | 0 {security => report_py3o/security}/ir.model.access.csv | 0 {views => report_py3o/views}/ir_report.xml | 0 {views => report_py3o/views}/menu.xml | 0 {views => report_py3o/views}/py3o_server.xml | 0 {views => report_py3o/views}/py3o_template.xml | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename LICENSE => report_py3o/LICENSE (100%) rename NEWS => report_py3o/NEWS (100%) rename README.md => report_py3o/README.md (100%) rename __init__.py => report_py3o/__init__.py (100%) rename __openerp__.py => report_py3o/__openerp__.py (100%) rename {data => report_py3o/data}/py3o.fusion.filetype.csv (100%) rename {i18n => report_py3o/i18n}/fr.po (100%) rename {models => report_py3o/models}/__init__.py (100%) rename {models => report_py3o/models}/ir_report.py (100%) rename {models => report_py3o/models}/py3o_fusion_filetype.py (100%) rename {models => report_py3o/models}/py3o_server.py (100%) rename {models => report_py3o/models}/py3o_template.py (100%) rename py3o_parser.py => report_py3o/py3o_parser.py (100%) rename py3o_report_modif.py => report_py3o/py3o_report_modif.py (100%) rename {security => report_py3o/security}/ir.model.access.csv (100%) rename {views => report_py3o/views}/ir_report.xml (100%) rename {views => report_py3o/views}/menu.xml (100%) rename {views => report_py3o/views}/py3o_server.xml (100%) rename {views => report_py3o/views}/py3o_template.xml (100%) diff --git a/LICENSE b/report_py3o/LICENSE similarity index 100% rename from LICENSE rename to report_py3o/LICENSE diff --git a/NEWS b/report_py3o/NEWS similarity index 100% rename from NEWS rename to report_py3o/NEWS diff --git a/README.md b/report_py3o/README.md similarity index 100% rename from README.md rename to report_py3o/README.md diff --git a/__init__.py b/report_py3o/__init__.py similarity index 100% rename from __init__.py rename to report_py3o/__init__.py diff --git a/__openerp__.py b/report_py3o/__openerp__.py similarity index 100% rename from __openerp__.py rename to report_py3o/__openerp__.py diff --git a/data/py3o.fusion.filetype.csv b/report_py3o/data/py3o.fusion.filetype.csv similarity index 100% rename from data/py3o.fusion.filetype.csv rename to report_py3o/data/py3o.fusion.filetype.csv diff --git a/i18n/fr.po b/report_py3o/i18n/fr.po similarity index 100% rename from i18n/fr.po rename to report_py3o/i18n/fr.po diff --git a/models/__init__.py b/report_py3o/models/__init__.py similarity index 100% rename from models/__init__.py rename to report_py3o/models/__init__.py diff --git a/models/ir_report.py b/report_py3o/models/ir_report.py similarity index 100% rename from models/ir_report.py rename to report_py3o/models/ir_report.py diff --git a/models/py3o_fusion_filetype.py b/report_py3o/models/py3o_fusion_filetype.py similarity index 100% rename from models/py3o_fusion_filetype.py rename to report_py3o/models/py3o_fusion_filetype.py diff --git a/models/py3o_server.py b/report_py3o/models/py3o_server.py similarity index 100% rename from models/py3o_server.py rename to report_py3o/models/py3o_server.py diff --git a/models/py3o_template.py b/report_py3o/models/py3o_template.py similarity index 100% rename from models/py3o_template.py rename to report_py3o/models/py3o_template.py diff --git a/py3o_parser.py b/report_py3o/py3o_parser.py similarity index 100% rename from py3o_parser.py rename to report_py3o/py3o_parser.py diff --git a/py3o_report_modif.py b/report_py3o/py3o_report_modif.py similarity index 100% rename from py3o_report_modif.py rename to report_py3o/py3o_report_modif.py diff --git a/security/ir.model.access.csv b/report_py3o/security/ir.model.access.csv similarity index 100% rename from security/ir.model.access.csv rename to report_py3o/security/ir.model.access.csv diff --git a/views/ir_report.xml b/report_py3o/views/ir_report.xml similarity index 100% rename from views/ir_report.xml rename to report_py3o/views/ir_report.xml diff --git a/views/menu.xml b/report_py3o/views/menu.xml similarity index 100% rename from views/menu.xml rename to report_py3o/views/menu.xml diff --git a/views/py3o_server.xml b/report_py3o/views/py3o_server.xml similarity index 100% rename from views/py3o_server.xml rename to report_py3o/views/py3o_server.xml diff --git a/views/py3o_template.xml b/report_py3o/views/py3o_template.xml similarity index 100% rename from views/py3o_template.xml rename to report_py3o/views/py3o_template.xml From 0b4945849ebd17e5a05a25f9565648e96475eda5 Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Mon, 3 Oct 2016 17:55:59 +0200 Subject: [PATCH 65/69] remove hg files --- .hgignore | 24 ------------------------ .hgtags | 15 --------------- 2 files changed, 39 deletions(-) delete mode 100644 .hgignore delete mode 100644 .hgtags diff --git a/.hgignore b/.hgignore deleted file mode 100644 index ca621bc48..000000000 --- a/.hgignore +++ /dev/null @@ -1,24 +0,0 @@ -syntax: glob -**/*.pyc -*.pyc -*.pyo -*.swp -.tmp* -*~ -*.egg-info -dist/* -build/* -lib/* -output/* -*.orig -*.log -.settings/* -storage/* -.project -.idea -.pydevproject -*.db -.ropeproject/* -.mob - - diff --git a/.hgtags b/.hgtags deleted file mode 100644 index 41e72145d..000000000 --- a/.hgtags +++ /dev/null @@ -1,15 +0,0 @@ -ae59b92cb6bda5ada7fffd01d9006c800a6da9f4 TAG_1.0 -392758bf61eb33256bc3a46e16ca15d6195bc8a8 TAG_1.1 -b6c64ac6e39936d1ee13c58c14bf62b04c5b5202 TAG_1.2 -cd840bda3fa0d8cd4290700b299a8bad2c99606d TAG_1.2.1 -092018f8f6cab00d7198e0fdd2489e0d41c33f9f TAG_1.2.2 -2203f0ccc2e2d728c81080d37cba4559d705917e TAG_1.2.3 -d6fa2a1477bc1f7a3590eb425ab7f907779cb732 TAG_1.2.4 -067b603cc82e6403b66fedd1636f50ff104c3fee TAG_1.2.5 -ce6b34e125174856e86c108399dfbb0eb84a7418 TAG_1.3 -a9d3f37477695c6255e7f603fa47defcfbd3a2af TAG_1.3.1 -801e4bf837a28a824637570ec19bd6bc148c6837 TAG_2.0b1 -2e68df0df26d1dbcb28b30f8b626d8b237650751 TAG_2.0b2 -0118ec4a766ed2a279ac2d6caaf71e0c3db0023b TAG_8.0.2.0 -77b8df6148164972274ab8246a56e053357d61ca TAG_8.0.2.0 -65b3c62a20f1f7f06c36783bc3fa2cc52dd8a0b3 TAG_8.0.2.1 From b6383bbe9a8f63fd505404dc4dc776b8f9f1ef36 Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Mon, 3 Oct 2016 18:04:11 +0200 Subject: [PATCH 66/69] Add setup --- report_py3o/__openerp__.py | 2 +- setup/report_py3o/odoo_addons/__init__.py | 1 + setup/report_py3o/odoo_addons/report_py3o | 1 + setup/report_py3o/setup.py | 6 ++++++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 setup/report_py3o/odoo_addons/__init__.py create mode 120000 setup/report_py3o/odoo_addons/report_py3o create mode 100644 setup/report_py3o/setup.py diff --git a/report_py3o/__openerp__.py b/report_py3o/__openerp__.py index 328da8e60..cafbe598c 100644 --- a/report_py3o/__openerp__.py +++ b/report_py3o/__openerp__.py @@ -30,7 +30,7 @@ templates. The py3o.template package is required; install it with: pip install py3o.template ''', - 'version': '8.0.2.1', + 'version': '9.0.1.0.0', 'category': 'Reporting', 'author': 'XCG Consulting', 'website': 'http://odoo.consulting/', diff --git a/setup/report_py3o/odoo_addons/__init__.py b/setup/report_py3o/odoo_addons/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/setup/report_py3o/odoo_addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/report_py3o/odoo_addons/report_py3o b/setup/report_py3o/odoo_addons/report_py3o new file mode 120000 index 000000000..5e51a0c17 --- /dev/null +++ b/setup/report_py3o/odoo_addons/report_py3o @@ -0,0 +1 @@ +../../../report_py3o \ No newline at end of file diff --git a/setup/report_py3o/setup.py b/setup/report_py3o/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/report_py3o/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 4318e41acd117048d694b4f79dad35f77e4f8350 Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Mon, 3 Oct 2016 18:25:04 +0200 Subject: [PATCH 67/69] [FIX] View inheritance may not use attribute 'string' as a selector. --- report_py3o/views/ir_report.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report_py3o/views/ir_report.xml b/report_py3o/views/ir_report.xml index a5586aa4d..1fbb52c45 100644 --- a/report_py3o/views/ir_report.xml +++ b/report_py3o/views/ir_report.xml @@ -9,7 +9,7 @@ - + From 6dae7de2c76a6a0fb5c29b00642c3c1deb9ee621 Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Tue, 4 Oct 2016 08:23:12 +0200 Subject: [PATCH 68/69] [IMP] New API + Short Hearder --- report_py3o/__init__.py | 2 +- report_py3o/__openerp__.py | 24 +--- report_py3o/models/__init__.py | 8 +- report_py3o/models/ir_report.py | 77 +++++------ report_py3o/models/py3o_fusion_filetype.py | 19 +-- report_py3o/models/py3o_server.py | 22 +--- report_py3o/models/py3o_template.py | 39 +++--- report_py3o/py3o_parser.py | 8 +- report_py3o/py3o_report_modif.py | 146 --------------------- 9 files changed, 73 insertions(+), 272 deletions(-) delete mode 100644 report_py3o/py3o_report_modif.py diff --git a/report_py3o/__init__.py b/report_py3o/__init__.py index 2c4eac3f8..9a7e03ede 100644 --- a/report_py3o/__init__.py +++ b/report_py3o/__init__.py @@ -1 +1 @@ -import models \ No newline at end of file +from . import models \ No newline at end of file diff --git a/report_py3o/__openerp__.py b/report_py3o/__openerp__.py index cafbe598c..8a4d79906 100644 --- a/report_py3o/__openerp__.py +++ b/report_py3o/__openerp__.py @@ -1,26 +1,6 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# LibreOffice Report Engine, for OpenERP -# Copyright (C) 2013 XCG Consulting (http://odoo.consulting) -# -# 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 . -# -# Author: Anael LORIMIER -# Vincent Lhote-Hatakeyama -# -############################################################################## +# Copyright 2013 XCG Consulting (http://odoo.consulting) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'LibreOffice Report Engine', 'description': ''' diff --git a/report_py3o/models/__init__.py b/report_py3o/models/__init__.py index 28989d9f8..ccf621aaa 100644 --- a/report_py3o/models/__init__.py +++ b/report_py3o/models/__init__.py @@ -1,4 +1,4 @@ -import ir_report -import py3o_fusion_filetype -import py3o_template -import py3o_server \ No newline at end of file +from . import ir_report +from . import py3o_fusion_filetype +from . import py3o_template +from . import py3o_server \ No newline at end of file diff --git a/report_py3o/models/ir_report.py b/report_py3o/models/ir_report.py index 106bd332b..927994e3b 100644 --- a/report_py3o/models/ir_report.py +++ b/report_py3o/models/ir_report.py @@ -1,11 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2013 XCG Consulting (http://odoo.consulting) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import os -from openerp.osv import fields, osv +from openerp import api, fields, models from openerp.report.interface import report_int from ..py3o_parser import Py3oParser +from openerp.exceptions import ValidationError from openerp import addons -class report_xml(osv.Model): +class ReportXml(models.Model): """ Inherit from ir.actions.report.xml to allow customizing the template file. The user cam chose a template from a list. The list is configurable in the configuration tab, see py3o_template.py @@ -13,49 +17,32 @@ class report_xml(osv.Model): _inherit = 'ir.actions.report.xml' - _columns = { - # TODO required when report_type type is py3o, add python constraint - 'py3o_fusion_filetype': fields.many2one( - 'py3o.fusion.filetype', - u"Output Format", - ), - 'py3o_template_id': fields.many2one( - 'py3o.template', - u"Template", - ), - 'module': fields.char( - u"Module", - size=64, - help=u"The implementer module that provides this report", - ), - 'py3o_template_fallback': fields.char( - u"Fallback", - size=128, - help=( - u"If the user does not provide a template this will be used " - u"it should be a relative path to root of YOUR module" - ) - ), - 'report_type': fields.selection( - [ - ('qweb-pdf', u"PDF"), - ('qweb-html', u"HTML"), - ('controller', u"Controller"), - ('pdf', u"RML pdf (deprecated)"), - ('sxw', u"RML sxw (deprecated)"), - ('webkit', u"Webkit (deprecated)"), - ('py3o', u"Py3o"), - ], - string=u"Report Type", - required=True, - help=u"HTML will open the report directly in your browser, " - u"PDF will use wkhtmltopdf to render the HTML into a PDF " - u"file and let you download it, Controller allows you to " - u"define the url of a custom controller outputting " - u"any kind of report.", - ) - } + @api.one + @api.constrains("py3o_fusion_filetype", "report_type") + def _check_py3o_fusion_filetype(self): + if self.report_type == "py3o" and not self.py3o_fusion_filetype: + raise ValidationError( + "Field 'Output Format' is required for Py3O report") + py3o_fusion_filetype = fields.Many2one( + 'py3o.fusion.filetype', + "Output Format") + py3o_template_id = fields.Many2one( + 'py3o.template', + "Template") + module = fields.Char( + "Module", + help="The implementer module that provides this report") + py3o_template_fallback = fields.Char( + "Fallback", + size=128, + help=( + "If the user does not provide a template this will be used " + "it should be a relative path to root of YOUR module" + )) + report_type = fields.Selection(selection_add=[('py3o', "Py3o")]) + + @api.cr def _lookup_report(self, cr, name): """Look up a report definition. """ @@ -95,4 +82,4 @@ class report_xml(osv.Model): if new_report: return new_report else: - return super(report_xml, self)._lookup_report(cr, name) + return super(ReportXml, self)._lookup_report(cr, name) diff --git a/report_py3o/models/py3o_fusion_filetype.py b/report_py3o/models/py3o_fusion_filetype.py index 570ca4749..009494fcf 100644 --- a/report_py3o/models/py3o_fusion_filetype.py +++ b/report_py3o/models/py3o_fusion_filetype.py @@ -1,18 +1,13 @@ -from openerp.osv import fields, osv +# -*- coding: utf-8 -*- +# Copyright 2013 XCG Consulting (http://odoo.consulting) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp import fields, models -class py3o_fusion_filetype(osv.Model): +class Py3oFusionFiletype(models.Model): _name = 'py3o.fusion.filetype' _rec_name = 'human_ext' - _columns = { - 'fusion_ext': fields.char( - u"Fusion Extension", - size=8, - ), - 'human_ext': fields.char( - u"Human readble extension", - size=8, - ), - } + fusion_ext = fields.Char("Fusion Extension", siez=8) + human_ext = fields.Char("Human readble extension", size=8) diff --git a/report_py3o/models/py3o_server.py b/report_py3o/models/py3o_server.py index d815a845c..bf434a594 100644 --- a/report_py3o/models/py3o_server.py +++ b/report_py3o/models/py3o_server.py @@ -1,19 +1,11 @@ -from openerp.osv import fields, osv +# -*- coding: utf-8 -*- +# Copyright 2013 XCG Consulting (http://odoo.consulting) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp import fields, models -class py3o_server(osv.Model): +class Py3oServer(models.Model): _name = 'py3o.server' - _columns = { - 'url': fields.char( - u"URL", - size=256, - ), - 'is_active': fields.boolean( - u"Active", - ) - } - - _defaults = { - 'is_active': True, - } + url = fields.Char("URL", required=True) + is_active = fields.Boolean("Active", default=True) diff --git a/report_py3o/models/py3o_template.py b/report_py3o/models/py3o_template.py index 29b49dfd0..7281f3332 100644 --- a/report_py3o/models/py3o_template.py +++ b/report_py3o/models/py3o_template.py @@ -1,28 +1,19 @@ -from openerp.osv import fields, osv +# -*- coding: utf-8 -*- +# Copyright 2013 XCG Consulting (http://odoo.consulting) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp import fields, models -class py3o_template(osv.Model): +class Py3oTemplate(models.Model): _name = 'py3o.template' - _columns = { - 'name': fields.char( - u"Name", - ), - - 'py3o_template_data': fields.binary( - u"LibreOffice template", - ), - - 'filetype': fields.selection( - [ - ('odt', u"ODF Text Document"), - ('ods', u"ODF Spreadsheet"), - ], - u"LibreOffice Template File Type", - required=True, - ), - } - - _defaults = { - 'filetype': 'odt' - } + name = fields.Char(required=True) + py3o_template_data = fields.Binary("LibreOffice template") + filetype = fields.Selection( + selection=[ + ('odt', u"ODF Text Document"), + ('ods', u"ODF Spreadsheet"), + ], + string="LibreOffice Template File Type", + required=True, + default='odt') diff --git a/report_py3o/py3o_parser.py b/report_py3o/py3o_parser.py index d7467f956..36ed669df 100644 --- a/report_py3o/py3o_parser.py +++ b/report_py3o/py3o_parser.py @@ -1,4 +1,6 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- +# Copyright 2013 XCG Consulting (http://odoo.consulting) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from cStringIO import StringIO import json import pkg_resources @@ -66,8 +68,8 @@ class Py3oParser(report_sxw): or from the default template file provided by the implementer. ATM this method takes a report definition recordset - to try and fetch the report template from database. If not found it will - fallback to the template file referenced in the report definition. + to try and fetch the report template from database. If not found it + will fallback to the template file referenced in the report definition. @param report_obj: a recordset representing the report defintion @type report_obj: openerp.model.recordset instance diff --git a/report_py3o/py3o_report_modif.py b/report_py3o/py3o_report_modif.py deleted file mode 100644 index 8683ea256..000000000 --- a/report_py3o/py3o_report_modif.py +++ /dev/null @@ -1,146 +0,0 @@ -from base64 import b64decode -from tempfile import NamedTemporaryFile as tempfile - -from openerp import pooler -from openerp.report.report_sxw import * -from openerp.tools.translate import _ -from openerp.osv.osv import except_osv - -from py3o.template import Template - -from oe_json_serializer import OESerializer - -import json -import requests -import os - - -class py3o_report(report_sxw): -# def __init__(self, name, table): -# super(py3o_report, self).__init__(name, table) - - def get_values(self, cr, uid, ids, data, context): - ''' Override this function to customize the dictionary given to the - py3o.template renderer. ''' - - return { - 'lang': self.get_lang(cr, uid, context), - 'objects': self.getObjects(cr, uid, ids, context), - } - - def get_lang(self, cr, uid, context): - pool = pooler.get_pool(cr.dbname) - lang_obj = pool.get('res.lang') - user_obj = pool.get('res.users') - - lang_code = user_obj.browse(cr, uid, uid, context=context).lang - lang = lang_obj.search(cr, uid, - [('code', '=', lang_code)], - context=context)[0] - return lang_obj.browse(cr, uid, lang, context=context) - - def format_date(self, date, values): - ''' Return a date formatted according to the language extracted from - the "values" argument (which should be the result of get_values). ''' - return date.strftime(values['lang'].date_format) - - def create(self, cr, uid, ids, data, context=None): - # Find the report definition to get its settings. - pool = pooler.get_pool(cr.dbname) - report_xml_obj = pool.get('ir.actions.report.xml') - report_xml_ids = report_xml_obj.search(cr, uid, - [('report_name', '=', self.name[7:])], # Ignore "report." - context=context) - - if not report_xml_ids: - return super(py3o_report, self).create(cr, uid, ids, data, - context=context) - report_xml = report_xml_obj.browse(cr, uid, - report_xml_ids[0], - context=context) - - template = report_xml.py3o_template_id - filetype = report_xml.py3o_fusion_filetype - - - #Try to request fusion server: - - fusion_server_obj = pool['py3o.server'] - #TODO: Raise a message if no config found - fusion_server_id = fusion_server_obj.search( - cr, uid, [], context=context - )[0] - fusion_server = fusion_server_obj.browse(cr, uid, fusion_server_id) - - # py3o.template operates on filenames so create temporary files. - in_temp = tempfile(suffix='.odt', prefix='py3o-template-') - - in_temp.write(b64decode(template.py3o_template_data)) - in_temp.seek(0) - out_temp = tempfile(suffix='.odt', prefix='py3o-report-') - - # We need to get the variables used in the template - #TODO: Find a way to avoid calling Template - t = Template(in_temp.name, out_temp.name) - # Remove 'py3o.' - user_variable = [x[5:] for x in t.get_all_user_python_expression()] - print user_variable - - values = self.get_values(cr, uid, ids, data, context) - t.render(values) - print values - - #WARNING: We rely on the fact that there is a for loop on the report - # on objects (for object in objects) due to lack of time - val_dict = {} - for val in values: - if val == 'objects': - o = [] - for obj in values[val]: - x = OESerializer.serialize( - obj, - [ - v[len('object') + 1:] - for v in user_variable - if v.startswith('object') - ] - ) - o.append(x) - val_dict.update({val: o}) - continue - - x = OESerializer.serialize( - values[val], - [ - v[len(val) + 1:] - for v in user_variable - if v.startswith(val) - ] - ) - val_dict.update({val: x}) - - import pprint - pprint.pprint(val_dict) - val_json = json.dumps(val_dict) - - fields = { - 'targetformat': filetype.fusion_ext, - 'datadict': val_json, - 'image_mapping': '{}', - } - print fields - - r = requests.post( - fusion_server.url, data=fields, files={'tmpl_file': in_temp} - ) - in_temp.close() - if r.status_code == 400: - raise Exception("Problem with fusion server: %s" % r.json()) - - chunk_size = 1024 - - ext = filetype.human_ext - for chunk in r.iter_content(chunk_size): - out_temp.write(chunk) - out_temp.seek(0) - return out_temp.read(), ext From 5dc8bb3376c3d645fdd7256077421baae5b35ccc Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Tue, 4 Oct 2016 08:37:36 +0200 Subject: [PATCH 69/69] [FIX] Add depndency on py3o.template --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 3ccfc8e79..222148e9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ xlwt xlsxwriter +py3o.template