openobject-italia-core-devs team mailing list archive
-
openobject-italia-core-devs team
-
Mailing list archive
-
Message #01181
[Merge] lp:~icsergio/openobject-italia/remake-vat-registries into lp:openobject-italia/7.0
Sergio Corato has proposed merging lp:~icsergio/openobject-italia/remake-vat-registries into lp:openobject-italia/7.0.
Requested reviews:
OpenERP Italia core devs (openobject-italia-core-devs)
For more details, see:
https://code.launchpad.net/~icsergio/openobject-italia/remake-vat-registries/+merge/152598
Proposal to substitute l10n_it_vat_registries with vatregistries_webkit.
The latter one works correctly with this branch: https://code.launchpad.net/~icsergio/openobject-addons/trunk-improve-l10n_it-taxes , which adds some tax codes for completely configure the taxes.
The taxes' values and sums are taken as they are from database, without risky computations on the fly.
--
https://code.launchpad.net/~icsergio/openobject-italia/remake-vat-registries/+merge/152598
Your team OpenERP Italia core devs is requested to review the proposed merge of lp:~icsergio/openobject-italia/remake-vat-registries into lp:openobject-italia/7.0.
=== added directory 'vatregistries_webkit'
=== added file 'vatregistries_webkit/__init__.py'
--- vatregistries_webkit/__init__.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/__init__.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi. Copyright 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 <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+import account
+from . import wizard
+from . import report
=== added file 'vatregistries_webkit/__openerp__.py'
--- vatregistries_webkit/__openerp__.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/__openerp__.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,56 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Authors: Nicolas Bessi, Guewen Baconnier
+# Copyright Camptocamp SA 2011
+# (c) 2013 Sergio Corato
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+{
+ 'name': 'VAT Registries - Webkit',
+ 'description': """
+VAT Registries - Webkit
+==========================
+
+This module adds the following standard OpenERP financial reports:
+- VAT Journal
+
+HTML headers and footers are deactivated for these reports because of
+an issue in wkhtmltopdf
+(http://code.google.com/p/wkhtmltopdf/issues/detail?id=656) Instead,
+the header and footer are created as text with arguments passed to
+wkhtmltopdf. The texts are defined inside the report classes.
+""",
+ 'version': '1.0',
+ 'author': 'Sergio Corato',
+ 'license': 'AGPL-3',
+ 'category': 'Finance',
+ 'website': 'http://www.icstools.it',
+ 'images': [],
+ 'depends': ['account',
+ 'report_webkit'],
+ 'init_xml': [],
+ 'demo_xml' : [],
+ 'update_xml': ['data/financial_webkit_header.xml',
+ 'report/report.xml',
+ 'wizard/wizard.xml',
+ 'wizard/vatregistries_wizard_view.xml',
+ 'report_menus.xml',
+ ],
+ 'active': False,
+ 'installable': True,
+ 'application': True,
+}
=== added directory 'vatregistries_webkit/data'
=== added file 'vatregistries_webkit/data/financial_webkit_header.xml'
--- vatregistries_webkit/data/financial_webkit_header.xml 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/data/financial_webkit_header.xml 2013-03-10 21:38:23 +0000
@@ -0,0 +1,409 @@
+<?xml version="1.0" ?>
+<openerp>
+ <data noupdate="1">
+ <record id="financial_landscape_header" model="ir.header_webkit">
+ <field name="footer_html"><![CDATA[
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
+ <script>
+ function subst() {
+ var vars={};
+ var x=document.location.search.substring(1).split('&');
+ for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
+ var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
+ for(var i in x) {
+ var y = document.getElementsByClassName(x[i]);
+ for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
+ }
+ }
+ </script>
+ </head>
+ <% import datetime %>
+ <body style="border:0; margin: 0;" onload="subst()">
+ <table style="border-top: 1px solid black; width: 1080px">
+ <tr style="border-collapse:collapse;">
+ <td style="text-align:left;font-size:10;width:350px;">${formatLang( str(datetime.datetime.today()), date_time=True)}</td>
+ <td style="text-align:center;font-size:10;width:350px;">${user.name}</td>
+ <td style="text-align:right;font-size:10;width:350px;">Page <span class="page"/></td>
+ <td style="text-align:left;font-size:10;width:30px"> of <span class="topage"/></td>
+ </tr>
+ </table>
+ </body>
+</html>]]></field>
+ <field name="orientation">Landscape</field>
+ <field name="format">A4</field>
+ <field name="html"><![CDATA[
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
+ <script>
+ function subst() {
+ var vars={};
+ var x=document.location.search.substring(1).split('&');
+ for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
+ var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
+ for(var i in x) {
+ var y = document.getElementsByClassName(x[i]);
+ for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
+ }
+ }
+ </script>
+ <style type="text/css">
+ ${css}
+ </style>
+ </head>
+ <body style="border:0; margin: 0;" onload="subst()">
+ <table class="header" style="border-bottom: 0px solid black; width: 100%">
+ <tr>
+ <td style="text-align:left; font-size:11px; font-weight: bold;"><span style="text-transform:uppercase; font-size:12px;">${report_name}</span> - ${company.partner_id.name | entity} - ${company.currency_id.name | entity}</td>
+ </tr>
+ </table> ${_debug or ''|n} </body>
+</html>]]>
+ </field>
+ <field eval="0.0" name="margin_top"/>
+ <field name="css"><![CDATA[
+
+body, table, td, span, div {
+ font-family: Helvetica, Arial;
+}
+
+.act_as_table {
+ display: table;
+}
+.act_as_row {
+ display: table-row;
+}
+.act_as_cell {
+ display: table-cell;
+}
+.act_as_thead {
+ display: table-header-group;
+}
+.act_as_tbody {
+ display: table-row-group;
+}
+.act_as_tfoot {
+ display: table-footer-group;
+}
+.act_as_caption {
+ display: table-caption;
+}
+act_as_colgroup {
+ display: table-column-group;
+}
+
+.list_table, .data_table {
+ width: 1080px;
+ table-layout: fixed
+}
+
+.bg, .act_as_row.labels {
+ background-color:#F0F0F0;
+}
+
+.list_table, .data_table, .list_table .act_as_row {
+ border-left:0px;
+ border-right:0px;
+ text-align:left;
+ font-size:9px;
+ padding-right:3px;
+ padding-left:3px;
+ padding-top:2px;
+ padding-bottom:2px;
+ border-collapse:collapse;
+}
+
+.list_table .act_as_row.labels, .list_table .act_as_row.initial_balance, .list_table .act_as_row.lines {
+ border-color:gray;
+ border-bottom:1px solid lightGrey;
+}
+
+.data_table .act_as_cell {
+ border: 1px solid lightGrey;
+ text-align: center;
+}
+
+.data_table .act_as_cell, .list_table .act_as_cell {
+ word-wrap: break-word;
+}
+
+.data_table .act_as_row.labels {
+ font-weight: bold;
+}
+
+.initial_balance .act_as_cell {
+ font-style:italic;
+}
+
+.account_title {
+ font-size:10px;
+ font-weight:bold;
+ page-break-after: avoid;
+}
+
+.act_as_cell.amount {
+ word-wrap:normal;
+ text-align:right;
+}
+
+.list_table .act_as_cell{
+ padding-left: 5px;
+/* border-right:1px solid lightGrey; uncomment to active column lines */
+}
+.list_table .act_as_cell.first_column {
+ padding-left: 0px;
+/* border-left:1px solid lightGrey; uncomment to active column lines */
+}
+
+.sep_left {
+ border-left: 1px solid lightGrey;
+}
+
+.overflow_ellipsis {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.open_invoice_previous_line {
+ font-style: italic;
+}
+
+.clearance_line {
+ font-style: italic;
+}
+
+]]>
+ </field>
+ <field name="name">Financial Landscape Header</field>
+ </record>
+
+ <record id="financial_portrait_header" model="ir.header_webkit">
+ <field name="footer_html"><![CDATA[
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
+ <script>
+ function subst() {
+ var vars={};
+ var x=document.location.search.substring(1).split('&');
+ for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
+ var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
+ for(var i in x) {
+ var y = document.getElementsByClassName(x[i]);
+ for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
+ }
+ }
+ </script>
+ </head>
+ <% import datetime %>
+ <body style="border:0; margin: 0;" onload="subst()">
+ <table style="border-top: 1px solid black; width: 1080px">
+ <tr style="border-collapse:collapse;">
+ <td style="text-align:left;font-size:10;width:350px;">${formatLang( str(datetime.datetime.today()), date_time=True)}</td>
+ <td style="text-align:center;font-size:10;width:350px;">${user.name}</td>
+ <td style="text-align:right;font-size:10;width:350px;">Page <span class="page"/></td>
+ <td style="text-align:left;font-size:10;width:30px"> of <span class="topage"/></td>
+ </tr>
+ </table>
+ </body>
+</html>]]></field>
+ <field name="orientation">Portrait</field>
+ <field name="format">A4</field>
+ <field name="html"><![CDATA[
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
+ <script>
+ function subst() {
+ var vars={};
+ var x=document.location.search.substring(1).split('&');
+ for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
+ var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
+ for(var i in x) {
+ var y = document.getElementsByClassName(x[i]);
+ for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
+ }
+ }
+ </script>
+ <style type="text/css">
+ ${css}
+ </style>
+ </head>
+ <body style="border:0; margin: 0;" onload="subst()">
+ <table class="header" style="border-bottom: 0px solid black; width: 100%">
+ <tr>
+ <td style="text-align:left; font-size:11px; font-weight: bold;"><span style="text-transform:uppercase; font-size:12px;">${report_name}</span> - ${company.partner_id.name | entity} - ${company.currency_id.name | entity}</td>
+ </tr>
+ </table> ${_debug or ''|n} </body>
+</html>]]>
+ </field>
+ <field eval="17.0" name="margin_top"/>
+ <field eval="15.0" name="margin_bottom"/>
+ <field name="css"><![CDATA[
+
+body, table, td, span, div {
+ font-family: Helvetica, Arial;
+}
+
+.act_as_table {
+ display: table;
+}
+.act_as_row {
+ display: table-row;
+}
+.act_as_cell {
+ display: table-cell;
+}
+.act_as_thead {
+ display: table-header-group;
+}
+.act_as_tbody {
+ display: table-row-group;
+}
+.act_as_tfoot {
+ display: table-footer-group;
+}
+.act_as_caption {
+ display: table-caption;
+}
+act_as_colgroup {
+ display: table-column-group;
+}
+
+.list_table, .data_table {
+ width: 690px;
+ table-layout: fixed
+}
+
+.bg, .act_as_row.labels {
+ background-color:#F0F0F0;
+}
+
+.list_table, .data_table, .list_table .act_as_row {
+ border-left:0px;
+ border-right:0px;
+ text-align:left;
+ font-size:9px;
+ padding-right:3px;
+ padding-left:3px;
+ padding-top:2px;
+ padding-bottom:2px;
+ border-collapse:collapse;
+}
+
+.list_table .act_as_row.labels, .list_table .act_as_row.initial_balance, .list_table .act_as_row.lines {
+ border-color:gray;
+ border-bottom:1px solid lightGrey;
+}
+
+.data_table .act_as_cell {
+ border: 1px solid lightGrey;
+ text-align: center;
+}
+
+.data_table .act_as_cell, .list_table .act_as_cell {
+ word-wrap: break-word;
+}
+
+.data_table .act_as_row.labels {
+ font-weight: bold;
+}
+
+.initial_balance .act_as_cell {
+ font-style:italic;
+}
+
+.account_title {
+ font-size:10px;
+ font-weight:bold;
+ page-break-after: avoid;
+}
+
+.act_as_cell.amount {
+ word-wrap:normal;
+ text-align:right;
+}
+
+.list_table .act_as_cell{
+ padding-left: 5px;
+/* border-right:1px solid lightGrey; uncomment to active column lines */
+}
+.list_table .act_as_cell.first_column {
+ padding-left: 0px;
+/* border-left:1px solid lightGrey; uncomment to active column lines */
+}
+
+.sep_left {
+ border-left: 1px solid lightGrey;
+}
+
+.account_level_1 {
+ text-transform: uppercase;
+ /*font-weight: bold;*/
+ font-size: 15px;
+ background-color:#F0F0F0;
+}
+
+/*
+.account_level_1 .act_as_cell {
+ height: 30px;
+ vertical-align: bottom;
+}
+*/
+
+.account_level_2 {
+ /*text-transform: uppercase;
+ font-weight: bold;*/
+ font-size: 12px;
+ background-color:#F0F0F0;
+}
+
+/*
+.account_level_2 .act_as_cell {
+ height: 20px;
+ vertical-align: bottom;
+}
+
+.account_level_3 {
+ text-transform: uppercase;
+ font-weight: bold;
+ font-size: 11px;
+ background-color:#FAFAFA;
+}
+
+.account_level_4 {
+ font-weight: bold;
+ font-size: 11px;
+}
+*/
+
+.account_level_5 {
+
+}
+
+.regular_account_type {
+ font-weight: normal;
+}
+
+.view_account_type {
+ font-weight: bold;
+
+.account_level_consol {
+ font-weight: normal;
+ font-style: italic;
+}
+
+.overflow_ellipsis {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+]]>
+ </field>
+ <field name="name">Financial Portrait Header</field>
+ </record>
+ </data>
+</openerp>
=== added directory 'vatregistries_webkit/i18n'
=== added directory 'vatregistries_webkit/report'
=== added file 'vatregistries_webkit/report/__init__.py'
--- vatregistries_webkit/report/__init__.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report/__init__.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,3 @@
+from . import common_reports
+from . import webkit_parser_header_fix
+from . import vatregistries
=== added file 'vatregistries_webkit/report/common_reports.py'
--- vatregistries_webkit/report/common_reports.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report/common_reports.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,521 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi, Guewen Baconnier
+# Copyright Camptocamp SA 2011
+# SQL inspired from OpenERP original code
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+# TODO refactor helper in order to act more like mixin
+# By using properties we will have a more simple signature in fuctions
+
+import logging
+
+from openerp.osv import osv
+from openerp.tools.translate import _
+from openerp.addons.account.report.common_report_header import common_report_header
+
+_logger = logging.getLogger('financial.reports.webkit')
+
+
+class CommonReportHeaderWebkit(common_report_header):
+ """Define common helper for financial report"""
+
+ ####################From getter helper #####################################
+ def get_start_period_br(self, data):
+ return self._get_info(data, 'period_from', 'account.period')
+
+ def get_end_period_br(self, data):
+ return self._get_info(data, 'period_to', 'account.period')
+
+ def get_fiscalyear_br(self, data):
+ return self._get_info(data, 'fiscalyear_id', 'account.fiscalyear')
+
+ def _get_chart_account_id_br(self, data):
+ return self._get_info(data, 'chart_account_id', 'account.account')
+
+ def _get_accounts_br(self, data):
+ return self._get_info(data, 'account_ids', 'account.account')
+
+ def _get_info(self, data, field, model):
+ info = data.get('form', {}).get(field)
+ if info:
+ return self.pool.get(model).browse(self.cursor, self.uid, info)
+ return False
+
+ def _get_display_account(self, data):
+ val = self._get_form_param('display_account', data)
+ if val == 'bal_all':
+ return _('All accounts')
+ elif val == 'bal_mix':
+ return _('With transactions or non zero balance')
+ else:
+ return val
+
+ def _get_display_partner_account(self, data):
+ val = self._get_form_param('result_selection', data)
+ if val == 'customer':
+ return _('Receivable Accounts')
+ elif val == 'supplier':
+ return _('Payable Accounts')
+ elif val == 'customer_supplier':
+ return _('Receivable and Payable Accounts')
+ else:
+ return val
+
+ def _get_display_target_move(self, data):
+ val = self._get_form_param('target_move', data)
+ if val == 'posted':
+ return _('All Posted Entries')
+ elif val == 'all':
+ return _('All Entries')
+ else:
+ return val
+
+ def _get_display_account_raw(self, data):
+ return self._get_form_param('display_account', data)
+
+ def _get_filter(self, data):
+ return self._get_form_param('filter', data)
+
+ def _get_target_move(self, data):
+ return self._get_form_param('target_move', data)
+
+ def _get_target_invoice(self, data):
+ return self._get_form_param('target_invoice', data)
+
+ def _get_initial_balance(self, data):
+ return self._get_form_param('initial_balance', data)
+
+ def _get_amount_currency(self, data):
+ return self._get_form_param('amount_currency', data)
+
+ def _get_date_from(self, data):
+ return self._get_form_param('date_from', data)
+
+ def _get_date_to(self, data):
+ return self._get_form_param('date_to', data)
+
+ def _get_form_param(self, param, data, default=False):
+ return data.get('form', {}).get(param, default)
+
+ ####################Account and account line filter helper #################
+
+ def sort_accounts_with_structure(self, root_account_ids, account_ids, context=None):
+ """Sort accounts by code respecting their structure"""
+
+ def recursive_sort_by_code(accounts, parent):
+ sorted_accounts = []
+ # add all accounts with same parent
+ level_accounts = [account for account in accounts
+ if account['parent_id'] and account['parent_id'][0] == parent['id']]
+ # add consolidation children of parent, as they are logically on the same level
+ if parent.get('child_consol_ids'):
+ level_accounts.extend([account for account in accounts
+ if account['id'] in parent['child_consol_ids']])
+ # stop recursion if no children found
+ if not level_accounts:
+ return []
+
+ level_accounts = sorted(level_accounts, key=lambda a: a['code'])
+
+ for level_account in level_accounts:
+ sorted_accounts.append(level_account['id'])
+ sorted_accounts.extend(recursive_sort_by_code(accounts, parent=level_account))
+ return sorted_accounts
+
+ if not account_ids:
+ return []
+
+ accounts_data = self.pool.get('account.account').read(self.cr, self.uid,
+ account_ids,
+ ['id', 'parent_id', 'level', 'code', 'child_consol_ids'],
+ context=context)
+
+ sorted_accounts = []
+
+ root_accounts_data = [account_data for account_data in accounts_data
+ if account_data['id'] in root_account_ids]
+ for root_account_data in root_accounts_data:
+ sorted_accounts.append(root_account_data['id'])
+ sorted_accounts.extend(recursive_sort_by_code(accounts_data, root_account_data))
+
+ # fallback to unsorted accounts when sort failed
+ # sort fails when the levels are miscalculated by account.account
+ # check lp:783670
+ if len(sorted_accounts) != len(account_ids):
+ _logger.warn('Webkit financial reports: Sort of accounts failed.')
+ sorted_accounts = account_ids
+
+ return sorted_accounts
+
+ def get_all_accounts(self, account_ids, exclude_type=None, only_type=None, filter_report_type=None, context=None):
+ """Get all account passed in params with their childrens
+
+ @param exclude_type: list of types to exclude (view, receivable, payable, consolidation, other)
+ @param only_type: list of types to filter on (view, receivable, payable, consolidation, other)
+ @param filter_report_type: list of report type to filter on
+ """
+ context = context or {}
+ accounts = []
+ if not isinstance(account_ids, list):
+ account_ids = [account_ids]
+ acc_obj = self.pool.get('account.account')
+ for account_id in account_ids:
+ accounts.append(account_id)
+ accounts += acc_obj._get_children_and_consol(self.cursor, self.uid, account_id, context=context)
+ res_ids = list(set(accounts))
+ res_ids = self.sort_accounts_with_structure(account_ids, res_ids, context=context)
+
+ if exclude_type or only_type or filter_report_type:
+ sql_filters = {'ids': tuple(res_ids)}
+ sql_select = "SELECT a.id FROM account_account a"
+ sql_join = ""
+ sql_where = "WHERE a.id IN %(ids)s"
+ if exclude_type:
+ sql_where += " AND a.type not in %(exclude_type)s"
+ sql_filters.update({'exclude_type': tuple(exclude_type)})
+ if only_type:
+ sql_where += " AND a.type IN %(only_type)s"
+ sql_filters.update({'only_type': tuple(only_type)})
+ if filter_report_type:
+ sql_join += "INNER JOIN account_account_type t" \
+ " ON t.id = a.user_type"
+ sql_join += " AND t.report_type IN %(report_type)s"
+ sql_filters.update({'report_type': tuple(filter_report_type)})
+
+ sql = ' '.join((sql_select, sql_join, sql_where))
+ self.cursor.execute(sql, sql_filters)
+ fetch_only_ids = self.cursor.fetchall()
+ if not fetch_only_ids:
+ return []
+ only_ids = [only_id[0] for only_id in fetch_only_ids]
+ # keep sorting but filter ids
+ res_ids = [res_id for res_id in res_ids if res_id in only_ids]
+ return res_ids
+
+ ####################Periods and fiscal years helper #######################
+
+ def _get_opening_periods(self):
+ """Return the list of all journal that can be use to create opening entries
+ We actually filter on this instead of opening period as older version of OpenERP
+ did not have this notion"""
+ return self.pool.get('account.period').search(self.cursor, self.uid, [('special', '=', True)])
+
+ def exclude_opening_periods(self, period_ids):
+ period_obj = self.pool.get('account.period')
+ return period_obj.search(self.cr, self.uid, [['special', '=', False], ['id', 'in', period_ids]])
+
+ def get_included_opening_period(self, period):
+ """Return the opening included in normal period we use the assumption
+ that there is only one opening period per fiscal year"""
+ period_obj = self.pool.get('account.period')
+ return period_obj.search(self.cursor, self.uid,
+ [('special', '=', True),
+ ('date_start', '>=', period.date_start),
+ ('date_stop', '<=', period.date_stop)],
+ limit=1)
+
+ def periods_contains_move_lines(self, period_ids):
+ if not period_ids:
+ return False
+ mv_line_obj = self.pool.get('account.move.line')
+ if isinstance(period_ids, (int, long)):
+ period_ids = [period_ids]
+ return mv_line_obj.search(self.cursor, self.uid, [('period_id', 'in', period_ids)], limit=1) and True or False
+
+ def _get_period_range_from_periods(self, start_period, stop_period, mode=None):
+ """
+ Deprecated. We have to use now the build_ctx_periods of period_obj otherwise we'll have
+ inconsistencies, because build_ctx_periods does never filter on the the special
+ """
+ period_obj = self.pool.get('account.period')
+ search_period = [('date_start', '>=', start_period.date_start),
+ ('date_stop', '<=', stop_period.date_stop)]
+
+ if mode == 'exclude_opening':
+ search_period += [('special', '=', False)]
+ res = period_obj.search(self.cursor, self.uid, search_period)
+ return res
+
+ def _get_period_range_from_start_period(self, start_period, include_opening=False,
+ fiscalyear=False, stop_at_previous_opening=False):
+ """We retrieve all periods before start period"""
+ opening_period_id = False
+ past_limit = []
+ period_obj = self.pool.get('account.period')
+ mv_line_obj = self.pool.get('account.move.line')
+ # We look for previous opening period
+ if stop_at_previous_opening:
+ opening_search = [('special', '=', True),
+ ('date_stop', '<', start_period.date_start)]
+ if fiscalyear:
+ opening_search.append(('fiscalyear_id', '=', fiscalyear.id))
+
+ opening_periods = period_obj.search(self.cursor, self.uid, opening_search,
+ order='date_stop desc')
+ for opening_period in opening_periods:
+ validation_res = mv_line_obj.search(self.cursor,
+ self.uid,
+ [('period_id', '=', opening_period)],
+ limit=1)
+ if validation_res:
+ opening_period_id = opening_period
+ break
+ if opening_period_id:
+ #we also look for overlapping periods
+ opening_period_br = period_obj.browse(self.cursor, self.uid, opening_period_id)
+ past_limit = [('date_start', '>=', opening_period_br.date_stop)]
+
+ periods_search = [('date_stop', '<=', start_period.date_stop)]
+ periods_search += past_limit
+
+ if not include_opening:
+ periods_search += [('special', '=', False)]
+
+ if fiscalyear:
+ periods_search.append(('fiscalyear_id', '=', fiscalyear.id))
+ periods = period_obj.search(self.cursor, self.uid, periods_search)
+ if include_opening and opening_period_id:
+ periods.append(opening_period_id)
+ periods = list(set(periods))
+ if start_period.id in periods:
+ periods.remove(start_period.id)
+ return periods
+
+ def get_first_fiscalyear_period(self, fiscalyear):
+ return self._get_st_fiscalyear_period(fiscalyear)
+
+ def get_last_fiscalyear_period(self, fiscalyear):
+ return self._get_st_fiscalyear_period(fiscalyear, order='DESC')
+
+ def _get_st_fiscalyear_period(self, fiscalyear, special=False, order='ASC'):
+ period_obj = self.pool.get('account.period')
+ p_id = period_obj.search(self.cursor,
+ self.uid,
+ [('special', '=', special),
+ ('fiscalyear_id', '=', fiscalyear.id)],
+ limit=1,
+ order='date_start %s' % (order,))
+ if not p_id:
+ raise osv.except_osv(_('No period found'), '')
+ return period_obj.browse(self.cursor, self.uid, p_id[0])
+
+ ####################Initial Balance helper #################################
+
+ def _compute_init_balance(self, account_id=None, period_ids=None, mode='computed', default_values=False):
+ if not isinstance(period_ids, list):
+ period_ids = [period_ids]
+ res = {}
+
+ if not default_values:
+ if not account_id or not period_ids:
+ raise Exception('Missing account or period_ids')
+ try:
+ self.cursor.execute("SELECT sum(debit) AS debit, "
+ " sum(credit) AS credit, "
+ " sum(debit)-sum(credit) AS balance, "
+ " sum(amount_currency) AS curr_balance"
+ " FROM account_move_line"
+ " WHERE period_id in %s"
+ " AND account_id = %s", (tuple(period_ids), account_id))
+ res = self.cursor.dictfetchone()
+
+ except Exception, exc:
+ self.cursor.rollback()
+ raise
+
+ return {'debit': res.get('debit') or 0.0,
+ 'credit': res.get('credit') or 0.0,
+ 'init_balance': res.get('balance') or 0.0,
+ 'init_balance_currency': res.get('curr_balance') or 0.0,
+ 'state': mode}
+
+ def _read_opening_balance(self, account_ids, start_period):
+ """ Read opening balances from the opening balance
+ """
+ opening_period_selected = self.get_included_opening_period(start_period)
+ if not opening_period_selected:
+ raise osv.except_osv(
+ _('Error'),
+ _('No opening period found to compute the opening balances.\n'
+ 'You have to configure a period on the first of January'
+ ' with the special flag.'))
+
+ res = {}
+ for account_id in account_ids:
+ res[account_id] = self._compute_init_balance(account_id, opening_period_selected, mode='read')
+ return res
+
+ def _compute_initial_balances(self, account_ids, start_period, fiscalyear):
+ """We compute initial balance.
+ If form is filtered by date all initial balance are equal to 0
+ This function will sum pear and apple in currency amount if account as no secondary currency"""
+ # if opening period is included in start period we do not need to compute init balance
+ # we just read it from opening entries
+ res = {}
+ # PNL and Balance accounts are not computed the same way look for attached doc
+ # We include opening period in pnl account in order to see if opening entries
+ # were created by error on this account
+ pnl_periods_ids = self._get_period_range_from_start_period(start_period, fiscalyear=fiscalyear,
+ include_opening=True)
+ bs_period_ids = self._get_period_range_from_start_period(start_period, include_opening=True,
+ stop_at_previous_opening=True)
+ opening_period_selected = self.get_included_opening_period(start_period)
+
+ for acc in self.pool.get('account.account').browse(self.cursor, self.uid, account_ids):
+ res[acc.id] = self._compute_init_balance(default_values=True)
+ if acc.user_type.close_method == 'none':
+ # we compute the initial balance for close_method == none only when we print a GL
+ # during the year, when the opening period is not included in the period selection!
+ if pnl_periods_ids and not opening_period_selected:
+ res[acc.id] = self._compute_init_balance(acc.id, pnl_periods_ids)
+ else:
+ res[acc.id] = self._compute_init_balance(acc.id, bs_period_ids)
+ return res
+
+ ####################Account move retrieval helper ##########################
+ def _get_move_ids_from_periods(self, account_id, period_start, period_stop, target_move):
+ move_line_obj = self.pool.get('account.move.line')
+ period_obj = self.pool.get('account.period')
+ periods = period_obj.build_ctx_periods(self.cursor, self.uid, period_start.id, period_stop.id)
+ if not periods:
+ return []
+ search = [('period_id', 'in', periods), ('account_id', '=', account_id)]
+ if target_move == 'posted':
+ search += [('move_id.state', '=', 'posted')]
+ return move_line_obj.search(self.cursor, self.uid, search)
+
+ def _get_move_ids_from_dates(self, account_id, date_start, date_stop, target_move, mode='include_opening'):
+ # TODO imporve perfomance by setting opening period as a property
+ move_line_obj = self.pool.get('account.move.line')
+ search_period = [('date', '>=', date_start),
+ ('date', '<=', date_stop),
+ ('account_id', '=', account_id)]
+
+ # actually not used because OpenERP itself always include the opening when we
+ # get the periods from january to december
+ if mode == 'exclude_opening':
+ opening = self._get_opening_periods()
+ if opening:
+ search_period += ['period_id', 'not in', opening]
+
+ if target_move == 'posted':
+ search_period += [('move_id.state', '=', 'posted')]
+
+ return move_line_obj.search(self.cursor, self.uid, search_period)
+
+ def get_move_lines_ids(self, account_id, main_filter, start, stop, target_move, mode='include_opening'):
+ """Get account move lines base on form data"""
+ if mode not in ('include_opening', 'exclude_opening'):
+ raise osv.except_osv(_('Invalid query mode'), _('Must be in include_opening, exclude_opening'))
+
+ if main_filter in ('filter_period', 'filter_no'):
+ return self._get_move_ids_from_periods(account_id, start, stop, target_move)
+
+ elif main_filter == 'filter_date':
+ return self._get_move_ids_from_dates(account_id, start, stop, target_move)
+ else:
+ raise osv.except_osv(_('No valid filter'), _('Please set a valid time filter'))
+
+ def _get_move_line_datas(self, move_line_ids, order='per.special DESC, l.date ASC, per.date_start ASC, m.name ASC'):
+ if not move_line_ids:
+ return []
+ if not isinstance(move_line_ids, list):
+ move_line_ids = [move_line_ids]
+ monster = """
+SELECT l.id AS id,
+ l.date AS ldate,
+ j.code AS jcode ,
+ l.currency_id,
+ l.account_id,
+ l.amount_currency,
+ l.ref AS lref,
+ l.name AS lname,
+ COALESCE(l.debit, 0.0) - COALESCE(l.credit, 0.0) AS balance,
+ l.debit,
+ l.credit,
+ l.period_id AS lperiod_id,
+ per.code as period_code,
+ per.special AS peropen,
+ l.partner_id AS lpartner_id,
+ p.name AS partner_name,
+ m.name AS move_name,
+ COALESCE(partialrec.name, fullrec.name, '') AS rec_name,
+ m.id AS move_id,
+ c.name AS currency_code,
+ i.id AS invoice_id,
+ i.type AS invoice_type,
+ i.number AS invoice_number,
+ l.date_maturity
+FROM account_move_line l
+ JOIN account_move m on (l.move_id=m.id)
+ LEFT JOIN res_currency c on (l.currency_id=c.id)
+ LEFT JOIN account_move_reconcile partialrec on (l.reconcile_partial_id = partialrec.id)
+ LEFT JOIN account_move_reconcile fullrec on (l.reconcile_id = fullrec.id)
+ LEFT JOIN res_partner p on (l.partner_id=p.id)
+ LEFT JOIN account_invoice i on (m.id =i.move_id)
+ LEFT JOIN account_period per on (per.id=l.period_id)
+ JOIN account_journal j on (l.journal_id=j.id)
+ WHERE l.id in %s"""
+ monster += (" ORDER BY %s" % (order,))
+ try:
+ self.cursor.execute(monster, (tuple(move_line_ids),))
+ res = self.cursor.dictfetchall()
+ except Exception, exc:
+ self.cursor.rollback()
+ raise
+ return res or []
+
+ def _get_moves_counterparts(self, move_ids, account_id, limit=3):
+ if not move_ids:
+ return {}
+ if not isinstance(move_ids, list):
+ move_ids = [move_ids]
+ sql = """
+SELECT account_move.id,
+ array_to_string(
+ ARRAY(SELECT DISTINCT a.code
+ FROM account_move_line m2
+ LEFT JOIN account_account a ON (m2.account_id=a.id)
+ WHERE m2.move_id =account_move_line.move_id
+ AND m2.account_id<>%s limit %s) , ', ')
+
+FROM account_move
+ JOIN account_move_line on (account_move_line.move_id = account_move.id)
+ JOIN account_account on (account_move_line.account_id = account_account.id)
+WHERE move_id in %s"""
+
+ try:
+ self.cursor.execute(sql, (account_id, limit, tuple(move_ids)))
+ res = self.cursor.fetchall()
+ except Exception as exc:
+ self.cursor.rollback()
+ raise
+ return res and dict(res) or {}
+
+ def is_initial_balance_enabled(self, main_filter):
+ if main_filter not in ('filter_no', 'filter_year', 'filter_period'):
+ return False
+ return True
+
+ def _get_initial_balance_mode(self, start_period):
+ opening_period_selected = self.get_included_opening_period(start_period)
+ opening_move_lines = self.periods_contains_move_lines(opening_period_selected)
+ if opening_move_lines:
+ return 'opening_balance'
+ else:
+ return 'initial_balance'
=== added file 'vatregistries_webkit/report/report.xml'
--- vatregistries_webkit/report/report.xml 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report/report.xml 2013-03-10 21:38:23 +0000
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="account_report_vatregistries_webkit" model="ir.actions.report.xml">
+ <field name="report_type">webkit</field>
+ <field name="report_name">account.account_report_vatregistries_webkit</field>
+ <field eval="[(6,0,[])]" name="groups_id"/>
+ <field eval="0" name="multi"/>
+ <field eval="0" name="auto"/>
+ <field eval="1" name="header"/>
+ <field name="model">account.account</field>
+ <field name="type">ir.actions.report.xml</field>
+ <field name="name">VAT Registries Webkit</field>
+ <field name="report_rml">vatregistries_webkit/report/templates/account_report_vatregistries.mako</field>
+ <field name="report_file">vatregistries_webkit/report/templates/account_report_vatregistries.mako</field>
+ </record>
+
+ <record id="property_account_report_vatregistries_webkit" model="ir.property">
+ <field name="name">account_report_vatregistries_webkit</field>
+ <field name="fields_id" ref="report_webkit.field_ir_act_report_xml_webkit_header"/>
+ <field eval="'ir.header_webkit,'+str(ref('vatregistries_webkit.financial_portrait_header'))" model="ir.header_webkit" name="value"/>
+ <field eval="'ir.actions.report.xml,'+str(ref('vatregistries_webkit.account_report_vatregistries_webkit'))" model="ir.actions.report.xml" name="res_id"/>
+ </record>
+
+ </data>
+</openerp>
=== added directory 'vatregistries_webkit/report/templates'
=== added file 'vatregistries_webkit/report/templates/account_report_vatregistries.mako'
--- vatregistries_webkit/report/templates/account_report_vatregistries.mako 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report/templates/account_report_vatregistries.mako 2013-03-10 21:38:23 +0000
@@ -0,0 +1,120 @@
+<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";>
+ <head>
+ <style type="text/css">
+ ${css}
+
+ .list_table .act_as_row {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size:10px;
+ }
+
+ .account_line {
+ font-weight: bold;
+ font-size: 15px;
+ background-color:#F0F0F0;
+ }
+
+ .account_line .act_as_cell {
+ height: 30px;
+ vertical-align: bottom;
+ }
+
+ </style>
+ </head>
+ <body>
+ <%setLang(user.lang)%>
+
+<!--start header -->
+ <div class="account_title bg" style="margin-top: 20px; font-size: 12px; text-align: center; width: 95%;">${_('VAT Register')}</div>
+ <div class="act_as_table list_table" style="width: 95%;">
+ <div class="act_as_thead">
+ <div class="act_as_row labels">
+ <div class="act_as_cell " style="width: 30px;">${_('Fiscal Year')}</div>
+ <div class="act_as_cell " style="width: 70px;">${_('Journal')}</div>
+ <div class="act_as_cell " style="width: 70px;">${_('Journal Type')}</div>
+ <div class="act_as_cell " style="width: 30px;">${_('Period')}</div>
+ <!--div class="act_as_cell " style="width: 50px;">${_('Report')}</div-->
+ <div class="act_as_cell " style="width: 80px;">${_('Move Type')}</div>
+ </div>
+ </div>
+ <div class="act_as_tbody">
+ %for obj in objects:
+ <div class="act_as_row lines">
+ <div class="act_as_cell " style="width: 30px;">${ obj.fiscalyear_id.name}</div>
+ <div class="act_as_cell " style="width: 70px;">${ obj.journal_id.name}</div>
+ <div class="act_as_cell " style="width: 70px;">${obj.journal_id.type}</div>
+ <div class="act_as_cell " style="width: 30px;">${ obj.period_id.name}</div>
+ <!--div class="act_as_cell " style="width: 50px;">${ report_name or ''}</div-->
+ <div class="act_as_cell " style="width: 80px;">${ display_target_move(data) }</div>
+ </div>
+ %endfor
+ </div>
+ </div>
+<!--end header -->
+
+<!--start body -->
+ <div class="act_as_table list_table" style="width: 95%; margin-top: 20px;">
+ <div class="act_as_thead">
+ <div class="act_as_row labels">
+ <div class="act_as_cell first_row" style="width: 30px;">${_('Move Date')}</div>
+ <div class="act_as_cell first_row" style="width: 50px;">${_('Move Ref.')}</div>
+ <div class="act_as_cell first_row" style="width: 60px;">${_('Partner')}</div>
+ <div class="act_as_cell first_row" style="width: 30px;">${_('Taxable Amount')}</div>
+ <div class="act_as_cell first_row" style="width: 30px;">${_('Tax')}</div>
+ <div class="act_as_cell first_row" style="width: 80px;">${_('Tax Description')}</div>
+ </div>
+ </div>
+ <div class="act_as_tbody">
+
+%for obj in objects:
+%for line in lines(obj.period_id.id, obj.journal_id.id):
+%if line.tax_code_id.id:
+ <div class="act_as_row lines">
+ <div class="act_as_cell " style="width: 30px;">${formatLang(line.invoice_id.date_invoice, date=True)|entity}</div>
+ <div class="act_as_cell " style="width: 50px;">${ line.invoice_id.number}</div>
+ <div class="act_as_cell " style="width: 60px;">${ line.invoice_id.partner_id.name}</div>
+%if line.base_code_id.id:
+ <div class="act_as_cell amount" style="width: 30px;">${ line.base_amount *-1}</div>
+%else:
+ <div class="act_as_cell amount" style="width: 30px;"></div>
+%endif
+ <div class="act_as_cell amount" style="width: 30px;">${ line.tax_amount *-1}</div>
+ <div class="act_as_cell " style="width: 80px;">${ line.tax_code_id.name}</div>
+ </div>
+%endif
+%endfor
+%endfor
+
+ </div>
+ </div>
+<!--end body -->
+
+ <div class="act_as_table list_table" style="margin-left: 80px; margin-top: 20px; width: 50%;">
+ <div class="act_as_thead">
+ <div class="act_as_row labels">
+ <div class="act_as_cell first_row" style="width: 80px;">${_('Tax Code')}</div>
+ <div class="act_as_cell first_row" style="width: 20px;">${_('Base Amount')}</div>
+ <div class="act_as_cell first_row" style="width: 20px;">${_('Tax Amount')}</div>
+ </div>
+ </div>
+ <div class="act_as_tbody">
+%for obj in objects:
+%for obj_t in tax_total(obj.period_id.id, obj.journal_id.id):
+ <div class="act_as_row lines">
+ <div class="act_as_cell" style="width: 80px;">${ obj_t[0]}</div>
+%if not obj_t[4] is None:
+ <div class="act_as_cell amount" style="width: 20px;">${ obj_t[1] *-1}</div>
+%else:
+ <div class="act_as_cell amount" style="width: 20px;"></div>
+%endif
+ <div class="act_as_cell amount" style="width: 20px;">${ obj_t[2] *-1}</div>
+ </div>
+%endfor
+%endfor
+ </div>
+ </div>
+
+ </body>
+</html>
=== added file 'vatregistries_webkit/report/vatregistries.py'
--- vatregistries_webkit/report/vatregistries.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report/vatregistries.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,134 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Author: Guewen Baconnier
+# Copyright Camptocamp SA 2011
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+from datetime import datetime
+
+from openerp import pooler
+from openerp.report import report_sxw
+from openerp.tools.translate import _
+#from .common_partner_balance_reports import CommonPartnerBalanceReportHeaderWebkit
+from .common_reports import CommonReportHeaderWebkit
+from .webkit_parser_header_fix import HeaderFooterTextWebKitParser
+
+class vatregistries_webkit(report_sxw.rml_parse, CommonReportHeaderWebkit):
+
+ def __init__(self, cursor, uid, name, context):
+ super(vatregistries_webkit, self).__init__(cursor, uid, name, context=context)
+ self.pool = pooler.get_pool(self.cr.dbname)
+ self.cursor = self.cr
+ self.sort_selection = 'am.name'
+ #test self.target_invoice = data['form'].get('target_invoice','all')
+
+ company = self.pool.get('res.users').browse(self.cr, uid, uid, context=context).company_id
+ header_report_name = ' - '.join((_('SALE/PURCHASE JOURNAL'), company.name, company.currency_id.name))
+
+ footer_date_time = self.formatLang(str(datetime.today()), date_time=True)
+
+ self.localcontext.update({
+ 'cr': cursor,
+ 'uid': uid,
+ 'lines': self.tax_lines,
+ 'tax_total': self.tax_totals,
+ 'report_name': _('Sale/purchase journal'),
+ #'filter_form': self._get_filter,
+ 'target_invoice': self._get_target_invoice,
+ 'display_target_move': self._get_display_target_move,
+ 'additional_args': [
+ ('--header-font-name', 'Helvetica'),
+ ('--footer-font-name', 'Helvetica'),
+ ('--header-font-size', '10'),
+ ('--footer-font-size', '6'),
+ ('--header-left', header_report_name),
+ ('--header-spacing', '2'),
+ ('--footer-left', footer_date_time),
+ ('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
+ ('--footer-line',),
+ ],
+ })
+
+ def _get_target_invoice(self, data):
+ if data.get('form', False) and data['form'].get('target_invoice', False):
+ if data['form']['target_invoice'] == 'all':
+ return _('All Entries (includes draft)')
+ return _('All Posted Entries')
+ return ''
+
+ def set_context(self, objects, data, ids, report_type=None):
+ new_ids = ids
+ self.target_invoice = data['form'].get('target_invoice', 'all')
+ if (data['model'] == 'ir.ui.menu'):
+ self.period_ids = tuple(data['form']['periods'])
+ self.journal_ids = tuple(data['form']['journal_ids'])
+ new_ids = data['form'].get('active_ids', [])
+ objects = self.pool.get('account.journal.period').browse(self.cr, self.uid, new_ids)
+ elif new_ids:
+ #in case of direct access from account.journal.period object, we need to set the journal_ids and periods_ids
+ self.cr.execute('SELECT period_id, journal_id FROM account_journal_period WHERE id IN %s', (tuple(new_ids),))
+ res = self.cr.fetchall()
+ self.period_ids, self.journal_ids = zip(*res)
+ return super(vatregistries_webkit, self).set_context(objects, data, ids, report_type=report_type)
+
+ def tax_lines(self, period_id, journal_id=False):
+ if not journal_id:
+ journal_id = self.journal_ids
+ else:
+ journal_id = [journal_id]
+ if not period_id:
+ period_id = self.period_ids
+ else:
+ period_id = [period_id]
+
+ obj_tax_lines = self.pool.get('account.invoice.tax')
+
+ invoice_state = ['open','paid']
+ #da implementare se può essere utile avere una stampa con le fatture in bozza
+ #if self.target_invoice == 'all':
+ # invoice_state = ['open','paid','draft']
+ # ext = ' OR ai.period_id IS NULL'
+
+ self.cr.execute('SELECT ait.id FROM account_invoice_tax ait, account_invoice ai, account_tax_code atc WHERE (atc.id=ait.tax_code_id AND ai.id=ait.invoice_id AND ai.state IN %s AND ai.period_id IN %s AND ai.journal_id IN %s ) ORDER BY ai.number' , (tuple(invoice_state), tuple(period_id), tuple(journal_id) ))
+ ids = map(lambda x: x[0], self.cr.fetchall())
+ return obj_tax_lines.browse(self.cr, self.uid, ids)
+
+ def tax_totals(self, period_id, journal_id=False):
+
+ if not journal_id:
+ journal_id = self.journal_ids
+ else:
+ journal_id = [journal_id]
+ if not period_id:
+ period_id = self.period_ids
+ else:
+ period_id = [period_id]
+
+ obj_tax = self.pool.get('account.invoice.tax')
+ invoice_state = ['open','paid']
+
+ self.cr.execute('SELECT atc.name, SUM (ait.base_amount) AS base_totals, SUM(ait.tax_amount) AS tax_totals, ait.tax_code_id , ait.base_code_id FROM account_invoice_tax ait, account_invoice ai, account_tax_code atc WHERE atc.id=ait.tax_code_id AND ai.id=ait.invoice_id AND ai.state IN %s AND ai.period_id IN %s AND ai.journal_id IN %s GROUP BY atc.name, ait.base_code_id, ait.tax_code_id', (tuple(invoice_state), tuple(period_id), tuple(journal_id) ))
+ #ids = map(lambda x: x[0], self.cr.fetchall())
+ #return obj_mline_t.browse(self.cr, self.uid, ids)
+ return self.cr.fetchall()
+
+HeaderFooterTextWebKitParser('report.account.account_report_vatregistries_webkit',
+ 'account.account',
+ 'addons/vatregistries_webkit/report/templates/account_report_vatregistries.mako',
+ parser=vatregistries_webkit)
=== added file 'vatregistries_webkit/report/webkit_parser_header_fix.py'
--- vatregistries_webkit/report/webkit_parser_header_fix.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report/webkit_parser_header_fix.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,234 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)
+#
+# Author: Guewen Baconnier (Camptocamp)
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+import os
+import subprocess
+import tempfile
+import time
+import pooler
+import tools
+import logging
+
+
+from mako import exceptions
+from openerp.osv.osv import except_osv
+from openerp.tools.translate import _
+from openerp import addons
+from openerp.addons.report_webkit import webkit_report
+from openerp.addons.report_webkit.webkit_report import mako_template
+from openerp.addons.report_webkit.report_helper import WebKitHelper
+
+_logger = logging.getLogger('financial.reports.webkit')
+
+# Class used only as a workaround to bug:
+# http://code.google.com/p/wkhtmltopdf/issues/detail?id=656
+
+# html headers and footers do not work on big files (hundreds of pages) so we replace them by
+# text headers and footers passed as arguments to wkhtmltopdf
+# this class has to be removed once the bug is fixed
+
+# in your report class, to print headers and footers as text, you have to add them in the localcontext with a key 'additional_args'
+# for instance:
+# header_report_name = _('PARTNER LEDGER')
+# footer_date_time = self.formatLang(str(datetime.today()), date_time=True)
+# self.localcontext.update({
+# 'additional_args': [
+# ('--header-font-name', 'Helvetica'),
+# ('--footer-font-name', 'Helvetica'),
+# ('--header-font-size', '10'),
+# ('--footer-font-size', '7'),
+# ('--header-left', header_report_name),
+# ('--footer-left', footer_date_time),
+# ('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
+# ('--footer-line',),
+# ],
+# })
+
+
+class HeaderFooterTextWebKitParser(webkit_report.WebKitParser):
+
+ def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
+ """Call webkit in order to generate pdf"""
+ if not webkit_header:
+ webkit_header = report_xml.webkit_header
+ tmp_dir = tempfile.gettempdir()
+ out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.")
+ file_to_del = [out_filename]
+ if comm_path:
+ command = [comm_path]
+ else:
+ command = ['wkhtmltopdf']
+
+ command.append('--quiet')
+ # default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
+ command.extend(['--encoding', 'utf-8'])
+
+ if webkit_header.margin_top:
+ command.extend(['--margin-top', str(webkit_header.margin_top).replace(',', '.')])
+ if webkit_header.margin_bottom:
+ command.extend(['--margin-bottom', str(webkit_header.margin_bottom).replace(',', '.')])
+ if webkit_header.margin_left:
+ command.extend(['--margin-left', str(webkit_header.margin_left).replace(',', '.')])
+ if webkit_header.margin_right:
+ command.extend(['--margin-right', str(webkit_header.margin_right).replace(',', '.')])
+ if webkit_header.orientation:
+ command.extend(['--orientation', str(webkit_header.orientation).replace(',', '.')])
+ if webkit_header.format:
+ command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
+
+ if self.parser_instance.localcontext.get('additional_args', False):
+ for arg in self.parser_instance.localcontext['additional_args']:
+ command.extend(arg)
+
+ count = 0
+ for html in html_list:
+ html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
+ count += 1
+ html_file.write(html)
+ html_file.close()
+ file_to_del.append(html_file.name)
+ command.append(html_file.name)
+ command.append(out_filename)
+ stderr_fd, stderr_path = tempfile.mkstemp(text=True)
+ file_to_del.append(stderr_path)
+ try:
+ status = subprocess.call(command, stderr=stderr_fd)
+ os.close(stderr_fd) # ensure flush before reading
+ stderr_fd = None # avoid closing again in finally block
+ fobj = open(stderr_path, 'r')
+ error_message = fobj.read()
+ fobj.close()
+ if not error_message:
+ error_message = _('No diagnosis message was provided')
+ else:
+ error_message = _('The following diagnosis message was provided:\n') + error_message
+ if status:
+ raise except_osv(_('Webkit error' ),
+ _("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message))
+ pdf_file = open(out_filename, 'rb')
+ pdf = pdf_file.read()
+ pdf_file.close()
+ finally:
+ if stderr_fd is not None:
+ os.close(stderr_fd)
+ for f_to_del in file_to_del:
+ try:
+ os.unlink(f_to_del)
+ except (OSError, IOError), exc:
+ _logger.error('cannot remove file %s: %s', f_to_del, exc)
+ return pdf
+
+ # override needed to keep the attachments' storing procedure
+ def create_single_pdf(self, cursor, uid, ids, data, report_xml, context=None):
+ """generate the PDF"""
+
+ if context is None:
+ context={}
+ htmls = []
+ if report_xml.report_type != 'webkit':
+ return super(HeaderFooterTextWebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
+
+ self.parser_instance = self.parser(cursor,
+ uid,
+ self.name2,
+ context=context)
+
+ self.pool = pooler.get_pool(cursor.dbname)
+ objs = self.getObjects(cursor, uid, ids, context)
+ self.parser_instance.set_context(objs, data, ids, report_xml.report_type)
+
+ template = False
+
+ if report_xml.report_file:
+ path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep))
+ if os.path.exists(path):
+ template = file(path).read()
+ if not template and report_xml.report_webkit_data:
+ template = report_xml.report_webkit_data
+ if not template:
+ raise except_osv(_('Error!'), _('Webkit Report template not found !'))
+ header = report_xml.webkit_header.html
+ footer = report_xml.webkit_header.footer_html
+ if not header and report_xml.header:
+ raise except_osv(
+ _('No header defined for this Webkit report!'),
+ _('Please set a header in company settings')
+ )
+
+ css = report_xml.webkit_header.css
+ if not css:
+ css = ''
+ user = self.pool.get('res.users').browse(cursor, uid, uid)
+
+ #default_filters=['unicode', 'entity'] can be used to set global filter
+ body_mako_tpl = mako_template(template)
+ helper = WebKitHelper(cursor, uid, report_xml.id, context)
+ if report_xml.precise_mode:
+ for obj in objs:
+ self.parser_instance.localcontext['objects'] = [obj]
+ try:
+ html = body_mako_tpl.render(helper=helper,
+ css=css,
+ _=self.translate_call,
+ **self.parser_instance.localcontext)
+ htmls.append(html)
+ except Exception, e:
+ msg = exceptions.text_error_template().render()
+ _logger.error(msg)
+ raise except_osv(_('Webkit render'), msg)
+ else:
+ try:
+ html = body_mako_tpl.render(helper=helper,
+ css=css,
+ _=self.translate_call,
+ **self.parser_instance.localcontext)
+ htmls.append(html)
+ except Exception, e:
+ msg = exceptions.text_error_template().render()
+ _logger.error(msg)
+ raise except_osv(_('Webkit render'), msg)
+
+ # NO html footer and header because we write them as text with wkhtmltopdf
+ head = foot = False
+
+ if report_xml.webkit_debug:
+ try:
+ deb = body_mako_tpl.render(helper=helper,
+ css=css,
+ _debug=tools.ustr("\n".join(htmls)),
+ _=self.translate_call,
+ **self.parser_instance.localcontext)
+ except Exception, e:
+ msg = exceptions.text_error_template().render()
+ _logger.error(msg)
+ raise except_osv(_('Webkit render'), msg)
+ return (deb, 'html')
+ bin = self.get_lib(cursor, uid)
+ pdf = self.generate_pdf(bin, report_xml, head, foot, htmls)
+ return (pdf, 'pdf')
=== added file 'vatregistries_webkit/report_menus.xml'
--- vatregistries_webkit/report_menus.xml 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/report_menus.xml 2013-03-10 21:38:23 +0000
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+
+ <menuitem icon="STOCK_PRINT" name="VAT Registries"
+ parent="account.next_id_22" action="action_account_print_vatregistries_webkit"
+ groups="account.group_account_manager,account.group_account_user" id="account.menu_account_vatregistries_report"/>
+
+ </data>
+</openerp>
=== added directory 'vatregistries_webkit/wizard'
=== added file 'vatregistries_webkit/wizard/__init__.py'
--- vatregistries_webkit/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/wizard/__init__.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,21 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# (c)2013 Sergio Corato
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from . import vatregistries_wizard
=== added file 'vatregistries_webkit/wizard/vatregistries_wizard.py'
--- vatregistries_webkit/wizard/vatregistries_wizard.py 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/wizard/vatregistries_wizard.py 2013-03-10 21:38:23 +0000
@@ -0,0 +1,55 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Author: Guewen Baconnier
+# Copyright Camptocamp SA 2011
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, orm
+
+class account_print_vatregistries(orm.TransientModel):
+ _inherit = "account.common.journal.report"
+ _name = 'account.print.vatregistries'
+ _description = 'Account Print VAT Registries'
+
+ _columns = {
+ 'sort_selection': fields.selection([('l.date', 'Date'),
+ ('am.name', 'Journal Entry Number'),],
+ 'Entries Sorted by', required=True),
+ 'journal_ids': fields.many2many('account.journal',
+ 'account_print_journal_vatregistries_rel', 'account_id',
+ 'journal_id', 'Journals', required=True),
+ 'target_invoice': fields.selection([('posted', 'All Posted Entries'),
+ ('all', 'All Entries (includes draft)'),
+ ], 'Target Invoices', required=True),
+ }
+
+ _defaults = {
+ 'sort_selection': 'am.name',
+ 'filter': 'filter_period',
+ 'journal_ids': False,
+ 'target_invoice': 'posted',
+ }
+
+ def _print_report(self, cr, uid, ids, data, context=None):
+ # we update form with display account value
+ data = self.pre_print_report(cr, uid, ids, data, context=context)
+
+ return {'type': 'ir.actions.report.xml',
+ 'report_name': 'account.account_report_vatregistries_webkit',
+ 'datas': data}
+
=== added file 'vatregistries_webkit/wizard/vatregistries_wizard_view.xml'
--- vatregistries_webkit/wizard/vatregistries_wizard_view.xml 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/wizard/vatregistries_wizard_view.xml 2013-03-10 21:38:23 +0000
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="account_report_print_vatregistries" model="ir.ui.view">
+ <field name="name">Account Print VAT Registries</field>
+ <field name="model">account.print.vatregistries</field>
+ <field name="inherit_id" ref="account.account_common_report_view" />
+ <field name="arch" type="xml">
+ <data>
+ <xpath expr="//field[@name='target_move']" position="replace">
+ <!--field name="target_invoice"/-->
+ <!--field name="amount_currency"/-->
+ <newline/>
+ <field name="filter" on_change="onchange_filter(filter, fiscalyear_id)" colspan="4" invisible="1"/>
+ <separator string="Periods" colspan="4"/>
+ <field name="period_from" domain="[('fiscalyear_id', '=', fiscalyear_id)]" required="1" colspan="4"/>
+ <field name="period_to" domain="[('fiscalyear_id', '=', fiscalyear_id)]" required="1" colspan="4"/>
+ <separator string="Journals" colspan="4"/>
+ <field name="journal_ids" domain="[('type', 'in', ('sale','purchase','sale_refund','purchase_refund'))]" colspan="4" nolabel="1"/>
+ </xpath>
+ <xpath expr="//page[@name='filters']" position="replace">
+ </xpath>
+ <xpath expr="//page[@name='journal_ids']" position="replace">
+ </xpath>
+ </data>
+ </field>
+ </record>
+
+ <record id="action_account_print_vatregistries_webkit" model="ir.actions.act_window">
+ <field name="name">Print VAT Journal</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">account.print.vatregistries</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+ <field name="view_id" ref="account_report_print_vatregistries"/>
+ </record>
+ <menuitem
+ name="VAT Journals"
+ parent="account.menu_journals_report"
+ action="action_account_print_vatregistries_webkit"
+ id="menu_account_print_vatregistries"
+ icon="STOCK_PRINT"
+ sequence="1"/>
+
+ </data>
+</openerp>
=== added file 'vatregistries_webkit/wizard/wizard.xml'
--- vatregistries_webkit/wizard/wizard.xml 1970-01-01 00:00:00 +0000
+++ vatregistries_webkit/wizard/wizard.xml 2013-03-10 21:38:23 +0000
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<openerp>
+ <data> </data>
+</openerp>
Follow ups