From af306bab1f7b33adda0b95650eaac1deed03e87c Mon Sep 17 00:00:00 2001 From: jcoux Date: Tue, 21 Jun 2016 17:22:51 +0200 Subject: [PATCH] Add OCA General/Partner Ledger PDF --- account_financial_report_qweb/__openerp__.py | 7 +- account_financial_report_qweb/menuitems.xml | 11 +- .../report/__init__.py | 1 - .../report/common.py | 101 -- .../report/general_ledger.py | 896 +++++++++++++++--- .../report/templates/general_ledger.xml | 452 ++++----- .../report/templates/layouts.xml | 38 + account_financial_report_qweb/reports.xml | 26 +- .../static/src/css/report.css | 20 +- .../view/account_view.xml | 17 + .../wizard/__init__.py | 2 +- .../wizard/general_ledger_wizard.py | 96 ++ .../wizard/general_ledger_wizard.xml | 97 -- .../wizard/general_ledger_wizard_view.xml | 60 ++ .../wizard/ledger.sql | 54 -- .../wizard/ledger_report_wizard.py | 337 ------- .../wizard/partner_ledger_wizard.xml | 54 -- 17 files changed, 1238 insertions(+), 1031 deletions(-) delete mode 100644 account_financial_report_qweb/report/common.py create mode 100644 account_financial_report_qweb/report/templates/layouts.xml create mode 100644 account_financial_report_qweb/view/account_view.xml create mode 100644 account_financial_report_qweb/wizard/general_ledger_wizard.py delete mode 100644 account_financial_report_qweb/wizard/general_ledger_wizard.xml create mode 100644 account_financial_report_qweb/wizard/general_ledger_wizard_view.xml delete mode 100644 account_financial_report_qweb/wizard/ledger.sql delete mode 100644 account_financial_report_qweb/wizard/ledger_report_wizard.py delete mode 100644 account_financial_report_qweb/wizard/partner_ledger_wizard.xml diff --git a/account_financial_report_qweb/__openerp__.py b/account_financial_report_qweb/__openerp__.py index 33b23c0f..777ac341 100644 --- a/account_financial_report_qweb/__openerp__.py +++ b/account_financial_report_qweb/__openerp__.py @@ -21,9 +21,8 @@ ], 'data': [ 'wizard/aged_partner_balance_wizard_view.xml', - 'wizard/general_ledger_wizard.xml', + 'wizard/general_ledger_wizard_view.xml', 'wizard/open_invoice_wizard_view.xml', - 'wizard/partner_ledger_wizard.xml', 'wizard/balance_common_wizard_view.xml', 'wizard/partner_balance_wizard_view.xml', 'wizard/trial_balance_wizard_view.xml', @@ -31,7 +30,9 @@ 'menuitems.xml', 'reports.xml', 'report/templates/general_ledger.xml', - 'report/templates/open_invoice_report.xml' + 'report/templates/layouts.xml', + 'report/templates/open_invoice_report.xml', + 'view/account_view.xml' ], 'test': [ ], diff --git a/account_financial_report_qweb/menuitems.xml b/account_financial_report_qweb/menuitems.xml index bf82c1c1..ff8fd49c 100644 --- a/account_financial_report_qweb/menuitems.xml +++ b/account_financial_report_qweb/menuitems.xml @@ -6,18 +6,13 @@ parent="account.menu_finance_reports" id="menu_oca_reports" name="OCA reports" + groups="account.group_account_manager,account.group_account_user" /> - - =', start_date)] - if end_date: - domain += [('date', '<=', end_date)] - - if self.target_move == 'posted': - domain += [('move_state', '=', 'posted')] - - if self.account_ids: - domain += [('account_id', 'in', self.account_ids.ids)] - - return domain diff --git a/account_financial_report_qweb/report/general_ledger.py b/account_financial_report_qweb/report/general_ledger.py index 4b6522e9..122cae42 100644 --- a/account_financial_report_qweb/report/general_ledger.py +++ b/account_financial_report_qweb/report/general_ledger.py @@ -1,130 +1,802 @@ # -*- coding: utf-8 -*- -# © 2015 Yannick Vaucher (Camptocamp) +# © 2016 Julien Coux (Camptocamp) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openerp import models, fields, api -class FinancialReportLine(models.Model): - _inherit = 'financial.report.line' - _name = 'general.ledger.line' - _description = "General Ledger report line" +class GeneralLedgerReport(models.TransientModel): + """ Here, we just define class fields. + For methods, go more bottom at this file. + """ + + _name = 'report_general_ledger_qweb' + + date_from = fields.Date() + date_to = fields.Date() + fy_start_date = fields.Date() + only_posted_moves = fields.Boolean() + hide_account_balance_at_0 = fields.Boolean() + company_id = fields.Many2one(comodel_name='res.company') + filter_account_ids = fields.Many2many(comodel_name='account.account') + filter_partner_ids = fields.Many2many(comodel_name='res.partner') + has_second_currency = fields.Boolean() + centralize = fields.Boolean() + show_cost_center = fields.Boolean( + default=lambda self: self.env.user.has_group( + 'analytic.group_analytic_accounting' + ) + ) - _auto = False - _order = 'account_id, date' + account_ids = fields.One2many( + comodel_name='report_general_ledger_qweb_account', + inverse_name='report_id' + ) - @api.depends('invoice_number', 'name') - def _get_label(self): - for rec in self: - label = rec.name - if rec.invoice_number: - label += u' ({})'.format(rec.invoice_number) - rec.label = label - label = fields.Char(compute='_get_label', readonly=True, store=False) +class GeneralLedgerReportAccount(models.TransientModel): + _name = 'report_general_ledger_qweb_account' + _order = 'code ASC' + + report_id = fields.Many2one( + comodel_name='report_general_ledger_qweb', + ondelete='cascade', + index=True + ) + account_id = fields.Many2one( + 'account.account', + index=True + ) + code = fields.Char() + name = fields.Char() + initial_debit = fields.Float(digits=(16, 2)) + initial_credit = fields.Float(digits=(16, 2)) + initial_balance = fields.Float(digits=(16, 2)) + final_debit = fields.Float(digits=(16, 2)) + final_credit = fields.Float(digits=(16, 2)) + final_balance = fields.Float(digits=(16, 2)) + is_partner_account = fields.Boolean() + + move_line_ids = fields.One2many( + comodel_name='report_general_ledger_qweb_move_line', + inverse_name='report_account_id' + ) + partner_ids = fields.One2many( + comodel_name='report_general_ledger_qweb_partner', + inverse_name='report_account_id' + ) -class GeneralLedgerReport(models.TransientModel): - _name = 'report.account.report_generalledger_qweb' - _inherit = 'account.report.common' +class GeneralLedgerReportPartner(models.TransientModel): - @api.multi - def _get_account_ids(self): - res = False - context = self.env.context - if (context.get('active_model') == 'account.account' and - context.get('active_ids')): - res = context['active_ids'] - return res + _name = 'report_general_ledger_qweb_partner' + report_account_id = fields.Many2one( + comodel_name='report_general_ledger_qweb_account', + ondelete='cascade', + index=True + ) + partner_id = fields.Many2one( + 'res.partner', + index=True + ) name = fields.Char() - initial_balance = fields.Integer() - account_ids = fields.Many2many( - 'account.account', - string='Filter on accounts', - default=_get_account_ids, - help="Only selected accounts will be printed. Leave empty to " - "print all accounts.") - journal_ids = fields.Many2many( - 'account.journal', - string='Filter on jourvals', - help="Only selected journals will be printed. Leave empty to " - "print all journals.") - balance_mode = fields.Selection( - [('initial_balance', 'Initial balance'), - ('opening_balance', 'Opening balance')] - ) - display_account = fields.Char() - display_ledger_lines = fields.Boolean() - display_initial_balance = fields.Boolean() - - MAPPING = { - 'date_from': 'start_date', - 'date_to': 'end_date', - } + initial_debit = fields.Float(digits=(16, 2)) + initial_credit = fields.Float(digits=(16, 2)) + initial_balance = fields.Float(digits=(16, 2)) + final_debit = fields.Float(digits=(16, 2)) + final_credit = fields.Float(digits=(16, 2)) + final_balance = fields.Float(digits=(16, 2)) + + move_line_ids = fields.One2many( + comodel_name='report_general_ledger_qweb_move_line', + inverse_name='report_partner_id' + ) @api.model - def _get_values_from_wizard(self, data): - """ Get values from wizard """ - values = {} - for key, val in data.iteritems(): - if key in self.MAPPING: - values[self.MAPPING[key]] = val - elif key == 'journal_ids': - if val: - values[key] = [(6, 0, val)] - else: - values[key] = val - return values - - @api.multi - def _get_centralized_move_ids(self, domain): - """ Get last line of each selected centralized accounts """ - # inverse search on centralized boolean to finish the search to get the - # ids of last lines of centralized accounts - # XXX USE DISTINCT to speed up ? - domain = domain[:] - centralize_index = domain.index(('centralized', '=', False)) - domain[centralize_index] = ('centralized', '=', True) - - gl_lines = self.env['general.ledger.line'].search(domain) - accounts = gl_lines.mapped('account_id') - - line_ids = [] - for acc in accounts: - acc_lines = gl_lines.filtered(lambda rec: rec.account_id == acc) - line_ids.append(acc_lines[-1].id) - return line_ids - - @api.multi - def _get_moves_from_dates(self): - domain = self._get_moves_from_dates_domain() - if self.centralize: - centralized_ids = self._get_centralized_move_ids(domain) - if centralized_ids: - domain.insert(0, '|') - domain.append(('id', 'in', centralized_ids)) - return self.env['general.ledger.line'].search(domain) - - @api.multi - def render_html(self, data=None): - report_name = 'account.report_generalledger_qweb' - if data is None: - return - values = self._get_values_from_wizard(data['form']) - report = self.create(values) - - report_lines = report._get_moves_from_dates() - # TODO warning if no report_lines - self.env['report']._get_report_from_name(report_name) - - docargs = { - 'doc_ids': report.ids, - 'doc_model': self._name, - 'report_lines': report_lines, - 'docs': report, - # XXX - 'has_currency': True + def _generate_order_by(self, order_spec, query): + return """ +ORDER BY +CASE + WHEN "report_general_ledger_qweb_partner"."partner_id" IS NOT NULL + THEN 0 + ELSE 1 + END, +"report_general_ledger_qweb_partner"."name" + """ + + +class GeneralLedgerReportMoveLine(models.TransientModel): + + _name = 'report_general_ledger_qweb_move_line' + + report_account_id = fields.Many2one( + comodel_name='report_general_ledger_qweb_account', + ondelete='cascade', + index=True + ) + report_partner_id = fields.Many2one( + comodel_name='report_general_ledger_qweb_partner', + ondelete='cascade', + index=True + ) + move_line_id = fields.Many2one('account.move.line') + date = fields.Date() + entry = fields.Char() + journal = fields.Char() + account = fields.Char() + partner = fields.Char() + label = fields.Char() + cost_center = fields.Char() + matching_number = fields.Char() + debit = fields.Float(digits=(16, 2)) + credit = fields.Float(digits=(16, 2)) + cumul_balance = fields.Float(digits=(16, 2)) + currency_name = fields.Char() + amount_currency = fields.Float(digits=(16, 2)) + + +class GeneralLedgerReportCompute(models.TransientModel): + + _inherit = 'report_general_ledger_qweb' + + @api.model + def print_report(self): + self.ensure_one() + self.compute_data_for_report() + return { + 'type': 'ir.actions.report.xml', + 'report_name': + 'account_financial_report_qweb.report_general_ledger_qweb', + 'datas': {'ids': [self.id]}, } - return self.env['report'].render(report_name, docargs) + + @api.model + def compute_data_for_report(self): + self.ensure_one() + + self.inject_account_values() + self.inject_partner_values() + self.inject_line_not_centralized_values() + self.inject_line_not_centralized_values(is_account_line=False, + is_partner_line=True) + self.inject_line_not_centralized_values(is_account_line=False, + is_partner_line=True, + only_empty_partner_line=True) + if self.centralize: + self.inject_line_centralized_values() + self.compute_has_second_currency() + + def inject_account_values(self): + subquery_sum_amounts = """ + SELECT + a.id AS account_id, + SUM(ml.debit) AS debit, + SUM(ml.credit) AS credit, + SUM(ml.balance) AS balance + FROM + accounts a + INNER JOIN + account_account_type at ON a.user_type_id = at.id + INNER JOIN + account_move_line ml + ON a.id = ml.account_id + AND ml.date <= %s + AND + ( + at.include_initial_balance != TRUE AND ml.date >= %s + OR at.include_initial_balance = TRUE + ) + """ + if self.only_posted_moves: + subquery_sum_amounts += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + subquery_sum_amounts += """ + GROUP BY + a.id + """ + query_inject_account = """ +WITH + accounts AS + ( + SELECT + a.id, + a.code, + a.name, + a.internal_type IN ('payable', 'receivable') + AS is_partner_account, + a.user_type_id + FROM + account_account a + """ + if self.filter_partner_ids: + query_inject_account += """ + INNER JOIN + account_move_line ml ON a.id = ml.account_id + INNER JOIN + res_partner p ON ml.partner_id = p.id + """ + query_inject_account += """ + WHERE + a.company_id = %s + """ + if self.filter_account_ids: + query_inject_account += """ + AND + a.id IN %s + """ + if self.filter_partner_ids: + query_inject_account += """ + AND + p.id IN %s + GROUP BY + a.id + """ + query_inject_account += """ + ), + initial_sum_amounts AS ( """ + subquery_sum_amounts + """ ), + final_sum_amounts AS ( """ + subquery_sum_amounts + """ ) +INSERT INTO + report_general_ledger_qweb_account + ( + report_id, + create_uid, + create_date, + account_id, + code, + name, + initial_debit, + initial_credit, + initial_balance, + final_debit, + final_credit, + final_balance, + is_partner_account + ) +SELECT + %s AS report_id, + %s AS create_uid, + NOW() AS create_date, + a.id AS account_id, + a.code, + a.name, + COALESCE(i.debit, 0.0) AS initial_debit, + COALESCE(i.credit, 0.0) AS initial_credit, + COALESCE(i.balance, 0.0) AS initial_balance, + COALESCE(f.debit, 0.0) AS final_debit, + COALESCE(f.credit, 0.0) AS final_credit, + COALESCE(f.balance, 0.0) AS final_balance, + a.is_partner_account +FROM + accounts a +LEFT JOIN + initial_sum_amounts i ON a.id = i.account_id +LEFT JOIN + final_sum_amounts f ON a.id = f.account_id +WHERE + ( + i.debit IS NOT NULL AND i.debit != 0 + OR i.credit IS NOT NULL AND i.credit != 0 + OR i.balance IS NOT NULL AND i.balance != 0 + OR f.debit IS NOT NULL AND f.debit != 0 + OR f.credit IS NOT NULL AND f.credit != 0 + OR f.balance IS NOT NULL AND f.balance != 0 + ) + """ + if self.hide_account_balance_at_0: + query_inject_account += """ +AND + f.balance IS NOT NULL AND f.balance != 0 + """ + query_inject_account_params = ( + self.company_id.id, + ) + if self.filter_account_ids: + query_inject_account_params += ( + tuple(self.filter_account_ids.ids), + ) + if self.filter_partner_ids: + query_inject_account_params += ( + tuple(self.filter_partner_ids.ids), + ) + query_inject_account_params += ( + self.date_from, + self.fy_start_date, + self.date_to, + self.fy_start_date, + self.id, + self.env.uid, + ) + self.env.cr.execute(query_inject_account, query_inject_account_params) + + def inject_partner_values(self): + subquery_sum_amounts = """ + SELECT + ap.account_id AS account_id, + ap.partner_id AS partner_id, + SUM(ml.debit) AS debit, + SUM(ml.credit) AS credit, + SUM(ml.balance) AS balance + FROM + accounts_partners ap + INNER JOIN + account_move_line ml + ON ap.account_id = ml.account_id + AND ( + ap.partner_id = ml.partner_id + OR ap.partner_id IS NULL AND ml.partner_id IS NULL + ) + AND ml.date <= %s + AND ( + ap.include_initial_balance != TRUE AND ml.date >= %s + OR ap.include_initial_balance = TRUE + ) + """ + if self.only_posted_moves: + subquery_sum_amounts += """ + INNER JOIN + account_move m ON ml.move_id = m.id AND m.state = 'posted' + """ + subquery_sum_amounts += """ + GROUP BY + ap.account_id, ap.partner_id + """ + query_inject_partner = """ +WITH + accounts_partners AS + ( + SELECT + ra.id AS report_account_id, + a.id AS account_id, + at.include_initial_balance AS include_initial_balance, + p.id AS partner_id, + COALESCE( + CASE + WHEN + NULLIF(p.name, '') IS NOT NULL + AND NULLIF(p.ref, '') IS NOT NULL + THEN p.name || ' (' || p.ref || ')' + ELSE p.name + END, + 'No partner allocated' + ) AS partner_name + FROM + report_general_ledger_qweb_account ra + INNER JOIN + account_account a ON ra.account_id = a.id + INNER JOIN + account_account_type at ON a.user_type_id = at.id + INNER JOIN + account_move_line ml ON a.id = ml.account_id + LEFT JOIN + res_partner p ON ml.partner_id = p.id + WHERE + ra.report_id = %s + AND + ra.is_partner_account = TRUE + """ + if self.centralize: + query_inject_partner += """ + AND + (a.centralized IS NULL OR a.centralized != TRUE) + """ + if self.filter_partner_ids: + query_inject_partner += """ + AND + p.id IN %s + """ + query_inject_partner += """ + GROUP BY + ra.id, + a.id, + p.id, + at.include_initial_balance + ), + initial_sum_amounts AS ( """ + subquery_sum_amounts + """ ), + final_sum_amounts AS ( """ + subquery_sum_amounts + """ ) +INSERT INTO + report_general_ledger_qweb_partner + ( + report_account_id, + create_uid, + create_date, + partner_id, + name, + initial_debit, + initial_credit, + initial_balance, + final_debit, + final_credit, + final_balance + ) +SELECT + ap.report_account_id, + %s AS create_uid, + NOW() AS create_date, + ap.partner_id, + ap.partner_name, + COALESCE(i.debit, 0.0) AS initial_debit, + COALESCE(i.credit, 0.0) AS initial_credit, + COALESCE(i.balance, 0.0) AS initial_balance, + COALESCE(f.debit, 0.0) AS final_debit, + COALESCE(f.credit, 0.0) AS final_credit, + COALESCE(f.balance, 0.0) AS final_balance +FROM + accounts_partners ap +LEFT JOIN + initial_sum_amounts i + ON + ( + ap.partner_id = i.partner_id + OR ap.partner_id IS NULL AND i.partner_id IS NULL + ) + AND ap.account_id = i.account_id +LEFT JOIN + final_sum_amounts f + ON + ( + ap.partner_id = f.partner_id + OR ap.partner_id IS NULL AND f.partner_id IS NULL + ) + AND ap.account_id = f.account_id +WHERE + ( + i.debit IS NOT NULL AND i.debit != 0 + OR i.credit IS NOT NULL AND i.credit != 0 + OR i.balance IS NOT NULL AND i.balance != 0 + OR f.debit IS NOT NULL AND f.debit != 0 + OR f.credit IS NOT NULL AND f.credit != 0 + OR f.balance IS NOT NULL AND f.balance != 0 + ) + """ + if self.hide_account_balance_at_0: + query_inject_partner += """ +AND + f.balance IS NOT NULL AND f.balance != 0 + """ + query_inject_partner_params = ( + self.id, + ) + if self.filter_partner_ids: + query_inject_partner_params += ( + tuple(self.filter_partner_ids.ids), + ) + query_inject_partner_params += ( + self.date_from, + self.fy_start_date, + self.date_to, + self.fy_start_date, + self.env.uid, + ) + print query_inject_partner_params + self.env.cr.execute(query_inject_partner, query_inject_partner_params) + + def inject_line_not_centralized_values(self, + is_account_line=True, + is_partner_line=False, + only_empty_partner_line=False): + query_inject_move_line = """ +INSERT INTO + report_general_ledger_qweb_move_line + ( + """ + if is_account_line: + query_inject_move_line += """ + report_account_id, + """ + elif is_partner_line: + query_inject_move_line += """ + report_partner_id, + """ + query_inject_move_line += """ + create_uid, + create_date, + move_line_id, + date, + entry, + journal, + account, + partner, + label, + cost_center, + matching_number, + debit, + credit, + cumul_balance, + currency_name, + amount_currency + ) +SELECT + """ + if is_account_line: + query_inject_move_line += """ + ra.id AS report_account_id, + """ + elif is_partner_line: + query_inject_move_line += """ + rp.id AS report_partner_id, + """ + query_inject_move_line += """ + %s AS create_uid, + NOW() AS create_date, + ml.id AS move_line_id, + ml.date, + m.name AS entry, + j.code AS journal, + a.code AS account, + """ + if not only_empty_partner_line: + query_inject_move_line += """ + CASE + WHEN + NULLIF(p.name, '') IS NOT NULL + AND NULLIF(p.ref, '') IS NOT NULL + THEN p.name || ' (' || p.ref || ')' + ELSE p.name + END AS partner, + """ + elif only_empty_partner_line: + query_inject_move_line += """ + 'No partner allocated' AS partner, + """ + query_inject_move_line += """ + CONCAT_WS(' - ', NULLIF(ml.ref, ''), NULLIF(ml.name, '')) AS label, + aa.name AS cost_center, + fr.name AS matching_number, + ml.debit, + ml.credit, + """ + if is_account_line: + query_inject_move_line += """ + ra.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code + ORDER BY a.code, ml.date, ml.id) + ) AS cumul_balance, + """ + elif is_partner_line and not only_empty_partner_line: + query_inject_move_line += """ + rp.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code, p.name + ORDER BY a.code, p.name, ml.date, ml.id) + ) AS cumul_balance, + """ + elif is_partner_line and only_empty_partner_line: + query_inject_move_line += """ + rp.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code + ORDER BY a.code, ml.date, ml.id) + ) AS cumul_balance, + """ + query_inject_move_line += """ + c.name AS currency_name, + ml.amount_currency +FROM + """ + if is_account_line: + query_inject_move_line += """ + report_general_ledger_qweb_account ra + """ + elif is_partner_line: + query_inject_move_line += """ + report_general_ledger_qweb_partner rp +INNER JOIN + report_general_ledger_qweb_account ra ON rp.report_account_id = ra.id + """ + query_inject_move_line += """ +INNER JOIN + account_move_line ml ON ra.account_id = ml.account_id +INNER JOIN + account_move m ON ml.move_id = m.id +INNER JOIN + account_journal j ON ml.journal_id = j.id +INNER JOIN + account_account a ON ml.account_id = a.id + """ + if is_account_line: + query_inject_move_line += """ +LEFT JOIN + res_partner p ON ml.partner_id = p.id + """ + elif is_partner_line and not only_empty_partner_line: + query_inject_move_line += """ +INNER JOIN + res_partner p + ON ml.partner_id = p.id AND rp.partner_id = p.id + """ + query_inject_move_line += """ +LEFT JOIN + account_full_reconcile fr ON ml.full_reconcile_id = fr.id +LEFT JOIN + res_currency c ON a.currency_id = c.id +LEFT JOIN + account_analytic_account aa ON ml.analytic_account_id = aa.id +WHERE + ra.report_id = %s +AND + """ + if is_account_line: + query_inject_move_line += """ + (ra.is_partner_account IS NULL OR ra.is_partner_account != TRUE) + """ + elif is_partner_line: + query_inject_move_line += """ + ra.is_partner_account = TRUE + """ + if self.centralize: + query_inject_move_line += """ +AND + (a.centralized IS NULL OR a.centralized != TRUE) + """ + query_inject_move_line += """ +AND + ml.date BETWEEN %s AND %s + """ + if self.only_posted_moves: + query_inject_move_line += """ +AND + m.state = 'posted' + """ + if only_empty_partner_line: + query_inject_move_line += """ +AND + ml.partner_id IS NULL +AND + rp.partner_id IS NULL + """ + if is_account_line: + query_inject_move_line += """ +ORDER BY + a.code, ml.date, ml.id + """ + elif is_partner_line and not only_empty_partner_line: + query_inject_move_line += """ +ORDER BY + a.code, p.name, ml.date, ml.id + """ + elif is_partner_line and only_empty_partner_line: + query_inject_move_line += """ +ORDER BY + a.code, ml.date, ml.id + """ + self.env.cr.execute( + query_inject_move_line, + (self.env.uid, + self.id, + self.date_from, + self.date_to,) + ) + + def inject_line_centralized_values(self): + query_inject_move_line_centralized = """ +WITH + move_lines AS + ( + SELECT + ml.account_id, + ( + DATE_TRUNC('month', ml.date) + interval '1 month' + - interval '1 day' + )::date AS date, + SUM(ml.debit) AS debit, + SUM(ml.credit) AS credit, + SUM(ml.balance) AS balance + FROM + report_general_ledger_qweb_account ra + INNER JOIN + account_move_line ml ON ra.account_id = ml.account_id + INNER JOIN + account_move m ON ml.move_id = m.id + INNER JOIN + account_account a ON ml.account_id = a.id + WHERE + ra.report_id = %s + AND + a.centralized = TRUE + AND + ml.date BETWEEN %s AND %s + """ + if self.only_posted_moves: + query_inject_move_line_centralized += """ + AND + m.state = 'posted' + """ + query_inject_move_line_centralized += """ + GROUP BY + ra.id, ml.account_id, a.code, 2 + ) +INSERT INTO + report_general_ledger_qweb_move_line + ( + report_account_id, + create_uid, + create_date, + date, + account, + label, + debit, + credit, + cumul_balance + ) +SELECT + ra.id AS report_account_id, + %s AS create_uid, + NOW() AS create_date, + ml.date, + a.code AS account, + 'Centralized Entries' AS label, + ml.debit AS debit, + ml.credit AS credit, + ra.initial_balance + ( + SUM(ml.balance) + OVER (PARTITION BY a.code ORDER BY ml.date) + ) AS cumul_balance +FROM + report_general_ledger_qweb_account ra +INNER JOIN + move_lines ml ON ra.account_id = ml.account_id +INNER JOIN + account_account a ON ml.account_id = a.id +LEFT JOIN + res_currency c ON a.currency_id = c.id +WHERE + ra.report_id = %s +AND + (a.centralized IS NOT NULL AND a.centralized = TRUE) +ORDER BY + a.code, ml.date + """ + self.env.cr.execute( + query_inject_move_line_centralized, + (self.id, + self.date_from, + self.date_to, + self.env.uid, + self.id,) + ) + + def compute_has_second_currency(self): + query_update_has_second_currency = """ +UPDATE + report_general_ledger_qweb +SET + has_second_currency = + ( + SELECT + TRUE + FROM + report_general_ledger_qweb_move_line l + INNER JOIN + report_general_ledger_qweb_account a + ON l.report_account_id = a.id + WHERE + a.report_id = %s + AND l.currency_name IS NOT NULL + LIMIT 1 + ) + OR + ( + SELECT + TRUE + FROM + report_general_ledger_qweb_move_line l + INNER JOIN + report_general_ledger_qweb_partner p + ON l.report_partner_id = p.id + INNER JOIN + report_general_ledger_qweb_account a + ON p.report_account_id = a.id + WHERE + a.report_id = %s + AND l.currency_name IS NOT NULL + LIMIT 1 + ) +WHERE id = %s + """ + params = (self.id,) * 3 + self.env.cr.execute(query_update_has_second_currency, params) diff --git a/account_financial_report_qweb/report/templates/general_ledger.xml b/account_financial_report_qweb/report/templates/general_ledger.xml index f769fddd..fe19bb16 100644 --- a/account_financial_report_qweb/report/templates/general_ledger.xml +++ b/account_financial_report_qweb/report/templates/general_ledger.xml @@ -1,287 +1,225 @@ - - + + \ No newline at end of file diff --git a/account_financial_report_qweb/report/templates/layouts.xml b/account_financial_report_qweb/report/templates/layouts.xml new file mode 100644 index 00000000..d7a18165 --- /dev/null +++ b/account_financial_report_qweb/report/templates/layouts.xml @@ -0,0 +1,38 @@ + + + + + + + + \ No newline at end of file diff --git a/account_financial_report_qweb/reports.xml b/account_financial_report_qweb/reports.xml index 08a2ae2d..d1fe0f0c 100644 --- a/account_financial_report_qweb/reports.xml +++ b/account_financial_report_qweb/reports.xml @@ -4,11 +4,11 @@ @@ -45,5 +45,25 @@ + + Account financial report qweb paperformat + + custom + 297 + 210 + Portrait + 12 + 8 + 5 + 5 + + 10 + 110 + + + + + + diff --git a/account_financial_report_qweb/static/src/css/report.css b/account_financial_report_qweb/static/src/css/report.css index 8c959955..6efa9919 100644 --- a/account_financial_report_qweb/static/src/css/report.css +++ b/account_financial_report_qweb/static/src/css/report.css @@ -29,7 +29,7 @@ body, table, td, span, div { border-left:0px; border-right:0px; text-align:left; - font-size:9px; + font-size:10px; padding-right:3px; padding-left:3px; padding-top:2px; @@ -58,14 +58,22 @@ body, table, td, span, div { font-style:italic; } .account_title { - font-size:10px; + font-size:11px; font-weight:bold; - page-break-after: avoid; +} +.account_title.labels { + background-color:#F0F0F0 !important; } .act_as_cell.amount { word-wrap:normal; text-align:right; } +.act_as_cell.left { + text-align:left; +} +.act_as_cell.right { + text-align:right; +} .list_table .act_as_cell{ padding-left: 5px; /* border-right:1px solid lightGrey; uncomment to active column lines */ @@ -79,3 +87,9 @@ body, table, td, span, div { overflow: hidden; white-space: nowrap; } +.custom_footer { + font-size:7px !important; +} +.page_break { + page-break-inside: avoid; +} diff --git a/account_financial_report_qweb/view/account_view.xml b/account_financial_report_qweb/view/account_view.xml new file mode 100644 index 00000000..c7781c57 --- /dev/null +++ b/account_financial_report_qweb/view/account_view.xml @@ -0,0 +1,17 @@ + + + + + account.account.form.inherit + + account.account + form + + + + + + + + + diff --git a/account_financial_report_qweb/wizard/__init__.py b/account_financial_report_qweb/wizard/__init__.py index 8ff39244..5df33995 100644 --- a/account_financial_report_qweb/wizard/__init__.py +++ b/account_financial_report_qweb/wizard/__init__.py @@ -3,6 +3,6 @@ # Copyright 2016 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import aged_partner_balance_wizard -from . import ledger_report_wizard from . import balance_common_wizard +from . import general_ledger_wizard from . import open_invoice_wizard diff --git a/account_financial_report_qweb/wizard/general_ledger_wizard.py b/account_financial_report_qweb/wizard/general_ledger_wizard.py new file mode 100644 index 00000000..098df527 --- /dev/null +++ b/account_financial_report_qweb/wizard/general_ledger_wizard.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Author: Damien Crier +# Author: Julien Coux +# Copyright 2016 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, fields, api + + +class GeneralLedgerReportWizard(models.TransientModel): + """General ledger report wizard.""" + + _name = "general.ledger.report.wizard" + _description = "General Ledger Report Wizard" + + company_id = fields.Many2one( + comodel_name='res.company', + default=lambda self: self.env.user.company_id + ) + date_range_id = fields.Many2one(comodel_name='date.range', required=True) + date_from = fields.Date(required=True) + date_to = fields.Date(required=True) + fy_start_date = fields.Date(required=True) + target_move = fields.Selection([('posted', 'All Posted Entries'), + ('all', 'All Entries')], + string='Target Moves', + required=True, + default='all') + account_ids = fields.Many2many( + comodel_name='account.account', + string='Filter accounts', + ) + centralize = fields.Boolean(string='Activate centralization', + default=True) + hide_account_balance_at_0 = fields.Boolean( + string='Hide account ending balance at 0', + help='Use this filter to hide an account or a partner ' + 'with an ending balance at 0. ' + 'If partners are filtered, ' + 'debits and credits totals will not match the trial balance.', + default=False) + receivable_accounts_only = fields.Boolean() + payable_accounts_only = fields.Boolean() + partner_ids = fields.Many2many( + comodel_name='res.partner', + string='Filter partners', + ) + + @api.onchange('date_range_id') + def onchange_date_range_id(self): + """Handle date range change.""" + self.date_from = self.date_range_id.date_start + self.date_to = self.date_range_id.date_end + if self.date_from: + self.fy_start_date = self.env.user.company_id.find_daterange_fy( + fields.Date.from_string(self.date_range_id.date_start) + ).date_start + + @api.onchange('receivable_accounts_only', 'payable_accounts_only') + def onchange_type_accounts_only(self): + """Handle receivable/payable accounts only change.""" + if self.receivable_accounts_only or self.payable_accounts_only: + domain = [] + if self.receivable_accounts_only and self.payable_accounts_only: + domain += [('internal_type', 'in', ('receivable', 'payable'))] + elif self.receivable_accounts_only: + domain += [('internal_type', '=', 'receivable')] + elif self.payable_accounts_only: + domain += [('internal_type', '=', 'payable')] + self.account_ids = self.env['account.account'].search(domain) + else: + self.account_ids = None + + @api.onchange('partner_ids') + def onchange_partner_ids(self): + """Handle partners change.""" + if self.partner_ids: + self.receivable_accounts_only = self.payable_accounts_only = True + else: + self.receivable_accounts_only = self.payable_accounts_only = False + + @api.multi + def button_export_pdf(self): + model = self.env['report_general_ledger_qweb'] + report = model.create({ + 'date_from': self.date_from, + 'date_to': self.date_to, + 'only_posted_moves': self.target_move == 'posted', + 'hide_account_balance_at_0': self.hide_account_balance_at_0, + 'company_id': self.company_id.id, + 'filter_account_ids': [(6, 0, self.account_ids.ids)], + 'filter_partner_ids': [(6, 0, self.partner_ids.ids)], + 'centralize': self.centralize, + 'fy_start_date': self.fy_start_date, + }) + return report.print_report() diff --git a/account_financial_report_qweb/wizard/general_ledger_wizard.xml b/account_financial_report_qweb/wizard/general_ledger_wizard.xml deleted file mode 100644 index 82f93690..00000000 --- a/account_financial_report_qweb/wizard/general_ledger_wizard.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - General Ledger - ledger.report.wizard - -
- - - - - - - - - - - - - - - - - - - - -
- - - General Ledger Line tree - ledger.report.wizard.line - tree - - - - - - - - - - - - - - - General Ledger Line search - ledger.report.wizard.line - - - - - - - - - - - - General Ledger - ir.actions.act_window - ledger.report.wizard - form - form - - new - - -
-
diff --git a/account_financial_report_qweb/wizard/general_ledger_wizard_view.xml b/account_financial_report_qweb/wizard/general_ledger_wizard_view.xml new file mode 100644 index 00000000..5226de8d --- /dev/null +++ b/account_financial_report_qweb/wizard/general_ledger_wizard_view.xml @@ -0,0 +1,60 @@ + + + + + + + General Ledger + general.ledger.report.wizard + +
+ + + + + + + + + + + + + + + + +
+ + + General Ledger + ir.actions.act_window + general.ledger.report.wizard + form + form + + new + + +
+
diff --git a/account_financial_report_qweb/wizard/ledger.sql b/account_financial_report_qweb/wizard/ledger.sql deleted file mode 100644 index 9e8afd07..00000000 --- a/account_financial_report_qweb/wizard/ledger.sql +++ /dev/null @@ -1,54 +0,0 @@ -WITH view_q as ( - SELECT - ml.date, - acc.id AS account_id, - ml.debit, - ml.credit, - ml.name AS name, - ml.ref, - ml.journal_id, - ml.partner_id, - SUM(debit - credit) OVER w_account - (debit - credit) AS init_balance, - SUM(debit - credit) OVER w_account AS cumul_balance - FROM account_account AS acc - LEFT JOIN account_move_line AS ml ON (ml.account_id = acc.id) - INNER JOIN account_move AS m ON (ml.move_id = m.id) - INNER JOIN account_account_type aat ON (acc.user_type_id = aat.id) - WHERE ml.date >= %(fy_date)s OR aat.include_initial_balance IS TRUE - WINDOW w_account AS ( - PARTITION BY acc.code - ORDER BY ml.date, ml.id - ) - ORDER BY acc.id, ml.date -) -INSERT INTO ledger_report_wizard_line ( - date, - name, - journal_id, - account_id, - partner_id, - ref, - label, - --counterpart - init_balance, - debit, - credit, - cumul_balance, - wizard_id -) -SELECT - date, - name, - journal_id, - account_id, - partner_id, - ref, - ' TODO label ' AS label, - --counterpart - init_balance, - debit, - credit, - cumul_balance, - %(wizard_id)s AS wizard_id -FROM view_q -WHERE date BETWEEN %(date_from)s AND %(date_to)s; diff --git a/account_financial_report_qweb/wizard/ledger_report_wizard.py b/account_financial_report_qweb/wizard/ledger_report_wizard.py deleted file mode 100644 index 4d16df0f..00000000 --- a/account_financial_report_qweb/wizard/ledger_report_wizard.py +++ /dev/null @@ -1,337 +0,0 @@ -# -*- coding: utf-8 -*- -# Author: Damien Crier -# Copyright 2016 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from operator import itemgetter -from pkg_resources import resource_string - -from openerp import models, fields, api, _ - -# order to be placed on the report: field -FIELDS_TO_READ = {1: 'date', - 0: 'account_id', - 4: 'account_code', - 2: 'move_name', - 3: 'journal_id', - 5: 'partner_name', - 6: 'ref', - 7: 'label', - 8: 'debit', - 9: 'credit', - 30: 'amount_currency', - 40: 'currency_code', - 50: 'month', - 60: 'partner_ref', - 10: 'cumul_balance', - 70: 'init_balance', - } - - -class LedgerReportWizard(models.TransientModel): - """Base ledger report wizard.""" - - _name = "ledger.report.wizard" - _description = "Ledger Report Wizard" - - company_id = fields.Many2one(comodel_name='res.company') - date_range_id = fields.Many2one(comodel_name='date.range', required=True) - date_from = fields.Date(required=True) - date_to = fields.Date(required=True) - fy_start_date = fields.Date(required=True) - target_move = fields.Selection([('posted', 'All Posted Entries'), - ('all', 'All Entries')], - string='Target Moves', - required=True, - default='posted') - account_ids = fields.Many2many( - comodel_name='account.account', - string='Filter accounts', - ) - amount_currency = fields.Boolean(string='With currency', - default=False) - centralize = fields.Boolean(string='Activate centralization', - default=False) - result_selection = fields.Selection( - [ - ('customer', 'Receivable Accounts'), - ('supplier', 'Payable Accounts'), - ('customer_supplier', 'Receivable and Payable Accounts'), - ], - string="Partner's", - default='customer') - partner_ids = fields.Many2many( - comodel_name='res.partner', - string='Filter partners', - ) - line_ids = fields.One2many(comodel_name='ledger.report.wizard.line', - inverse_name='wizard_id') - - def _query(self): - """Execute query. - - Short summary: - Prepare all lines for report - by calculating debit/credit amounts - plus the cumulative one. - - Narrow the search by using PG windows. - - Insert all the rows in `ledger_report_wizard_line` - at once, so that we have real model objects - and we can filter/group them in the tree view. - - """ - query = resource_string(__name__, 'ledger.sql') - params = dict(fy_date=self.fy_start_date, wizard_id=self.id, - date_from=self.date_from, date_to=self.date_to) - self.env.cr.execute(query, params) - return True - - @api.multi - def _print_report(self, data): - # we update form with display account value - data = self.pre_print_report(data) - Report = self.env['report'].with_context(landscape=True) - return Report.get_action( - self, 'account.report_generalledger_qweb', - data=data) - - def _build_contexts(self, data): - result = {} - result['journal_ids'] = ( - 'journal_ids' in data['form'] and - data['form']['journal_ids'] or False - ) - result['state'] = ( - 'target_move' in data['form'] and - data['form']['target_move'] or '' - ) - result['date_from'] = data['form']['date_from'] or False - result['date_to'] = data['form']['date_to'] or False - result['strict_range'] = True if result['date_from'] else False - return result - - @api.multi - def button_view(self): - """Open tree view w/ results.""" - return self.process() - - @api.multi - def process(self): - """Process data and return window action.""" - self._query() - - return { - 'domain': [('wizard_id', '=', self.id)], - 'name': _('Ledger lines'), - 'view_type': 'form', - 'view_mode': 'tree', - 'res_model': 'ledger.report.wizard.line', - 'view_id': False, - 'context': { - 'search_default_group_by_account_id': True, - 'search_default_group_by_date': True, - }, - 'type': 'ir.actions.act_window' - } - - @api.onchange('date_range_id') - def onchange_date_range_id(self): - """Handle date range change.""" - self.date_from = self.date_range_id.date_start - self.date_to = self.date_range_id.date_end - if self.date_from: - self.fy_start_date = self.env.user.company_id.find_daterange_fy( - fields.Date.from_string(self.date_range_id.date_start) - ).date_start - - -class LedgerReportWizardLine(models.TransientModel): - """A wizard line. - - Lines are populated on the fly when submitting the wizard. - """ - _name = 'ledger.report.wizard.line' - - wizard_id = fields.Many2one(comodel_name='ledger.report.wizard') - - name = fields.Char() - label = fields.Char() - ref = fields.Char() - date = fields.Date() - month = fields.Char() - partner_name = fields.Char() - partner_ref = fields.Char() - account_id = fields.Many2one('account.account') - account_code = fields.Char() - journal_id = fields.Many2one('account.journal') - partner_id = fields.Many2one('res.partner') - - init_credit = fields.Float() - init_debit = fields.Float() - debit = fields.Float() - credit = fields.Float() - balance = fields.Float() - - cumul_credit = fields.Float() - cumul_debit = fields.Float() - cumul_balance = fields.Float() - - init_balance = fields.Float() - - move_name = fields.Char() - move_state = fields.Char() - invoice_number = fields.Char() - - centralized = fields.Boolean() - - @api.multi - def check_report_xlsx(self): - self.ensure_one() - data = {} - data['ids'] = self.env.context.get('active_ids', []) - # data['model'] = 'general.ledger.line' - data['model'] = self.env.context.get('active_model', 'ir.ui.menu') - data['form'] = self.read(['date_from', 'date_to', - 'journal_ids', 'target_move'])[0] - used_context = self._build_contexts(data) - data['form']['used_context'] = dict( - used_context, - lang=self.env.context.get('lang', 'en_US')) - return self._print_report_xlsx(data) - - @api.multi - def _print_report_xlsx(self, data): - return { - 'name': 'export xlsx general ledger', - 'model': 'ledger.report.wizard', - 'type': 'ir.actions.report.xml', - 'report_name': 'ledger.report.wizard.xlsx', - 'report_type': 'xlsx', - 'context': self.env.context, - } - - @api.multi - def _get_centralized_move_ids(self, domain): - """ Get last line of each selected centralized accounts """ - # inverse search on centralized boolean to finish the search to get the - # ids of last lines of centralized accounts - # XXX USE DISTINCT to speed up ? - domain = domain[:] - centralize_index = domain.index(('centralized', '=', False)) - domain[centralize_index] = ('centralized', '=', True) - - gl_lines = self.env['general.ledger.line'].search(domain) - accounts = gl_lines.mapped('account_id') - - line_ids = [] - for acc in accounts: - acc_lines = gl_lines.filtered(lambda rec: rec.account_id == acc) - line_ids.append(acc_lines[-1].id) - return line_ids - - @api.multi - def _get_moves_from_dates_domain(self): - """ Prepare domain for `_get_moves_from_dates` """ - domain = [] - if self.centralize: - domain = [('centralized', '=', False)] - start_date = self.date_from - end_date = self.date_to - if start_date: - domain += [('date', '>=', start_date)] - if end_date: - domain += [('date', '<=', end_date)] - - if self.target_move == 'posted': - domain += [('move_state', '=', 'posted')] - - if self.account_ids: - domain += [('account_id', 'in', self.account_ids.ids)] - - return domain - - def compute_domain(self): - ret = self._get_moves_from_dates_domain() - if self.centralize: - centralized_ids = self._get_centralized_move_ids(ret) - if centralized_ids: - ret.insert(0, '|') - ret.append(('id', 'in', centralized_ids)) - return ret - - def initial_balance_line(self, amount, account_name, account_code, date): - return {'date': date, - 'account_id': account_name, - 'account_code': account_code, - 'move_name': '', - 'journal_id': '', - 'partner_name': '', - 'ref': '', - 'label': _('Initial Balance'), - 'debit': '', - 'credit': '', - 'amount_currency': '', - 'currency_code': '', - 'month': '', - 'partner_ref': '', - 'cumul_balance': amount, - 'init_balance': ''} - - def group_general_ledger(self, report_lines, date_start): - """ - group lines by account and order by account then date - """ - result = {} - accounts = report_lines.mapped('account_id') - for account in accounts: - lines = report_lines.filtered( - lambda a: a.account_id.id == account.id) - acc_full_name = account.name_get()[0][1] - sorted_lines = sorted(lines.read(FIELDS_TO_READ.values()), - key=itemgetter('date')) - initial_balance = sorted_lines[0]['init_balance'] - sorted_lines.insert(0, self.initial_balance_line(initial_balance, - acc_full_name, - account.code, - date_start)) - result[acc_full_name] = sorted_lines - - return result - - def construct_header(self): - result = {} - - result['title'] = _('General Ledger') - filters = {} - - filters['centralized'] = _('%s' % self.centralize) - filters['start_date'] = self.date_from - filters['end_date'] = self.date_to - - filters['target_moves'] = self.target_move - - filters['accounts'] = _('All') - if self.account_ids: - filters['accounts'] = ', '.join([a.code for a in self.account_ids]) - - result['filters'] = filters - - return result - - @api.multi - def compute(self): - self.ensure_one() - # header filled with a dict - header = [] - header.append(self.construct_header()) - # content filled with dicts - content = [] - - domain = self.compute_domain() - report_lines = self.env['general.ledger.line'].search(domain) - lines_general_ledger = self.group_general_ledger(report_lines, - self.date_from) - content.append(lines_general_ledger) - return {'header': header, - 'content': content} diff --git a/account_financial_report_qweb/wizard/partner_ledger_wizard.xml b/account_financial_report_qweb/wizard/partner_ledger_wizard.xml deleted file mode 100644 index c6b920d9..00000000 --- a/account_financial_report_qweb/wizard/partner_ledger_wizard.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - Partner Ledger - ledger.report.wizard - -
- - - - - - - - - - - - - - - - - - - - - -
- - - Partner Ledger - ir.actions.act_window - ledger.report.wizard - form - form - - new - - -
-