diff --git a/account_financial_report/__manifest__.py b/account_financial_report/__manifest__.py index aaaef0b6..416b42b2 100644 --- a/account_financial_report/__manifest__.py +++ b/account_financial_report/__manifest__.py @@ -1,16 +1,17 @@ # Author: Damien Crier # Author: Julien Coux # Copyright 2016 Camptocamp SA +# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': 'Account Financial Reports', - 'version': '12.0.1.4.1', + 'version': '12.0.2.0.0', 'category': 'Reporting', 'summary': 'OCA Financial Reports', 'author': 'Camptocamp SA,' 'initOS GmbH,' 'redCOR AG,' - 'Eficent,' + 'ForgeFlow,' 'Odoo Community Association (OCA)', "website": "https://odoo-community.org/", 'depends': [ @@ -43,6 +44,11 @@ 'view/report_aged_partner_balance.xml', 'view/report_vat_report.xml', ], + "external_dependencies": { + "python": ['natsort', + 'pandas', + ], + }, 'installable': True, 'application': True, 'auto_install': False, diff --git a/account_financial_report/models/account_group.py b/account_financial_report/models/account_group.py index bbc3b0b3..1561022f 100644 --- a/account_financial_report/models/account_group.py +++ b/account_financial_report/models/account_group.py @@ -23,6 +23,28 @@ class AccountGroup(models.Model): 'account.account', compute='_compute_group_accounts', string="Compute accounts", store=True) + complete_name = fields.Char("Full Name", + compute='_compute_complete_name') + complete_code = fields.Char("Full Code", + compute='_compute_complete_code') + + @api.depends('name', 'parent_id.complete_name') + def _compute_complete_name(self): + """ Forms complete name of location from parent location to child location. """ + if self.parent_id.complete_name: + self.complete_name = '%s/%s' % (self.parent_id.complete_name, + self.name) + else: + self.complete_name = self.name + + @api.depends('code_prefix', 'parent_id.complete_code') + def _compute_complete_code(self): + """ Forms complete code of location from parent location to child location. """ + if self.parent_id.complete_code: + self.complete_code = '%s/%s' % (self.parent_id.complete_code, + self.code_prefix) + else: + self.complete_code = self.code_prefix @api.multi @api.depends('parent_id', 'parent_id.level') diff --git a/account_financial_report/readme/CONTRIBUTORS.rst b/account_financial_report/readme/CONTRIBUTORS.rst index 4d06ad1a..97ae3190 100644 --- a/account_financial_report/readme/CONTRIBUTORS.rst +++ b/account_financial_report/readme/CONTRIBUTORS.rst @@ -1,4 +1,4 @@ -* Jordi Ballester +* Jordi Ballester * Yannick Vaucher * Simone Orsi * Leonardo Pistone @@ -14,7 +14,8 @@ * Akim Juillerat * Alexis de Lattre * Mihai Fekete -* Miquel Raïch +* Miquel Raïch +* Joan Sisquella * `Tecnativa `__: * Pedro M. Baeza diff --git a/account_financial_report/report/__init__.py b/account_financial_report/report/__init__.py index ae2ee3d5..41936758 100644 --- a/account_financial_report/report/__init__.py +++ b/account_financial_report/report/__init__.py @@ -3,7 +3,6 @@ # © 2016 Julien Coux (Camptocamp) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).- -from . import abstract_report from . import abstract_report_xlsx from . import aged_partner_balance from . import aged_partner_balance_xlsx diff --git a/account_financial_report/report/abstract_report.py b/account_financial_report/report/abstract_report.py deleted file mode 100644 index db9afeee..00000000 --- a/account_financial_report/report/abstract_report.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2018 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo import models -from psycopg2.extensions import AsIs - - -class AbstractReport(models.AbstractModel): - _name = 'account_financial_report_abstract' - _description = 'Abstract Report' - - def _transient_clean_rows_older_than(self, seconds): - assert self._transient, \ - "Model %s is not transient, it cannot be vacuumed!" % self._name - # Never delete rows used in last 5 minutes - seconds = max(seconds, 300) - query = ( - "DELETE FROM %s" - " WHERE COALESCE(" - "write_date, create_date, (now() at time zone 'UTC'))" - "::timestamp < ((now() at time zone 'UTC') - interval %s)" - ) - self.env.cr.execute(query, (AsIs(self._table), "%s seconds" % seconds)) diff --git a/account_financial_report/report/abstract_report_xlsx.py b/account_financial_report/report/abstract_report_xlsx.py index d4a867c6..09147468 100644 --- a/account_financial_report/report/abstract_report_xlsx.py +++ b/account_financial_report/report/abstract_report_xlsx.py @@ -1,4 +1,3 @@ - # Author: Julien Coux # Copyright 2016 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). @@ -44,7 +43,7 @@ class AbstractReportXslx(models.AbstractModel): self._define_formats(workbook) - report_name = self._get_report_name(report) + report_name = self._get_report_name(report, data=data) report_footer = self._get_report_footer() filters = self._get_report_filters(report) self.columns = self._get_report_columns(report) @@ -57,14 +56,13 @@ class AbstractReportXslx(models.AbstractModel): self._write_filters(filters) - self._generate_report_content(workbook, report) + self._generate_report_content(workbook, report, data) self._write_report_footer(report_footer) def _define_formats(self, workbook): """ Add cell formats to current workbook. Those formats can be used on all cell. - Available formats are : * format_bold * format_right @@ -221,10 +219,49 @@ class AbstractReportXslx(models.AbstractModel): ) self.row_pos += 1 + def write_line_from_dict(self, line_dict): + """Write a line on current line + """ + for col_pos, column in self.columns.items(): + value = line_dict.get(column['field'], False) + cell_type = column.get('type', 'string') + if cell_type == 'string': + if (line_dict.get('account_group_id', False) and + line_dict['account_group_id']): + self.sheet.write_string( + self.row_pos, col_pos, value or '', + self.format_bold) + else: + if not isinstance(value, str) and \ + not isinstance(value, bool) and \ + not isinstance(value, int): + value = value.strftime("%d/%m/%Y") + self.sheet.write_string( + self.row_pos, col_pos, value or '') + elif cell_type == 'amount': + if line_dict.get('account_group_id', False) and \ + line_dict['account_group_id']: + cell_format = self.format_amount_bold + else: + cell_format = self.format_amount + self.sheet.write_number( + self.row_pos, col_pos, float(value), cell_format + ) + elif cell_type == 'amount_currency': + if line_dict.get('currency_name', False): + format_amt = self._get_currency_amt_format_dict( + line_dict) + self.sheet.write_number( + self.row_pos, col_pos, float(value), format_amt + ) + elif cell_type == 'currency_name': + self.sheet.write_string( + self.row_pos, col_pos, value or '', self.format_right) + self.row_pos += 1 + def write_initial_balance(self, my_object, label): """Write a specific initial balance line on current line using defined columns field_initial_balance name. - Columns are defined with `_get_report_columns` method. """ col_pos_label = self._get_col_pos_initial_balance_label() @@ -259,10 +296,46 @@ class AbstractReportXslx(models.AbstractModel): ) self.row_pos += 1 + def write_initial_balance_from_dict(self, my_object, label): + """Write a specific initial balance line on current line + using defined columns field_initial_balance name. + Columns are defined with `_get_report_columns` method. + """ + col_pos_label = self._get_col_pos_initial_balance_label() + self.sheet.write(self.row_pos, col_pos_label, label, self.format_right) + for col_pos, column in self.columns.items(): + if column.get('field_initial_balance'): + value = my_object.get(column['field_initial_balance'], False) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, value or '') + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), self.format_amount + ) + elif cell_type == 'amount_currency': + if my_object['currency_id']: + format_amt = self._get_currency_amt_format( + my_object) + self.sheet.write_number( + self.row_pos, col_pos, + float(value), format_amt + ) + elif column.get('field_currency_balance'): + value = my_object.get(column['field_currency_balance'], False) + cell_type = column.get('type', 'string') + if cell_type == 'many2one': + if my_object['currency_id']: + self.sheet.write_string( + self.row_pos, col_pos, + value.name or '', + self.format_right + ) + self.row_pos += 1 + def write_ending_balance(self, my_object, name, label): """Write a specific ending balance line on current line using defined columns field_final_balance name. - Columns are defined with `_get_report_columns` method. """ for i in range(0, len(self.columns)): @@ -307,6 +380,54 @@ class AbstractReportXslx(models.AbstractModel): ) self.row_pos += 1 + def write_ending_balance_from_dict(self, my_object, name, label): + """Write a specific ending balance line on current line + using defined columns field_final_balance name. + Columns are defined with `_get_report_columns` method. + """ + for i in range(0, len(self.columns)): + self.sheet.write(self.row_pos, i, '', self.format_header_right) + row_count_name = self._get_col_count_final_balance_name() + col_pos_label = self._get_col_pos_final_balance_label() + self.sheet.merge_range( + self.row_pos, 0, self.row_pos, row_count_name - 1, name, + self.format_header_left + ) + self.sheet.write(self.row_pos, col_pos_label, label, + self.format_header_right) + for col_pos, column in self.columns.items(): + if column.get('field_final_balance'): + value = my_object.get(column['field_final_balance'], False) + cell_type = column.get('type', 'string') + if cell_type == 'string': + self.sheet.write_string(self.row_pos, col_pos, + value or '', + self.format_header_right) + elif cell_type == 'amount': + self.sheet.write_number( + self.row_pos, col_pos, float(value), + self.format_header_amount + ) + elif cell_type == 'amount_currency': + if my_object['currency_id'] and value: + format_amt = self._get_currency_amt_format_dict( + my_object) + self.sheet.write_number( + self.row_pos, col_pos, float(value), + format_amt + ) + elif column.get('field_currency_balance'): + value = my_object.get(column['field_currency_balance'], False) + cell_type = column.get('type', 'string') + if cell_type == 'many2one': + if my_object['currency_id']: + self.sheet.write_string( + self.row_pos, col_pos, + value or '', + self.format_header_right + ) + self.row_pos += 1 + def _get_currency_amt_format(self, line_object): """ Return amount format specific for each currency. """ if hasattr(line_object, 'account_group_id') and \ @@ -329,6 +450,30 @@ class AbstractReportXslx(models.AbstractModel): format_amt.set_num_format(format_amount) return format_amt + def _get_currency_amt_format_dict(self, line_dict): + """ Return amount format specific for each currency. """ + if line_dict.get('account_group_id', False) and \ + line_dict['account_group_id']: + format_amt = getattr(self, 'format_amount_bold') + field_prefix = 'format_amount_bold' + else: + format_amt = getattr(self, 'format_amount') + field_prefix = 'format_amount' + if line_dict.get('currency_id', False) and line_dict['currency_id']: + currency = self.env['res.currency'].browse( + [line_dict['currency_id']]) + field_name = \ + '%s_%s' % (field_prefix, currency.name) + if hasattr(self, field_name): + format_amt = getattr(self, field_name) + else: + format_amt = self.workbook.add_format() + setattr(self, 'field_name', format_amt) + format_amount = \ + '#,##0.' + ('0' * currency.decimal_places) + format_amt.set_num_format(format_amount) + return format_amt + def _get_currency_amt_header_format(self, line_object): """ Return amount header format for each currency. """ format_amt = getattr(self, 'format_header_amount') @@ -348,24 +493,42 @@ class AbstractReportXslx(models.AbstractModel): format_amt.set_num_format(format_amount) return format_amt - def _generate_report_content(self, workbook, report): + def _get_currency_amt_header_format_dict(self, line_object): + """ Return amount header format for each currency. """ + format_amt = getattr(self, 'format_header_amount') + if line_object['currency_id']: + field_name = \ + 'format_header_amount_%s' % line_object['currency_name'] + if hasattr(self, field_name): + format_amt = getattr(self, field_name) + else: + format_amt = self.workbook.add_format( + {'bold': True, + 'border': True, + 'bg_color': '#FFFFCC'}) + setattr(self, 'field_name', format_amt) + format_amount = \ + '#,##0.' + ('0' * line_object['currency_id'].decimal_places) + format_amt.set_num_format(format_amount) + return format_amt + + def _generate_report_content(self, workbook, report, data): """ Allow to fetch report content to be displayed. """ raise NotImplementedError() - def _get_report_complete_name(self, report, prefix): + def _get_report_complete_name(self, report, prefix, data=None): if report.company_id: suffix = ' - %s - %s' % ( report.company_id.name, report.company_id.currency_id.name) return prefix + suffix return prefix - def _get_report_name(self, report): + def _get_report_name(self, report, data=False): """ Allow to define the report name. Report name will be used as sheet name and as report title. - :return: the report name """ raise NotImplementedError() @@ -381,11 +544,8 @@ class AbstractReportXslx(models.AbstractModel): """ Allow to define the report columns which will be used to generate report. - :return: the report columns as dict - :Example: - { 0: {'header': 'Simple column', 'field': 'field_name_on_my_object', @@ -401,9 +561,7 @@ class AbstractReportXslx(models.AbstractModel): def _get_report_filters(self, report): """ :return: the report filters as list - :Example: - [ ['first_filter_name', 'first_filter_value'], ['second_filter_name', 'second_filter_value'] diff --git a/account_financial_report/report/aged_partner_balance.py b/account_financial_report/report/aged_partner_balance.py index 3a0b25f4..f715b55a 100644 --- a/account_financial_report/report/aged_partner_balance.py +++ b/account_financial_report/report/aged_partner_balance.py @@ -1,633 +1,375 @@ # © 2016 Julien Coux (Camptocamp) +# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api +from odoo import models, api +from odoo.tools import float_is_zero +from datetime import date, datetime, timedelta +import pandas as pd -class AgedPartnerBalanceReport(models.TransientModel): - """ Here, we just define class fields. - For methods, go more bottom at this file. - - The class hierarchy is : - * AgedPartnerBalanceReport - ** AgedPartnerBalanceReportAccount - *** AgedPartnerBalanceReportPartner - **** AgedPartnerBalanceReportLine - **** AgedPartnerBalanceReportMoveLine - If "show_move_line_details" is selected - """ - - _name = 'report_aged_partner_balance' - _inherit = 'account_financial_report_abstract' - - # Filters fields, used for data computation - date_at = fields.Date() - only_posted_moves = 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') - show_move_line_details = fields.Boolean() - - # Open Items Report Data fields, used as base for compute the data reports - open_items_id = fields.Many2one(comodel_name='report_open_items') - - # Data fields, used to browse report data - account_ids = fields.One2many( - comodel_name='report_aged_partner_balance_account', - inverse_name='report_id' - ) - - -class AgedPartnerBalanceReportAccount(models.TransientModel): - _name = 'report_aged_partner_balance_account' - _inherit = 'account_financial_report_abstract' - _order = 'code ASC' - - report_id = fields.Many2one( - comodel_name='report_aged_partner_balance', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - account_id = fields.Many2one( - 'account.account', - index=True - ) - - # Data fields, used for report display - code = fields.Char() - name = fields.Char() - - cumul_amount_residual = fields.Float(digits=(16, 2)) - cumul_current = fields.Float(digits=(16, 2)) - cumul_age_30_days = fields.Float(digits=(16, 2)) - cumul_age_60_days = fields.Float(digits=(16, 2)) - cumul_age_90_days = fields.Float(digits=(16, 2)) - cumul_age_120_days = fields.Float(digits=(16, 2)) - cumul_older = fields.Float(digits=(16, 2)) - - percent_current = fields.Float(digits=(16, 2)) - percent_age_30_days = fields.Float(digits=(16, 2)) - percent_age_60_days = fields.Float(digits=(16, 2)) - percent_age_90_days = fields.Float(digits=(16, 2)) - percent_age_120_days = fields.Float(digits=(16, 2)) - percent_older = fields.Float(digits=(16, 2)) - - # Data fields, used to browse report data - partner_ids = fields.One2many( - comodel_name='report_aged_partner_balance_partner', - inverse_name='report_account_id' - ) - - -class AgedPartnerBalanceReportPartner(models.TransientModel): - _name = 'report_aged_partner_balance_partner' - _inherit = 'account_financial_report_abstract' - - report_account_id = fields.Many2one( - comodel_name='report_aged_partner_balance_account', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - partner_id = fields.Many2one( - 'res.partner', - index=True - ) - - # Data fields, used for report display - name = fields.Char() - - # Data fields, used to browse report data - move_line_ids = fields.One2many( - comodel_name='report_aged_partner_balance_move_line', - inverse_name='report_partner_id' - ) - line_ids = fields.One2many( - comodel_name='report_aged_partner_balance_line', - inverse_name='report_partner_id' - ) +class AgedPartnerBalanceReport(models.AbstractModel): + _name = 'report.account_financial_report.aged_partner_balance' + _description = "Aged Partner Balance Report" @api.model - def _generate_order_by(self, order_spec, query): - """Custom order to display "No partner allocated" at last position.""" - return """ -ORDER BY - CASE - WHEN - "report_aged_partner_balance_partner"."partner_id" IS NOT NULL - THEN 0 - ELSE 1 - END, - "report_aged_partner_balance_partner"."name" - """ - - -class AgedPartnerBalanceReportLine(models.TransientModel): - _name = 'report_aged_partner_balance_line' - _inherit = 'account_financial_report_abstract' - - report_partner_id = fields.Many2one( - comodel_name='report_aged_partner_balance_partner', - ondelete='cascade', - index=True - ) - - # Data fields, used for report display - partner = fields.Char() - amount_residual = fields.Float(digits=(16, 2)) - current = fields.Float(digits=(16, 2)) - age_30_days = fields.Float(digits=(16, 2)) - age_60_days = fields.Float(digits=(16, 2)) - age_90_days = fields.Float(digits=(16, 2)) - age_120_days = fields.Float(digits=(16, 2)) - older = fields.Float(digits=(16, 2)) - - -class AgedPartnerBalanceReportMoveLine(models.TransientModel): - _name = 'report_aged_partner_balance_move_line' - _inherit = 'account_financial_report_abstract' - - report_partner_id = fields.Many2one( - comodel_name='report_aged_partner_balance_partner', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - move_line_id = fields.Many2one('account.move.line') - - # Data fields, used for report display - date = fields.Date() - date_due = fields.Date() - entry = fields.Char() - journal = fields.Char() - account = fields.Char() - partner = fields.Char() - label = fields.Char() + def _initialize_account(self, ag_pb_data, acc_id): + ag_pb_data[acc_id] = {} + ag_pb_data[acc_id]['id'] = acc_id + ag_pb_data[acc_id]['residual'] = 0.0 + ag_pb_data[acc_id]['current'] = 0.0 + ag_pb_data[acc_id]['30_days'] = 0.0 + ag_pb_data[acc_id]['60_days'] = 0.0 + ag_pb_data[acc_id]['90_days'] = 0.0 + ag_pb_data[acc_id]['120_days'] = 0.0 + ag_pb_data[acc_id]['older'] = 0.0 + return ag_pb_data - amount_residual = fields.Float(digits=(16, 2)) - current = fields.Float(digits=(16, 2)) - age_30_days = fields.Float(digits=(16, 2)) - age_60_days = fields.Float(digits=(16, 2)) - age_90_days = fields.Float(digits=(16, 2)) - age_120_days = fields.Float(digits=(16, 2)) - older = fields.Float(digits=(16, 2)) - - -class AgedPartnerBalanceReportCompute(models.TransientModel): - """ Here, we just define methods. - For class fields, go more top at this file. - """ + @api.model + def _initialize_partner(self, ag_pb_data, acc_id, prt_id): + ag_pb_data[acc_id][prt_id] = {} + ag_pb_data[acc_id][prt_id]['id'] = acc_id + ag_pb_data[acc_id][prt_id]['residual'] = 0.0 + ag_pb_data[acc_id][prt_id]['current'] = 0.0 + ag_pb_data[acc_id][prt_id]['30_days'] = 0.0 + ag_pb_data[acc_id][prt_id]['60_days'] = 0.0 + ag_pb_data[acc_id][prt_id]['90_days'] = 0.0 + ag_pb_data[acc_id][prt_id]['120_days'] = 0.0 + ag_pb_data[acc_id][prt_id]['older'] = 0.0 + ag_pb_data[acc_id][prt_id]['move_lines'] = [] + return ag_pb_data + + def _get_journals_data(self, journals_ids): + journals = self.env['account.journal'].browse(journals_ids) + journals_data = {} + for journal in journals: + journals_data.update({journal.id: {'id': journal.id, + 'code': journal.code}}) + return journals_data + + def _get_accounts_data(self, accounts_ids): + accounts = self.env['account.account'].browse(accounts_ids) + accounts_data = {} + for account in accounts: + accounts_data.update({account.id: {'id': account.id, + 'code': account.code, + 'name': account.name}}) + return accounts_data - _inherit = 'report_aged_partner_balance' + @api.model + def _get_move_lines_domain(self, company_id, account_ids, partner_ids, + only_posted_moves): + domain = [('account_id', 'in', account_ids), + ('company_id', '=', company_id), + ('reconciled', '=', False)] + if partner_ids: + domain += [('partner_id', 'in', partner_ids)] + if only_posted_moves: + domain += [('move_id.state', '=', 'posted')] + return domain - @api.multi - def print_report(self, report_type): - self.ensure_one() - if report_type == 'xlsx': - report_name = 'a_f_r.report_aged_partner_balance_xlsx' + @api.model + def _calculate_amounts(self, ag_pb_data, acc_id, prt_id, residual, + due_date, date_at_object): + ag_pb_data[acc_id]['residual'] += residual + ag_pb_data[acc_id][prt_id]['residual'] += residual + today = date_at_object + if not due_date or today <= due_date: + ag_pb_data[acc_id]['current'] += residual + ag_pb_data[acc_id][prt_id]['current'] += residual + elif today <= due_date + timedelta(days=30): + ag_pb_data[acc_id]['30_days'] += residual + ag_pb_data[acc_id][prt_id]['30_days'] += residual + elif today <= due_date + timedelta(days=60): + ag_pb_data[acc_id]['60_days'] += residual + ag_pb_data[acc_id][prt_id]['60_days'] += residual + elif today <= due_date + timedelta(days=90): + ag_pb_data[acc_id]['90_days'] += residual + ag_pb_data[acc_id][prt_id]['90_days'] += residual + elif today <= due_date + timedelta(days=120): + ag_pb_data[acc_id]['120_days'] += residual + ag_pb_data[acc_id][prt_id]['120_days'] += residual else: - report_name = 'account_financial_report.' \ - 'report_aged_partner_balance_qweb' - report = self.env['ir.actions.report'].search( - [('report_name', '=', report_name), - ('report_type', '=', report_type)], limit=1) - return report.report_action(self, config=False) - - def _get_html(self): - result = {} - rcontext = {} - context = dict(self.env.context) - report = self.browse(context.get('active_id')) - if report: - rcontext['o'] = report - result['html'] = self.env.ref( - 'account_financial_report.report_aged_partner_balance').render( - rcontext) - return result + ag_pb_data[acc_id]['older'] += residual + ag_pb_data[acc_id][prt_id]['older'] += residual + return ag_pb_data + + def _get_account_partial_reconciled(self, company_id, date_at_object): + domain = [('max_date', '>=', date_at_object), + ('company_id', '=', company_id)] + fields = ['debit_move_id', 'credit_move_id', 'amount'] + accounts_partial_reconcile = \ + self.env['account.partial.reconcile'].search_read( + domain=domain, + fields=fields + ) + debit_amount = {} + credit_amount = {} + for account_partial_reconcile_data in accounts_partial_reconcile: + debit_move_id = account_partial_reconcile_data['debit_move_id'][0] + credit_move_id = account_partial_reconcile_data['credit_move_id'][0] + if debit_move_id not in debit_amount.keys(): + debit_amount[debit_move_id] = 0.0 + debit_amount[debit_move_id] += \ + account_partial_reconcile_data['amount'] + if credit_move_id not in credit_amount.keys(): + credit_amount[credit_move_id] = 0.0 + credit_amount[credit_move_id] += \ + account_partial_reconcile_data['amount'] + account_partial_reconcile_data.update({ + 'debit_move_id': debit_move_id, + 'credit_move_id': credit_move_id, + }) + return accounts_partial_reconcile, debit_amount, credit_amount @api.model - def get_html(self, given_context=None): - return self._get_html() - - def _prepare_report_open_items(self): - self.ensure_one() - return { - 'date_at': self.date_at, - 'only_posted_moves': self.only_posted_moves, - 'company_id': self.company_id.id, - 'filter_account_ids': [(6, 0, self.filter_account_ids.ids)], - 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)], - } - - @api.multi - def compute_data_for_report(self): - self.ensure_one() - # Compute Open Items Report Data. - # The data of Aged Partner Balance Report - # are based on Open Items Report data. - model = self.env['report_open_items'] - self.open_items_id = model.create(self._prepare_report_open_items()) - self.open_items_id.compute_data_for_report() - - # Compute report data - self._inject_account_values() - self._inject_partner_values() - self._inject_line_values() - self._inject_line_values(only_empty_partner_line=True) - if self.show_move_line_details: - self._inject_move_line_values() - self._inject_move_line_values(only_empty_partner_line=True) - self._compute_accounts_cumul() - # Refresh cache because all data are computed with SQL requests - self.invalidate_cache() - - def _inject_account_values(self): - """Inject report values for report_aged_partner_balance_account""" - query_inject_account = """ -INSERT INTO - report_aged_partner_balance_account - ( - report_id, - create_uid, - create_date, - account_id, - code, - name - ) -SELECT - %s AS report_id, - %s AS create_uid, - NOW() AS create_date, - rao.account_id, - rao.code, - rao.name -FROM - report_open_items_account rao -WHERE - rao.report_id = %s - """ - query_inject_account_params = ( - self.id, - self.env.uid, - self.open_items_id.id, + def _get_new_move_lines_domain(self, new_ml_ids, account_ids, company_id, + partner_ids, only_posted_moves): + domain = [('account_id', 'in', account_ids), + ('company_id', '=', company_id), + ('id', 'in', new_ml_ids)] + if partner_ids: + domain += [('partner_id', 'in', partner_ids)] + if only_posted_moves: + domain += [('move_id.state', '=', 'posted')] + return domain + + def _recalculate_move_lines(self, move_lines, debit_ids, credit_ids, + debit_amount, credit_amount, ml_ids, + account_ids, company_id, partner_ids, + only_posted_moves): + reconciled_ids = list(debit_ids) + list(credit_ids) + new_ml_ids = [] + for reconciled_id in reconciled_ids: + if reconciled_id not in ml_ids and reconciled_id not in new_ml_ids: + new_ml_ids += [reconciled_id] + new_domain = self._get_new_move_lines_domain(new_ml_ids, account_ids, + company_id, partner_ids, + only_posted_moves) + ml_fields = [ + 'id', 'name', 'date', 'move_id', 'journal_id', 'account_id', + 'partner_id', 'amount_residual', 'date_maturity', 'ref', + 'reconciled'] + new_move_lines = self.env['account.move.line'].search_read( + domain=new_domain, fields=ml_fields ) - self.env.cr.execute(query_inject_account, query_inject_account_params) - - def _inject_partner_values(self): - """Inject report values for report_aged_partner_balance_partner""" - query_inject_partner = """ -INSERT INTO - report_aged_partner_balance_partner - ( - report_account_id, - create_uid, - create_date, - partner_id, - name - ) -SELECT - ra.id AS report_account_id, - %s AS create_uid, - NOW() AS create_date, - rpo.partner_id, - rpo.name -FROM - report_open_items_partner rpo -INNER JOIN - report_open_items_account rao ON rpo.report_account_id = rao.id -INNER JOIN - report_aged_partner_balance_account ra ON rao.code = ra.code -WHERE - rao.report_id = %s -AND ra.report_id = %s - """ - query_inject_partner_params = ( - self.env.uid, - self.open_items_id.id, - self.id, + move_lines = move_lines + new_move_lines + for move_line in move_lines: + ml_id = move_line['id'] + if ml_id in debit_ids: + move_line['amount_residual'] += debit_amount[ml_id] + if ml_id in credit_ids: + move_line['amount_residual'] -= credit_amount[ml_id] + return move_lines + + def _get_move_lines_data( + self, company_id, account_ids, partner_ids, date_at_object, + only_posted_moves, show_move_line_details): + domain = self._get_move_lines_domain(company_id, account_ids, + partner_ids, only_posted_moves) + ml_fields = [ + 'id', 'name', 'date', 'move_id', 'journal_id', 'account_id', + 'partner_id', 'amount_residual', 'date_maturity', 'ref', + 'reconciled'] + move_lines = self.env['account.move.line'].search_read( + domain=domain, fields=ml_fields ) - self.env.cr.execute(query_inject_partner, query_inject_partner_params) - - def _inject_line_values(self, only_empty_partner_line=False): - """ Inject report values for report_aged_partner_balance_line. + ml_ids = set(pd.DataFrame(move_lines).id.to_list()) + journals_ids = set() + partners_ids = set() + partners_data = {} + ag_pb_data = {} + if date_at_object < date.today(): + acc_partial_rec, debit_amount, credit_amount = \ + self._get_account_partial_reconciled(company_id, date_at_object) + if acc_partial_rec: + acc_partial_rec_data = pd.DataFrame(acc_partial_rec) + debit_ids = set(acc_partial_rec_data.debit_move_id.to_list()) + credit_ids = set(acc_partial_rec_data.credit_move_id.to_list()) + move_lines = self._recalculate_move_lines( + move_lines, debit_ids, credit_ids, + debit_amount, credit_amount, ml_ids, account_ids, + company_id, partner_ids, only_posted_moves + ) + moves_lines_to_remove = [] + for move_line in move_lines: + if move_line['date'] > date_at_object or \ + float_is_zero(move_line['amount_residual'], + precision_digits=2): + moves_lines_to_remove.append(move_line) + if len(moves_lines_to_remove) > 0: + for move_line_to_remove in moves_lines_to_remove: + move_lines.remove(move_line_to_remove) + for move_line in move_lines: + journals_ids.add(move_line['journal_id'][0]) + acc_id = move_line['account_id'][0] + if move_line['partner_id']: + prt_id = move_line['partner_id'][0] + prt_name = move_line['partner_id'][1] + else: + prt_id = 0 + prt_name = "" + if prt_id not in partners_ids: + partners_data.update({ + prt_id: {'id': prt_id, 'name': prt_name} + }) + partners_ids.add(prt_id) + if acc_id not in ag_pb_data.keys(): + ag_pb_data = self._initialize_account(ag_pb_data, acc_id) + if prt_id not in ag_pb_data[acc_id]: + ag_pb_data = self._initialize_partner(ag_pb_data, acc_id, + prt_id) + move_line_data = {} + if show_move_line_details: + move_line_data.update({ + 'date': move_line['date'], + 'entry': move_line['move_id'][1], + 'jnl_id': move_line['journal_id'][0], + 'acc_id': acc_id, + 'partner': prt_name, + 'ref': move_line['ref'], + 'due_date': move_line['date_maturity'], + 'residual': move_line['amount_residual'], + }) + ag_pb_data[acc_id][prt_id]['move_lines'].append(move_line_data) + ag_pb_data = self._calculate_amounts( + ag_pb_data, acc_id, prt_id, move_line['amount_residual'], + move_line['date_maturity'], date_at_object) + journals_data = self._get_journals_data(list(journals_ids)) + accounts_data = self._get_accounts_data(ag_pb_data.keys()) + return ag_pb_data, accounts_data, partners_data, journals_data - The "only_empty_partner_line" value is used - to compute data without partner. - """ - query_inject_line = """ -WITH - date_range AS - ( - SELECT - DATE %s AS date_current, - DATE %s - INTEGER '30' AS date_less_30_days, - DATE %s - INTEGER '60' AS date_less_60_days, - DATE %s - INTEGER '90' AS date_less_90_days, - DATE %s - INTEGER '120' AS date_less_120_days - ) -INSERT INTO - report_aged_partner_balance_line - ( - report_partner_id, - create_uid, - create_date, - partner, - amount_residual, - current, - age_30_days, - age_60_days, - age_90_days, - age_120_days, - older - ) -SELECT - rp.id AS report_partner_id, - %s AS create_uid, - NOW() AS create_date, - rp.name, - SUM(rlo.amount_residual) AS amount_residual, - SUM( - CASE - WHEN rlo.date_due >= date_range.date_current - THEN rlo.amount_residual - END - ) AS current, - SUM( - CASE - WHEN - rlo.date_due >= date_range.date_less_30_days - AND rlo.date_due < date_range.date_current - THEN rlo.amount_residual - END - ) AS age_30_days, - SUM( - CASE - WHEN - rlo.date_due >= date_range.date_less_60_days - AND rlo.date_due < date_range.date_less_30_days - THEN rlo.amount_residual - END - ) AS age_60_days, - SUM( - CASE - WHEN - rlo.date_due >= date_range.date_less_90_days - AND rlo.date_due < date_range.date_less_60_days - THEN rlo.amount_residual - END - ) AS age_90_days, - SUM( - CASE - WHEN - rlo.date_due >= date_range.date_less_120_days - AND rlo.date_due < date_range.date_less_90_days - THEN rlo.amount_residual - END - ) AS age_120_days, - SUM( - CASE - WHEN rlo.date_due < date_range.date_less_120_days - THEN rlo.amount_residual - END - ) AS older -FROM - date_range, - report_open_items_move_line rlo -INNER JOIN - report_open_items_partner rpo ON rlo.report_partner_id = rpo.id -INNER JOIN - report_open_items_account rao ON rpo.report_account_id = rao.id -INNER JOIN - report_aged_partner_balance_account ra ON rao.code = ra.code -INNER JOIN - report_aged_partner_balance_partner rp - ON - ra.id = rp.report_account_id - """ - if not only_empty_partner_line: - query_inject_line += """ - AND rpo.partner_id = rp.partner_id - """ - elif only_empty_partner_line: - query_inject_line += """ - AND rpo.partner_id IS NULL - AND rp.partner_id IS NULL - """ - query_inject_line += """ -WHERE - rao.report_id = %s -AND ra.report_id = %s -GROUP BY - rp.id - """ - query_inject_line_params = (self.date_at,) * 5 - query_inject_line_params += ( - self.env.uid, - self.open_items_id.id, - self.id, - ) - self.env.cr.execute(query_inject_line, query_inject_line_params) - - def _inject_move_line_values(self, only_empty_partner_line=False): - """ Inject report values for report_aged_partner_balance_move_line + @api.model + def _compute_maturity_date(self, ml, date_at_object): + ml.update({ + 'current': 0.0, + '30_days': 0.0, + '60_days': 0.0, + '90_days': 0.0, + '120_days': 0.0, + 'older': 0.0, + }) + due_date = ml['due_date'] + amount = ml['residual'] + today = date_at_object + if not due_date or today <= due_date: + ml['current'] += amount + elif today <= due_date + timedelta(days=30): + ml['30_days'] += amount + elif today <= due_date + timedelta(days=60): + ml['60_days'] += amount + elif today <= due_date + timedelta(days=90): + ml['90_days'] += amount + elif today <= due_date + timedelta(days=120): + ml['120_days'] += amount + else: + ml['older'] += amount + + def _create_account_list( + self, ag_pb_data, accounts_data, partners_data, journals_data, + show_move_line_details, date_at_oject): + aged_partner_data = [] + for account in accounts_data.values(): + acc_id = account['id'] + account.update({ + 'residual': ag_pb_data[acc_id]['residual'], + 'current': ag_pb_data[acc_id]['current'], + '30_days': ag_pb_data[acc_id]['30_days'], + '60_days': ag_pb_data[acc_id]['60_days'], + '90_days': ag_pb_data[acc_id]['90_days'], + '120_days': ag_pb_data[acc_id]['120_days'], + 'older': ag_pb_data[acc_id]['older'], + 'partners': [], + }) + for prt_id in ag_pb_data[acc_id]: + if isinstance(prt_id, int): + partner = { + 'name': partners_data[prt_id]['name'], + 'residual': ag_pb_data[acc_id][prt_id]['residual'], + 'current': ag_pb_data[acc_id][prt_id]['current'], + '30_days': ag_pb_data[acc_id][prt_id]['30_days'], + '60_days': ag_pb_data[acc_id][prt_id]['60_days'], + '90_days': ag_pb_data[acc_id][prt_id]['90_days'], + '120_days': ag_pb_data[acc_id][prt_id]['120_days'], + 'older': ag_pb_data[acc_id][prt_id]['older'], + } + if show_move_line_details: + move_lines = [] + for ml in ag_pb_data[acc_id][prt_id]['move_lines']: + ml.update({ + 'journal': journals_data[ml['jnl_id']]['code'], + 'account': accounts_data[ml['acc_id']]['code'], + }) + self._compute_maturity_date(ml, date_at_oject) + move_lines.append(ml) + partner.update({ + 'move_lines': move_lines + }) + account['partners'].append(partner) + aged_partner_data.append(account) + return aged_partner_data - The "only_empty_partner_line" value is used - to compute data without partner. - """ - query_inject_move_line = """ -WITH - date_range AS - ( - SELECT - DATE %s AS date_current, - DATE %s - INTEGER '30' AS date_less_30_days, - DATE %s - INTEGER '60' AS date_less_60_days, - DATE %s - INTEGER '90' AS date_less_90_days, - DATE %s - INTEGER '120' AS date_less_120_days - ) -INSERT INTO - report_aged_partner_balance_move_line - ( - report_partner_id, - create_uid, - create_date, - move_line_id, - date, - date_due, - entry, - journal, - account, - partner, - label, - amount_residual, - current, - age_30_days, - age_60_days, - age_90_days, - age_120_days, - older - ) -SELECT - rp.id AS report_partner_id, - %s AS create_uid, - NOW() AS create_date, - rlo.move_line_id, - rlo.date, - rlo.date_due, - rlo.entry, - rlo.journal, - rlo.account, - rlo.partner, - rlo.label, - rlo.amount_residual AS amount_residual, - CASE - WHEN rlo.date_due >= date_range.date_current - THEN rlo.amount_residual - END AS current, - CASE - WHEN - rlo.date_due >= date_range.date_less_30_days - AND rlo.date_due < date_range.date_current - THEN rlo.amount_residual - END AS age_30_days, - CASE - WHEN - rlo.date_due >= date_range.date_less_60_days - AND rlo.date_due < date_range.date_less_30_days - THEN rlo.amount_residual - END AS age_60_days, - CASE - WHEN - rlo.date_due >= date_range.date_less_90_days - AND rlo.date_due < date_range.date_less_60_days - THEN rlo.amount_residual - END AS age_90_days, - CASE - WHEN - rlo.date_due >= date_range.date_less_120_days - AND rlo.date_due < date_range.date_less_90_days - THEN rlo.amount_residual - END AS age_120_days, - CASE - WHEN rlo.date_due < date_range.date_less_120_days - THEN rlo.amount_residual - END AS older -FROM - date_range, - report_open_items_move_line rlo -INNER JOIN - report_open_items_partner rpo ON rlo.report_partner_id = rpo.id -INNER JOIN - report_open_items_account rao ON rpo.report_account_id = rao.id -INNER JOIN - report_aged_partner_balance_account ra ON rao.code = ra.code -INNER JOIN - report_aged_partner_balance_partner rp - ON - ra.id = rp.report_account_id - """ - if not only_empty_partner_line: - query_inject_move_line += """ - AND rpo.partner_id = rp.partner_id - """ - elif only_empty_partner_line: - query_inject_move_line += """ - AND rpo.partner_id IS NULL - AND rp.partner_id IS NULL - """ - query_inject_move_line += """ -WHERE - rao.report_id = %s -AND ra.report_id = %s - """ - query_inject_move_line_params = (self.date_at,) * 5 - query_inject_move_line_params += ( - self.env.uid, - self.open_items_id.id, - self.id, - ) - self.env.cr.execute(query_inject_move_line, - query_inject_move_line_params) + @api.model + def _calculate_percent(self, aged_partner_data): + for account in aged_partner_data: + if abs(account['residual']) > 0.01: + total = account['residual'] + account.update({ + 'percent_current': abs( + round((account['current'] / total) * 100, 2)), + 'percent_30_days': abs( + round((account['30_days'] / total) * 100, + 2)), + 'percent_60_days': abs( + round((account['60_days'] / total) * 100, + 2)), + 'percent_90_days': abs( + round((account['90_days'] / total) * 100, + 2)), + 'percent_120_days': abs( + round((account['120_days'] / total) * 100, + 2)), + 'percent_older': abs( + round((account['older'] / total) * 100, 2)), + }) + else: + account.update({ + 'percent_current': 0.0, + 'percent_30_days': 0.0, + 'percent_60_days': 0.0, + 'percent_90_days': 0.0, + 'percent_120_days': 0.0, + 'percent_older': 0.0, + }) + return aged_partner_data - def _compute_accounts_cumul(self): - """ Compute cumulative amount for - report_aged_partner_balance_account. - """ - query_compute_accounts_cumul = """ -WITH - cumuls AS - ( - SELECT - ra.id AS report_account_id, - SUM(rl.amount_residual) AS cumul_amount_residual, - SUM(rl.current) AS cumul_current, - SUM(rl.age_30_days) AS cumul_age_30_days, - SUM(rl.age_60_days) AS cumul_age_60_days, - SUM(rl.age_90_days) AS cumul_age_90_days, - SUM(rl.age_120_days) AS cumul_age_120_days, - SUM(rl.older) AS cumul_older - FROM - report_aged_partner_balance_line rl - INNER JOIN - report_aged_partner_balance_partner rp - ON rl.report_partner_id = rp.id - INNER JOIN - report_aged_partner_balance_account ra - ON rp.report_account_id = ra.id - WHERE - ra.report_id = %s - GROUP BY - ra.id - ) -UPDATE - report_aged_partner_balance_account -SET - cumul_amount_residual = c.cumul_amount_residual, - cumul_current = c.cumul_current, - cumul_age_30_days = c.cumul_age_30_days, - cumul_age_60_days = c.cumul_age_60_days, - cumul_age_90_days = c.cumul_age_90_days, - cumul_age_120_days = c.cumul_age_120_days, - cumul_older = c.cumul_older, - percent_current = - CASE - WHEN c.cumul_amount_residual != 0 - THEN 100 * c.cumul_current / c.cumul_amount_residual - END, - percent_age_30_days = - CASE - WHEN c.cumul_amount_residual != 0 - THEN 100 * c.cumul_age_30_days / c.cumul_amount_residual - END, - percent_age_60_days = - CASE - WHEN c.cumul_amount_residual != 0 - THEN 100 * c.cumul_age_60_days / c.cumul_amount_residual - END, - percent_age_90_days = - CASE - WHEN c.cumul_amount_residual != 0 - THEN 100 * c.cumul_age_90_days / c.cumul_amount_residual - END, - percent_age_120_days = - CASE - WHEN c.cumul_amount_residual != 0 - THEN 100 * c.cumul_age_120_days / c.cumul_amount_residual - END, - percent_older = - CASE - WHEN c.cumul_amount_residual != 0 - THEN 100 * c.cumul_older / c.cumul_amount_residual - END -FROM - cumuls c -WHERE - id = c.report_account_id - """ - params_compute_accounts_cumul = (self.id,) - self.env.cr.execute(query_compute_accounts_cumul, - params_compute_accounts_cumul) + @api.multi + def _get_report_values(self, docids, data): + wizard_id = data['wizard_id'] + company = self.env['res.company'].browse(data['company_id']) + company_id = data['company_id'] + account_ids = data['account_ids'] + partner_ids = data['partner_ids'] + date_at = data['date_at'] + date_at_object = datetime.strptime(date_at, '%Y-%m-%d').date() + only_posted_moves = data['only_posted_moves'] + show_move_line_details = data['show_move_line_details'] + ag_pb_data, accounts_data, partners_data, \ + journals_data = self._get_move_lines_data( + company_id, account_ids, partner_ids, date_at_object, + only_posted_moves, show_move_line_details) + aged_partner_data = self._create_account_list( + ag_pb_data, accounts_data, partners_data, journals_data, + show_move_line_details, date_at_object) + aged_partner_data = self._calculate_percent(aged_partner_data) + return { + 'doc_ids': [wizard_id], + 'doc_model': 'open.items.report.wizard', + 'docs': self.env['open.items.report.wizard'].browse(wizard_id), + 'company_name': company.display_name, + 'currency_name': company.currency_id.name, + 'date_at': date_at, + 'only_posted_moves': only_posted_moves, + 'aged_partner_balance': aged_partner_data, + 'show_move_lines_details': show_move_line_details, + } diff --git a/account_financial_report/report/aged_partner_balance_xlsx.py b/account_financial_report/report/aged_partner_balance_xlsx.py index a438cd83..01abb312 100644 --- a/account_financial_report/report/aged_partner_balance_xlsx.py +++ b/account_financial_report/report/aged_partner_balance_xlsx.py @@ -10,52 +10,58 @@ class AgedPartnerBalanceXslx(models.AbstractModel): _name = 'report.a_f_r.report_aged_partner_balance_xlsx' _inherit = 'report.account_financial_report.abstract_report_xlsx' - def _get_report_name(self, report): + def _get_report_name(self, report, data=False): + company_id = data.get('company_id', False) report_name = _('Aged Partner Balance') - return self._get_report_complete_name(report, report_name) + if company_id: + company = self.env['res.company'].browse(company_id) + suffix = ' - %s - %s' % ( + company.name, company.currency_id.name) + report_name = report_name + suffix + return report_name def _get_report_columns(self, report): if not report.show_move_line_details: return { - 0: {'header': _('Partner'), 'field': 'partner', 'width': 70}, + 0: {'header': _('Partner'), 'field': 'name', 'width': 70}, 1: {'header': _('Residual'), - 'field': 'amount_residual', - 'field_footer_total': 'cumul_amount_residual', + 'field': 'residual', + 'field_footer_total': 'residual', 'type': 'amount', 'width': 14}, 2: {'header': _('Current'), 'field': 'current', - 'field_footer_total': 'cumul_current', + 'field_footer_total': 'current', 'field_footer_percent': 'percent_current', 'type': 'amount', 'width': 14}, 3: {'header': _(u'Age ≤ 30 d.'), - 'field': 'age_30_days', - 'field_footer_total': 'cumul_age_30_days', - 'field_footer_percent': 'percent_age_30_days', + 'field': '30_days', + 'field_footer_total': '30_days', + 'field_footer_percent': 'percent_30_days', 'type': 'amount', 'width': 14}, 4: {'header': _(u'Age ≤ 60 d.'), - 'field': 'age_60_days', - 'field_footer_total': 'cumul_age_60_days', - 'field_footer_percent': 'percent_age_60_days', + 'field': '60_days', + 'field_footer_total': '60_days', + 'field_footer_percent': 'percent_60_days', 'type': 'amount', 'width': 14}, 5: {'header': _(u'Age ≤ 90 d.'), - 'field': 'age_90_days', - 'field_footer_total': 'cumul_age_90_days', - 'field_footer_percent': 'percent_age_90_days', + 'field': '90_days', + 'field_footer_total': '90_days', + 'field_footer_percent': 'percent_90_days', 'type': 'amount', 'width': 14}, 6: {'header': _(u'Age ≤ 120 d.'), - 'field': 'age_120_days', - 'field_footer_total': 'cumul_age_120_days', - 'field_footer_percent': 'percent_age_120_days', + 'field': '120_days', + 'field_footer_total': '120_days', + 'field_footer_percent': 'percent_120_days', 'type': 'amount', 'width': 14}, 7: {'header': _('Older'), 'field': 'older', - 'field_footer_total': 'cumul_older', + 'field_footer_total': 'older', 'field_footer_percent': 'percent_older', 'type': 'amount', 'width': 14}, @@ -66,52 +72,52 @@ class AgedPartnerBalanceXslx(models.AbstractModel): 2: {'header': _('Journal'), 'field': 'journal', 'width': 8}, 3: {'header': _('Account'), 'field': 'account', 'width': 9}, 4: {'header': _('Partner'), 'field': 'partner', 'width': 25}, - 5: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, - 6: {'header': _('Due date'), 'field': 'date_due', 'width': 11}, + 5: {'header': _('Ref - Label'), 'field': 'ref', 'width': 40}, + 6: {'header': _('Due date'), 'field': 'due_date', 'width': 11}, 7: {'header': _('Residual'), - 'field': 'amount_residual', - 'field_footer_total': 'cumul_amount_residual', - 'field_final_balance': 'amount_residual', + 'field': 'residual', + 'field_footer_total': 'residual', + 'field_final_balance': 'residual', 'type': 'amount', 'width': 14}, 8: {'header': _('Current'), 'field': 'current', - 'field_footer_total': 'cumul_current', + 'field_footer_total': 'current', 'field_footer_percent': 'percent_current', 'field_final_balance': 'current', 'type': 'amount', 'width': 14}, 9: {'header': _(u'Age ≤ 30 d.'), - 'field': 'age_30_days', - 'field_footer_total': 'cumul_age_30_days', - 'field_footer_percent': 'percent_age_30_days', - 'field_final_balance': 'age_30_days', + 'field': '30_days', + 'field_footer_total': '30_days', + 'field_footer_percent': 'percent_30_days', + 'field_final_balance': '30_days', 'type': 'amount', 'width': 14}, 10: {'header': _(u'Age ≤ 60 d.'), - 'field': 'age_60_days', - 'field_footer_total': 'cumul_age_60_days', - 'field_footer_percent': 'percent_age_60_days', - 'field_final_balance': 'age_60_days', + 'field': '60_days', + 'field_footer_total': '60_days', + 'field_footer_percent': 'percent_60_days', + 'field_final_balance': '60_days', 'type': 'amount', 'width': 14}, 11: {'header': _(u'Age ≤ 90 d.'), - 'field': 'age_90_days', - 'field_footer_total': 'cumul_age_90_days', - 'field_footer_percent': 'percent_age_90_days', - 'field_final_balance': 'age_90_days', + 'field': '90_days', + 'field_footer_total': '90_days', + 'field_footer_percent': 'percent_90_days', + 'field_final_balance': '90_days', 'type': 'amount', 'width': 14}, 12: {'header': _(u'Age ≤ 120 d.'), - 'field': 'age_120_days', - 'field_footer_total': 'cumul_age_120_days', - 'field_footer_percent': 'percent_age_120_days', - 'field_final_balance': 'age_120_days', + 'field': '120_days', + 'field_footer_total': '120_days', + 'field_footer_percent': 'percent_120_days', + 'field_final_balance': '120_days', 'type': 'amount', 'width': 14}, 13: {'header': _('Older'), 'field': 'older', - 'field_footer_total': 'cumul_older', + 'field_footer_total': 'older', 'field_footer_percent': 'percent_older', 'field_final_balance': 'older', 'type': 'amount', @@ -120,9 +126,9 @@ class AgedPartnerBalanceXslx(models.AbstractModel): def _get_report_filters(self, report): return [ - [_('Date at filter'), report.date_at], + [_('Date at filter'), report.date_at.strftime("%d/%m/%Y")], [_('Target moves filter'), - _('All posted entries') if report.only_posted_moves else _( + _('All posted entries') if report.target_move == 'posted' else _( 'All entries')], ] @@ -141,94 +147,89 @@ class AgedPartnerBalanceXslx(models.AbstractModel): def _get_col_pos_final_balance_label(self): return 5 - def _generate_report_content(self, workbook, report): - if not report.show_move_line_details: + def _generate_report_content(self, workbook, report, data): + res_data = self.env[ + 'report.account_financial_report.aged_partner_balance' + ]._get_report_values(report, data) + show_move_line_details = res_data['show_move_lines_details'] + aged_partner_balance = res_data['aged_partner_balance'] + if not show_move_line_details: # For each account - for account in report.account_ids: + for account in aged_partner_balance: # Write account title - self.write_array_title(account.code + ' - ' + account.name) + self.write_array_title(account['code'] + ' - ' + account[ + 'name']) # Display array header for partners lines self.write_array_header() # Display partner lines - for partner in account.partner_ids: - self.write_line(partner.line_ids) + for partner in account['partners']: + self.write_line_from_dict(partner) # Display account lines - self.write_account_footer(report, - account, - _('Total'), - 'field_footer_total', - self.format_header_right, - self.format_header_amount, - False) - self.write_account_footer(report, - account, - _('Percents'), - 'field_footer_percent', - self.format_right_bold_italic, - self.format_percent_bold_italic, - True) + self.write_account_footer_from_dict( + report, account, ('Total'), 'field_footer_total', + self.format_header_right, self.format_header_amount, False) + self.write_account_footer_from_dict( + report, account, ('Percents'), 'field_footer_percent', + self.format_right_bold_italic, + self.format_percent_bold_italic, True) # 2 lines break self.row_pos += 2 else: # For each account - for account in report.account_ids: + for account in aged_partner_balance: # Write account title - self.write_array_title(account.code + ' - ' + account.name) + self.write_array_title(account['code'] + ' - ' + account[ + 'name']) # For each partner - for partner in account.partner_ids: + for partner in account['partners']: # Write partner title - self.write_array_title(partner.name) + self.write_array_title(partner['name']) # Display array header for move lines self.write_array_header() # Display account move lines - for line in partner.move_line_ids: - self.write_line(line) + for line in partner['move_lines']: + self.write_line_from_dict(line) # Display ending balance line for partner - self.write_ending_balance(partner.line_ids) + self.write_ending_balance_from_dict(partner) # Line break self.row_pos += 1 # Display account lines - self.write_account_footer(report, - account, - _('Total'), - 'field_footer_total', - self.format_header_right, - self.format_header_amount, - False) - self.write_account_footer(report, - account, - _('Percents'), - 'field_footer_percent', - self.format_right_bold_italic, - self.format_percent_bold_italic, - True) + self.write_account_footer_from_dict( + report, account, ('Total'), 'field_footer_total', + self.format_header_right, self.format_header_amount, False) + + self.write_account_footer_from_dict( + report, account, ('Percents'), 'field_footer_percent', + self.format_right_bold_italic, + self.format_percent_bold_italic, True) # 2 lines break self.row_pos += 2 - def write_ending_balance(self, my_object): + def write_ending_balance_from_dict(self, my_object): """ Specific function to write ending partner balance for Aged Partner Balance """ name = None label = _('Partner cumul aged balance') - super(AgedPartnerBalanceXslx, self).write_ending_balance( + super(AgedPartnerBalanceXslx, self).write_ending_balance_from_dict( my_object, name, label ) - def write_account_footer(self, report, account, label, field_name, - string_format, amount_format, amount_is_percent): + def write_account_footer_from_dict( + self, report, account, label, field_name, string_format, + amount_format, amount_is_percent): """ Specific function to write account footer for Aged Partner Balance """ @@ -238,7 +239,7 @@ class AgedPartnerBalanceXslx(models.AbstractModel): if col_pos == col_pos_footer_label: value = label else: - value = getattr(account, column[field_name]) + value = account.get(column[field_name], False) cell_type = column.get('type', 'string') if cell_type == 'string' or col_pos == col_pos_footer_label: self.sheet.write_string(self.row_pos, col_pos, value or '', diff --git a/account_financial_report/report/general_ledger.py b/account_financial_report/report/general_ledger.py index 5beb736d..1ccb5c79 100644 --- a/account_financial_report/report/general_ledger.py +++ b/account_financial_report/report/general_ledger.py @@ -1,1783 +1,610 @@ - # © 2016 Julien Coux (Camptocamp) +# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api, _ - - -class GeneralLedgerReport(models.TransientModel): - """ Here, we just define class fields. - For methods, go more bottom at this file. - - The class hierarchy is : - * GeneralLedgerReport - ** GeneralLedgerReportAccount - *** GeneralLedgerReportMoveLine - For non receivable/payable accounts - For receivable/payable centralized accounts - *** GeneralLedgerReportPartner - For receivable/payable and not centralized accounts - **** GeneralLedgerReportMoveLine - For receivable/payable and not centralized accounts - """ - - _name = 'report_general_ledger' - _inherit = 'account_financial_report_abstract' - - # Filters fields, used for data computation - date_from = fields.Date() - date_to = fields.Date() - fy_start_date = fields.Date() - only_posted_moves = fields.Boolean() - hide_account_at_0 = fields.Boolean() - foreign_currency = fields.Boolean() - show_analytic_tags = 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') - filter_cost_center_ids = fields.Many2many( - comodel_name='account.analytic.account' - ) - filter_analytic_tag_ids = fields.Many2many( - comodel_name='account.analytic.tag', - ) - filter_journal_ids = fields.Many2many( - comodel_name='account.journal', - ) - centralize = fields.Boolean() - - # Flag fields, used for report display - show_cost_center = fields.Boolean( - default=lambda self: self.env.user.has_group( - 'analytic.group_analytic_accounting' - ) - ) - partner_ungrouped = fields.Boolean( - string='Partner ungrouped', - help='If set moves are not grouped by partner in any case' - ) - - # Data fields, used to browse report data - account_ids = fields.One2many( - comodel_name='report_general_ledger_account', - inverse_name='report_id' - ) - - # Compute of unaffected earnings account - @api.depends('company_id') - def _compute_unaffected_earnings_account(self): - account_type = self.env.ref('account.data_unaffected_earnings') - self.unaffected_earnings_account = self.env['account.account'].search( - [ - ('user_type_id', '=', account_type.id), - ('company_id', '=', self.company_id.id) - ]) - - unaffected_earnings_account = fields.Many2one( - comodel_name='account.account', - compute='_compute_unaffected_earnings_account', - store=True - ) - - -class GeneralLedgerReportAccount(models.TransientModel): - - _name = 'report_general_ledger_account' - _inherit = 'account_financial_report_abstract' - _order = 'code ASC' - - report_id = fields.Many2one( - comodel_name='report_general_ledger', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - account_id = fields.Many2one( - 'account.account', - index=True - ) - - # Data fields, used for report display - 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)) - currency_id = fields.Many2one('res.currency') - initial_balance_foreign_currency = 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)) - final_balance_foreign_currency = fields.Float(digits=(16, 2)) - - # Flag fields, used for report display and for data computation - is_partner_account = fields.Boolean() - - # Data fields, used to browse report data - move_line_ids = fields.One2many( - comodel_name='report_general_ledger_move_line', - inverse_name='report_account_id' - ) - partner_ids = fields.One2many( - comodel_name='report_general_ledger_partner', - inverse_name='report_account_id' - ) - - -class GeneralLedgerReportPartner(models.TransientModel): - - _name = 'report_general_ledger_partner' - _inherit = 'account_financial_report_abstract' - - report_account_id = fields.Many2one( - comodel_name='report_general_ledger_account', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - partner_id = fields.Many2one( - 'res.partner', - index=True - ) - - # Data fields, used for report display - name = fields.Char() - initial_debit = fields.Float(digits=(16, 2)) - initial_credit = fields.Float(digits=(16, 2)) - initial_balance = fields.Float(digits=(16, 2)) - currency_id = fields.Many2one('res.currency') - initial_balance_foreign_currency = 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)) - final_balance_foreign_currency = fields.Float(digits=(16, 2)) - - # Data fields, used to browse report data - move_line_ids = fields.One2many( - comodel_name='report_general_ledger_move_line', - inverse_name='report_partner_id' - ) - - @api.model - def _generate_order_by(self, order_spec, query): - """Custom order to display "No partner allocated" at last position.""" - return """ -ORDER BY - CASE - WHEN "report_general_ledger_partner"."partner_id" IS NOT NULL - THEN 0 - ELSE 1 - END, - "report_general_ledger_partner"."name" - """ - - -class GeneralLedgerReportMoveLine(models.TransientModel): - - _name = 'report_general_ledger_move_line' - _inherit = 'account_financial_report_abstract' - - report_account_id = fields.Many2one( - comodel_name='report_general_ledger_account', - ondelete='cascade', - index=True - ) - report_partner_id = fields.Many2one( - comodel_name='report_general_ledger_partner', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - move_line_id = fields.Many2one('account.move.line') - - # Data fields, used for report display - date = fields.Date() - entry = fields.Char() - journal = fields.Char() - account = fields.Char() - taxes_description = fields.Char() - partner = fields.Char() - label = fields.Char() - cost_center = fields.Char() - tags = 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_id = fields.Many2one('res.currency') - amount_currency = fields.Float(digits=(16, 2)) - - -class GeneralLedgerReportCompute(models.TransientModel): - """ Here, we just define methods. - For class fields, go more top at this file. - """ - - _inherit = 'report_general_ledger' - - @api.multi - def print_report(self, report_type): - self.ensure_one() - if report_type == 'xlsx': - report_name = 'a_f_r.report_general_ledger_xlsx' - else: - report_name = 'account_financial_report.' \ - 'report_general_ledger_qweb' - return self.env['ir.actions.report'].search( - [('report_name', '=', report_name), - ('report_type', '=', report_type)], - limit=1).report_action(self, config=False) - - def _get_html(self): - result = {} - rcontext = {} - context = dict(self.env.context) - report = self.browse(context.get('active_id')) - if report: - rcontext['o'] = report - result['html'] = self.env.ref( - 'account_financial_report.report_general_ledger').render( - rcontext) - return result - - @api.model - def get_html(self, given_context=None): - return self._get_html() - - @api.multi - def compute_data_for_report(self, - with_line_details=True, - with_partners=True): - self.ensure_one() - # Compute report data - self._inject_account_values() - - if with_partners: - self._inject_partner_values() - if not self.filter_partner_ids: - self._inject_partner_values(only_empty_partner=True) - # Add unaffected earnings account - if (not self.filter_account_ids or - self.unaffected_earnings_account.id in - self.filter_account_ids.ids): - self._inject_unaffected_earnings_account_values() - - # Call this function even if we don't want line details because, - # we need to compute - # at least the values for unaffected earnings account - # In this case, only unaffected earnings account values are computed - only_unaffected_earnings_account = not with_line_details - self._inject_line_not_centralized_values( - only_unaffected_earnings_account=only_unaffected_earnings_account - ) - - if with_line_details: - 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() - - if self.show_analytic_tags: - # Compute analytic tags - self._compute_analytic_tags() - - # Refresh cache because all data are computed with SQL requests - self.invalidate_cache() - - def _get_account_sub_subquery_sum_amounts( - self, include_initial_balance, date_included): - """ Return subquery used to compute sum amounts on accounts """ - sub_subquery_sum_amounts = """ - SELECT - a.id AS account_id, - SUM(ml.debit) AS debit, - SUM(ml.credit) AS credit, - SUM(ml.balance) AS balance, - c.id AS currency_id, - CASE - WHEN c.id IS NOT NULL - THEN SUM(ml.amount_currency) - ELSE NULL - END AS balance_currency - 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 - """ - - if date_included: - sub_subquery_sum_amounts += """ - AND ml.date <= %s - """ - else: - sub_subquery_sum_amounts += """ - AND ml.date < %s - """ - - if not include_initial_balance: - sub_subquery_sum_amounts += """ - AND at.include_initial_balance != TRUE AND ml.date >= %s - """ - else: - sub_subquery_sum_amounts += """ - AND at.include_initial_balance = TRUE - """ - if self.filter_journal_ids: - sub_subquery_sum_amounts += """ - AND - ml.journal_id IN %s - """ % (tuple(self.filter_journal_ids.ids,),) - - if self.only_posted_moves: - sub_subquery_sum_amounts += """ - INNER JOIN - account_move m ON ml.move_id = m.id AND m.state = 'posted' - """ - if self.filter_cost_center_ids: - sub_subquery_sum_amounts += """ - INNER JOIN - account_analytic_account aa - ON - ml.analytic_account_id = aa.id - AND aa.id IN %s - """ - if self.filter_analytic_tag_ids: - sub_subquery_sum_amounts += """ - INNER JOIN - move_lines_on_tags ON ml.id = move_lines_on_tags.ml_id - """ - sub_subquery_sum_amounts += """ - LEFT JOIN - res_currency c ON a.currency_id = c.id - """ - sub_subquery_sum_amounts += """ - GROUP BY - a.id, c.id - """ - return sub_subquery_sum_amounts - - def _get_final_account_sub_subquery_sum_amounts(self, date_included): - """ Return final subquery used to compute sum amounts on accounts """ - subquery_sum_amounts = """ - SELECT - sub.account_id AS account_id, - SUM(COALESCE(sub.debit, 0.0)) AS debit, - SUM(COALESCE(sub.credit, 0.0)) AS credit, - SUM(COALESCE(sub.balance, 0.0)) AS balance, - MAX(sub.currency_id) AS currency_id, - SUM(COALESCE(sub.balance_currency, 0.0)) AS balance_currency - FROM - ( - """ - subquery_sum_amounts += self._get_account_sub_subquery_sum_amounts( - include_initial_balance=False, date_included=date_included - ) - subquery_sum_amounts += """ - UNION - """ - subquery_sum_amounts += self._get_account_sub_subquery_sum_amounts( - include_initial_balance=True, date_included=date_included - ) - subquery_sum_amounts += """ - ) sub - GROUP BY - sub.account_id - """ - return subquery_sum_amounts - - def _inject_account_values(self): - """Inject report values for report_general_ledger_account.""" - query_inject_account = """ -WITH - accounts AS - ( - SELECT - a.id, - a.code, - a.name, - """ - if self.partner_ungrouped: - query_inject_account += """ - FALSE AS is_partner_account, - """ - else: - query_inject_account += """ - a.internal_type IN ('payable', 'receivable') - AS is_partner_account, - """ - query_inject_account += """ - a.user_type_id, - a.currency_id - FROM - account_account a - """ - if ( - self.filter_partner_ids or - self.filter_cost_center_ids or - self.filter_analytic_tag_ids - ): - query_inject_account += """ - INNER JOIN - account_move_line ml ON a.id = ml.account_id - """ - if self.filter_partner_ids: - query_inject_account += """ - INNER JOIN - res_partner p ON ml.partner_id = p.id - """ - if self.filter_cost_center_ids: - query_inject_account += """ - INNER JOIN - account_analytic_account aa - ON - ml.analytic_account_id = aa.id - AND aa.id IN %s - """ - if self.filter_analytic_tag_ids: - query_inject_account += """ - INNER JOIN - account_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - AND aat.id IN %s - """ - query_inject_account += """ - WHERE - a.company_id = %s - AND a.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 - """ - if ( - self.filter_partner_ids or - self.filter_cost_center_ids or - self.filter_analytic_tag_ids - ): - query_inject_account += """ - GROUP BY - a.id - """ - query_inject_account += """ - ), - """ - if self.filter_analytic_tag_ids: - query_inject_account += """ - move_lines_on_tags AS - ( - SELECT - DISTINCT ml.id AS ml_id - FROM - accounts a - INNER JOIN - account_move_line ml - ON a.id = ml.account_id - INNER JOIN - account_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - WHERE - aat.id IN %s - ), - """ - - init_subquery = self._get_final_account_sub_subquery_sum_amounts( - date_included=False - ) - final_subquery = self._get_final_account_sub_subquery_sum_amounts( - date_included=True - ) - - query_inject_account += """ - initial_sum_amounts AS ( """ + init_subquery + """ ), - final_sum_amounts AS ( """ + final_subquery + """ ) -INSERT INTO - report_general_ledger_account - ( - report_id, - create_uid, - create_date, - account_id, - code, - name, - initial_debit, - initial_credit, - initial_balance, - currency_id, - initial_balance_foreign_currency, - final_debit, - final_credit, - final_balance, - final_balance_foreign_currency, - 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, - c.id AS currency_id, - COALESCE(i.balance_currency, 0.0) AS initial_balance_foreign_currency, - COALESCE(f.debit, 0.0) AS final_debit, - COALESCE(f.credit, 0.0) AS final_credit, - COALESCE(f.balance, 0.0) AS final_balance, - COALESCE(f.balance_currency, 0.0) AS final_balance_foreign_currency, - 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 -LEFT JOIN - res_currency c ON c.id = a.currency_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_at_0: - query_inject_account += """ -AND - f.balance IS NOT NULL AND f.balance != 0 - """ - query_inject_account_params = () - if self.filter_cost_center_ids: - query_inject_account_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - if self.filter_analytic_tag_ids: - query_inject_account_params += ( - tuple(self.filter_analytic_tag_ids.ids), - ) - query_inject_account_params += ( - self.company_id.id, - self.unaffected_earnings_account.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), - ) - if self.filter_analytic_tag_ids: - query_inject_account_params += ( - tuple(self.filter_analytic_tag_ids.ids), - ) - query_inject_account_params += ( - self.date_from, - self.fy_start_date, - ) - if self.filter_cost_center_ids: - query_inject_account_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_account_params += ( - self.date_from, - ) - if self.filter_cost_center_ids: - query_inject_account_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_account_params += ( - self.date_to, - self.fy_start_date, - ) - if self.filter_cost_center_ids: - query_inject_account_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_account_params += ( - self.date_to, - ) - if self.filter_cost_center_ids: - query_inject_account_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_account_params += ( - self.id, - self.env.uid, - ) - self.env.cr.execute(query_inject_account, query_inject_account_params) - - def _get_partner_sub_subquery_sum_amounts( - self, only_empty_partner, include_initial_balance, date_included - ): - """ Return subquery used to compute sum amounts on partners """ - sub_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, - c.id as currency_id, - CASE - WHEN c.id IS NOT NULL - THEN SUM(ml.amount_currency) - ELSE NULL - END AS balance_currency - FROM - accounts_partners ap - INNER JOIN account_account ac - ON ac.id = ap.account_id - LEFT JOIN - res_currency c ON ac.currency_id = c.id - INNER JOIN - account_move_line ml - ON ap.account_id = ml.account_id - """ - if date_included: - sub_subquery_sum_amounts += """ - AND ml.date <= %s - """ - else: - sub_subquery_sum_amounts += """ - AND ml.date < %s - """ - if not only_empty_partner: - sub_subquery_sum_amounts += """ - AND ap.partner_id = ml.partner_id - """ - else: - sub_subquery_sum_amounts += """ - AND ap.partner_id IS NULL AND ml.partner_id IS NULL - """ - if not include_initial_balance: - sub_subquery_sum_amounts += """ - AND ap.include_initial_balance != TRUE AND ml.date >= %s - """ - else: - sub_subquery_sum_amounts += """ - AND ap.include_initial_balance = TRUE - """ - if self.only_posted_moves: - sub_subquery_sum_amounts += """ - INNER JOIN - account_move m ON ml.move_id = m.id AND m.state = 'posted' - """ - if self.filter_cost_center_ids: - sub_subquery_sum_amounts += """ - INNER JOIN - account_analytic_account aa - ON - ml.analytic_account_id = aa.id - AND aa.id IN %s - """ - if self.filter_analytic_tag_ids: - sub_subquery_sum_amounts += """ - INNER JOIN - move_lines_on_tags ON ml.id = move_lines_on_tags.ml_id - """ - sub_subquery_sum_amounts += """ - GROUP BY - ap.account_id, ap.partner_id, c.id - """ - return sub_subquery_sum_amounts - - def _get_final_partner_sub_subquery_sum_amounts(self, only_empty_partner, - date_included): - """Return final subquery used to compute sum amounts on partners""" - - subquery_sum_amounts = """ - - SELECT - sub.account_id AS account_id, - sub.partner_id AS partner_id, - SUM(COALESCE(sub.debit, 0.0)) AS debit, - SUM(COALESCE(sub.credit, 0.0)) AS credit, - SUM(COALESCE(sub.balance, 0.0)) AS balance, - MAX(sub.currency_id) AS currency_id, - SUM(COALESCE(sub.balance_currency, 0.0)) AS balance_currency - FROM - ( - """ - subquery_sum_amounts += self._get_partner_sub_subquery_sum_amounts( - only_empty_partner, - include_initial_balance=False, - date_included=date_included +from odoo import models, api +from operator import itemgetter +from natsort import natsorted +import calendar +import datetime + + +class GeneralLedgerReport(models.AbstractModel): + _name = 'report.account_financial_report.general_ledger' + _description = "General Ledger Report" + + def _get_accounts_data(self, account_ids): + accounts = self.env['account.account'].browse(account_ids) + accounts_data = {} + for account in accounts: + accounts_data.update({account.id: { + 'id': account.id, + 'code': account.code, + 'name': account.name, + 'group_id': account.group_id.id, + 'currency_id': account.currency_id or False, + 'currency_name': account.currency_id.name} + }) + return accounts_data + + def _get_journals_data(self, journals_ids): + journals = self.env['account.journal'].browse(journals_ids) + journals_data = {} + for journal in journals: + journals_data.update({journal.id: {'id': journal.id, + 'code': journal.code}}) + return journals_data + + def _get_tags_data(self, tags_ids): + tags = self.env['account.analytic.tag'].browse(tags_ids) + tags_data = {} + for tag in tags: + tags_data.update({tag.id: {'name': tag.name}}) + return tags_data + + def _get_taxes_data(self, taxes_ids): + taxes = self.env['account.tax'].browse(taxes_ids) + taxes_data = {} + for tax in taxes: + taxes_data.update({tax.id: { + 'id': tax.id, + 'amount': tax.amount, + 'amount_type': tax.amount_type, + }}) + if tax.amount_type == 'percent' or tax.amount_type == 'division': + taxes_data[tax.id]['string'] = '%' + else: + taxes_data[tax.id]['string'] = '' + return taxes_data + + def _get_acc_prt_accounts_ids(self, company_id): + accounts_domain = [ + ('company_id', '=', company_id), + ('internal_type', 'in', ['receivable', 'payable'])] + acc_prt_accounts = self.env['account.account'].search(accounts_domain) + return acc_prt_accounts.ids + + def _get_initial_balances_bs_ml_domain(self, account_ids, + company_id, date_from, + base_domain, acc_prt=False): + accounts_domain = [ + ('company_id', '=', company_id), + ('user_type_id.include_initial_balance', '=', True)] + if account_ids: + accounts_domain += [('id', 'in', account_ids)] + domain = [] + domain += base_domain + domain += [('date', '<', date_from)] + accounts = self.env['account.account'].search(accounts_domain) + domain += [('account_id', 'in', accounts.ids)] + if acc_prt: + domain += [('account_id.internal_type', 'in', [ + 'receivable', 'payable'])] + return domain + + def _get_initial_balances_pl_ml_domain(self, account_ids, + company_id, date_from, + fy_start_date, base_domain): + accounts_domain = [ + ('company_id', '=', company_id), + ('user_type_id.include_initial_balance', '=', False)] + if account_ids: + accounts_domain += [('id', 'in', account_ids)] + domain = [] + domain += base_domain + domain += [('date', '<', date_from), ('date', '>=', fy_start_date)] + accounts = self.env['account.account'].search(accounts_domain) + domain += [('account_id', 'in', accounts.ids)] + return domain + + def _get_accounts_initial_balance(self, initial_domain_bs, + initial_domain_pl): + gl_initial_acc_bs = self.env['account.move.line'].read_group( + domain=initial_domain_bs, + fields=['account_id', 'debit', 'credit', 'balance', + 'amount_currency'], + groupby=['account_id'] ) - subquery_sum_amounts += """ - UNION - """ - subquery_sum_amounts += self._get_partner_sub_subquery_sum_amounts( - only_empty_partner, - include_initial_balance=True, - date_included=date_included + gl_initial_acc_pl = self.env['account.move.line'].read_group( + domain=initial_domain_pl, + fields=['account_id', 'debit', 'credit', 'balance', + 'amount_currency'], + groupby=['account_id']) + gl_initial_acc = gl_initial_acc_bs + gl_initial_acc_pl + return gl_initial_acc + + def _get_initial_balance_fy_pl_ml_domain(self, account_ids, company_id, + fy_start_date, base_domain): + accounts_domain = [ + ('company_id', '=', company_id), + ('user_type_id.include_initial_balance', '=', False)] + if account_ids: + accounts_domain += [('id', 'in', account_ids)] + domain = [] + domain += base_domain + domain += [('date', '<', fy_start_date)] + accounts = self.env['account.account'].search(accounts_domain) + domain += [('account_id', 'in', accounts.ids)] + return domain + + def _get_pl_initial_balance(self, account_ids, company_id, + fy_start_date, foreign_currency, base_domain): + domain = self._get_initial_balance_fy_pl_ml_domain( + account_ids, company_id, fy_start_date, base_domain ) - subquery_sum_amounts += """ - ) sub - GROUP BY - sub.account_id, sub.partner_id - """ - return subquery_sum_amounts - - def _inject_partner_values(self, only_empty_partner=False): - """ Inject report values for report_general_ledger_partner. - - Only for "partner" accounts (payable and receivable). - """ - # pylint: disable=sql-injection - 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_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 - """ - if self.filter_cost_center_ids: - query_inject_partner += """ - INNER JOIN - account_analytic_account aa - ON - ml.analytic_account_id = aa.id - AND aa.id IN %s - """ - if self.filter_analytic_tag_ids: - query_inject_partner += """ - INNER JOIN - account_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - AND aat.id IN %s - """ - query_inject_partner += """ - WHERE - ra.report_id = %s - AND - ra.is_partner_account = TRUE - """ - if not only_empty_partner: - query_inject_partner += """ - AND - p.id IS NOT NULL - """ - else: - query_inject_partner += """ - AND - p.id IS NULL - """ - query_inject_partner += """ - """ - 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 - """ - - init_subquery = self._get_final_partner_sub_subquery_sum_amounts( - only_empty_partner, - date_included=False + initial_balances = self.env['account.move.line'].read_group( + domain=domain, + fields=[ + 'account_id', + 'debit', + 'credit', + 'balance', + 'amount_currency'], + groupby=['account_id']) + pl_initial_balance = { + 'debit': 0.0, + 'credit': 0.0, + 'balance': 0.0, + 'bal_curr': 0.0, + } + for initial_balance in initial_balances: + pl_initial_balance['debit'] += initial_balance['debit'] + pl_initial_balance['credit'] += initial_balance['credit'] + pl_initial_balance['balance'] += initial_balance['balance'] + pl_initial_balance['bal_curr'] += initial_balance['amount_currency'] + return pl_initial_balance + + def _get_initial_balance_data( + self, account_ids, partner_ids, company_id, date_from, + foreign_currency, only_posted_moves, hide_account_at_0, + unaffected_earnings_account, fy_start_date, analytic_tag_ids, + cost_center_ids): + base_domain = [] + if company_id: + base_domain += [('company_id', '=', company_id)] + if partner_ids: + base_domain += [('partner_id', 'in', partner_ids)] + if only_posted_moves: + base_domain += [('move_id.state', '=', 'posted')] + if analytic_tag_ids: + base_domain += [('analytic_tag_ids', 'in', analytic_tag_ids)] + if cost_center_ids: + base_domain += [('analytic_account_id', 'in', cost_center_ids)] + initial_domain_bs = self._get_initial_balances_bs_ml_domain( + account_ids, company_id, date_from, base_domain ) - final_subquery = self._get_final_partner_sub_subquery_sum_amounts( - only_empty_partner, - date_included=True + initial_domain_pl = self._get_initial_balances_pl_ml_domain( + account_ids, company_id, date_from, fy_start_date, base_domain ) - - query_inject_partner += """ - GROUP BY - ra.id, - a.id, - p.id, - at.include_initial_balance - ), - """ - if self.filter_analytic_tag_ids: - query_inject_partner += """ - move_lines_on_tags AS - ( - SELECT - DISTINCT ml.id AS ml_id - FROM - accounts_partners ap - INNER JOIN - account_move_line ml - ON ap.account_id = ml.account_id - INNER JOIN - account_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - WHERE - aat.id IN %s - ), - """ - - query_inject_partner += """ - initial_sum_amounts AS ( """ + init_subquery + """ ), - final_sum_amounts AS ( """ + final_subquery + """ ) -INSERT INTO - report_general_ledger_partner - ( - report_account_id, - create_uid, - create_date, - partner_id, - name, - initial_debit, - initial_credit, - initial_balance, - currency_id, - initial_balance_foreign_currency, - final_debit, - final_credit, - final_balance, - final_balance_foreign_currency - ) -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, - i.currency_id AS currency_id, - COALESCE(i.balance_currency, 0.0) AS initial_balance_foreign_currency, - COALESCE(f.debit, 0.0) AS final_debit, - COALESCE(f.credit, 0.0) AS final_credit, - COALESCE(f.balance, 0.0) AS final_balance, - COALESCE(f.balance_currency, 0.0) AS final_balance_foreign_currency -FROM - accounts_partners ap -LEFT JOIN - initial_sum_amounts i - ON - ( - """ - if not only_empty_partner: - query_inject_partner += """ - ap.partner_id = i.partner_id - """ - else: - query_inject_partner += """ - ap.partner_id IS NULL AND i.partner_id IS NULL - """ - query_inject_partner += """ - ) - AND ap.account_id = i.account_id -LEFT JOIN - final_sum_amounts f - ON - ( - """ - if not only_empty_partner: - query_inject_partner += """ - ap.partner_id = f.partner_id - """ - else: - query_inject_partner += """ - ap.partner_id IS NULL AND f.partner_id IS NULL - """ - query_inject_partner += """ - ) - 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_at_0: - query_inject_partner += """ -AND - f.balance IS NOT NULL AND f.balance != 0 - """ - query_inject_partner_params = () - if self.filter_cost_center_ids: - query_inject_partner_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - if self.filter_analytic_tag_ids: - query_inject_partner_params += ( - tuple(self.filter_analytic_tag_ids.ids), - ) - query_inject_partner_params += ( - self.id, + gl_initial_acc = self._get_accounts_initial_balance( + initial_domain_bs, initial_domain_pl ) - if self.filter_partner_ids: - query_inject_partner_params += ( - tuple(self.filter_partner_ids.ids), - ) - if self.filter_analytic_tag_ids: - query_inject_partner_params += ( - tuple(self.filter_analytic_tag_ids.ids), - ) - query_inject_partner_params += ( - self.date_from, - self.fy_start_date, + initial_domain_acc_prt = self._get_initial_balances_bs_ml_domain( + account_ids, company_id, date_from, base_domain, acc_prt=True ) - if self.filter_cost_center_ids: - query_inject_partner_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_partner_params += ( - self.date_from, + gl_initial_acc_prt = self.env['account.move.line'].read_group( + domain=initial_domain_acc_prt, + fields=['account_id', 'partner_id', + 'debit', 'credit', 'balance', 'amount_currency'], + groupby=['account_id', 'partner_id'], + lazy=False ) - if self.filter_cost_center_ids: - query_inject_partner_params += ( - tuple(self.filter_cost_center_ids.ids), + gen_ld_data = {} + for gl in gl_initial_acc: + acc_id = gl['account_id'][0] + gen_ld_data[acc_id] = {} + gen_ld_data[acc_id]['id'] = acc_id + gen_ld_data[acc_id]['partners'] = False + gen_ld_data[acc_id]['init_bal'] = {} + gen_ld_data[acc_id]['init_bal']['credit'] = gl['credit'] + gen_ld_data[acc_id]['init_bal']['debit'] = gl['debit'] + gen_ld_data[acc_id]['init_bal']['balance'] = gl['balance'] + gen_ld_data[acc_id]['fin_bal'] = {} + gen_ld_data[acc_id]['fin_bal']['credit'] = gl['credit'] + gen_ld_data[acc_id]['fin_bal']['debit'] = gl['debit'] + gen_ld_data[acc_id]['fin_bal']['balance'] = gl['balance'] + gen_ld_data[acc_id]['init_bal']['bal_curr'] = gl['amount_currency'] + gen_ld_data[acc_id]['fin_bal']['bal_curr'] = gl['amount_currency'] + partners_data = {} + partners_ids = set() + if gl_initial_acc_prt: + for gl in gl_initial_acc_prt: + if not gl['partner_id']: + prt_id = 0 + prt_name = 'Missing Partner' + else: + prt_id = gl['partner_id'][0] + prt_name = gl['partner_id'][1] + if prt_id not in partners_ids: + partners_ids.add(prt_id) + partners_data.update({ + prt_id: {'id': prt_id, 'name': prt_name} + }) + acc_id = gl['account_id'][0] + gen_ld_data[acc_id][prt_id] = {} + gen_ld_data[acc_id][prt_id]['id'] = prt_id + gen_ld_data[acc_id]['partners'] = True + gen_ld_data[acc_id][prt_id]['init_bal'] = {} + gen_ld_data[acc_id][prt_id][ + 'init_bal']['credit'] = gl['credit'] + gen_ld_data[acc_id][prt_id][ + 'init_bal']['debit'] = gl['debit'] + gen_ld_data[acc_id][prt_id][ + 'init_bal']['balance'] = gl['balance'] + gen_ld_data[acc_id][prt_id]['fin_bal'] = {} + gen_ld_data[acc_id][prt_id][ + 'fin_bal']['credit'] = gl['credit'] + gen_ld_data[acc_id][prt_id][ + 'fin_bal']['debit'] = gl['debit'] + gen_ld_data[acc_id][prt_id][ + 'fin_bal']['balance'] = gl['balance'] + gen_ld_data[acc_id][prt_id]['init_bal'][ + 'bal_curr'] = gl['amount_currency'] + gen_ld_data[acc_id][prt_id]['fin_bal'][ + 'bal_curr'] = gl['amount_currency'] + accounts_ids = list(gen_ld_data.keys()) + unaffected_id = unaffected_earnings_account + if unaffected_id not in accounts_ids: + accounts_ids.append(unaffected_id) + self._initialize_account( + gen_ld_data, unaffected_id, foreign_currency ) - query_inject_partner_params += ( - self.date_to, - self.fy_start_date, + pl_initial_balance = self._get_pl_initial_balance( + account_ids, company_id, fy_start_date, + foreign_currency, base_domain ) - if self.filter_cost_center_ids: - query_inject_partner_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_partner_params += ( - self.date_to, - ) - if self.filter_cost_center_ids: - query_inject_partner_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_partner_params += ( - self.env.uid, - ) - self.env.cr.execute(query_inject_partner, query_inject_partner_params) + gen_ld_data[unaffected_id]['init_bal']['debit'] += \ + pl_initial_balance['debit'] + gen_ld_data[unaffected_id]['init_bal']['credit'] += \ + pl_initial_balance['credit'] + gen_ld_data[unaffected_id]['init_bal']['balance'] += \ + pl_initial_balance['balance'] + gen_ld_data[unaffected_id]['fin_bal']['debit'] += \ + pl_initial_balance['debit'] + gen_ld_data[unaffected_id]['fin_bal']['credit'] += \ + pl_initial_balance['credit'] + gen_ld_data[unaffected_id]['fin_bal']['balance'] += \ + pl_initial_balance['balance'] + if foreign_currency: + gen_ld_data[unaffected_id]['init_bal']['bal_curr'] += \ + pl_initial_balance['bal_curr'] + gen_ld_data[unaffected_id]['fin_bal']['bal_curr'] += \ + pl_initial_balance['bal_curr'] + return gen_ld_data, partners_data, partner_ids - def _inject_line_not_centralized_values( - self, - is_account_line=True, - is_partner_line=False, - only_empty_partner_line=False, - only_unaffected_earnings_account=False): - """ Inject report values for report_general_ledger_move_line. - - If centralized option have been chosen, - only non centralized accounts are computed. + @api.model + def _get_move_line_data(self, move_line): + move_line_data = { + 'id': move_line['id'], + 'date': move_line['date'], + 'entry': move_line['move_id'][1], + 'entry_id': move_line['move_id'][0], + 'journal_id': move_line['journal_id'][0], + 'account_id': move_line['account_id'][0], + 'partner_id': move_line['partner_id'][0] if + move_line['partner_id'] else False, + 'partner_name': move_line['partner_id'][1] if + move_line['partner_id'] else "", + 'ref': move_line['name'], + 'tax_ids': move_line['tax_ids'], + 'debit': move_line['debit'], + 'credit': move_line['credit'], + 'balance': move_line['balance'], + 'bal_curr': move_line['amount_currency'], + 'rec_id': move_line['full_reconcile_id'][0] if + move_line['full_reconcile_id'] else False, + 'rec_name': move_line['full_reconcile_id'][1] if + move_line['full_reconcile_id'] else "", + 'tag_ids': move_line['analytic_tag_ids'], + 'currency_id': move_line['currency_id'], + } + return move_line_data - In function of `is_account_line` and `is_partner_line` values, - the move_line link is made either with account or either with partner. + @api.model + def _get_period_domain( + self, account_ids, partner_ids, company_id, only_posted_moves, + date_to, date_from, analytic_tag_ids, cost_center_ids): + domain = [('date', '>=', date_from), ('date', '<=', date_to)] + if account_ids: + domain += [('account_id', 'in', account_ids)] + if company_id: + domain += [('company_id', '=', company_id)] + if partner_ids: + domain += [('partner_id', 'in', partner_ids)] + if only_posted_moves: + domain += [('move_id.state', '=', 'posted')] + if analytic_tag_ids: + domain += [('analytic_tag_ids', 'in', analytic_tag_ids)] + if cost_center_ids: + domain += [('analytic_account_id', 'in', cost_center_ids)] + return domain - The "only_empty_partner_line" value is used - to compute data without partner. - """ - query_inject_move_line = "" - if self.filter_analytic_tag_ids: - query_inject_move_line += """ - WITH - move_lines_on_tags AS - ( - SELECT - DISTINCT ml.id AS ml_id - FROM - """ - if is_account_line: - query_inject_move_line += """ - report_general_ledger_account ra - """ - elif is_partner_line: - query_inject_move_line += """ - report_general_ledger_partner rp - INNER JOIN - report_general_ledger_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_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - WHERE - ra.report_id = %s - AND - aat.id IN %s - ) - """ - query_inject_move_line += """ -INSERT INTO - report_general_ledger_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, - taxes_description, - partner, - label, - cost_center, - matching_number, - debit, - credit, - cumul_balance, - currency_id, - 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, - CASE - WHEN - ml.tax_line_id is not null - THEN - COALESCE(at.description, at.name) - WHEN - ml.tax_line_id is null - THEN - (SELECT - array_to_string( - array_agg(COALESCE(at.description, at.name) - ), ', ') - FROM - account_move_line_account_tax_rel aml_at_rel - LEFT JOIN - account_tax at on (at.id = aml_at_rel.account_tax_id) - WHERE - aml_at_rel.account_move_line_id = ml.id) - ELSE - '' - END as taxes_description, - """ - 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.id AS currency_id, - ml.amount_currency -FROM - """ - if is_account_line: - query_inject_move_line += """ - report_general_ledger_account ra - """ - elif is_partner_line: - query_inject_move_line += """ - report_general_ledger_partner rp -INNER JOIN - report_general_ledger_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 -LEFT JOIN - account_tax at ON ml.tax_line_id = at.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 ml.currency_id = c.id - """ - if self.filter_cost_center_ids: - query_inject_move_line += """ -INNER JOIN - account_analytic_account aa - ON - ml.analytic_account_id = aa.id - AND aa.id IN %s - """ - else: - query_inject_move_line += """ -LEFT JOIN - account_analytic_account aa ON ml.analytic_account_id = aa.id - """ - if self.filter_analytic_tag_ids: - query_inject_move_line += """ - INNER JOIN - move_lines_on_tags ON ml.id = move_lines_on_tags.ml_id - """ - query_inject_move_line += """ -WHERE - ra.report_id = %s -AND - """ - if only_unaffected_earnings_account: - query_inject_move_line += """ - a.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 self.filter_journal_ids: - query_inject_move_line += """ -AND - j.id IN %s - """ - 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 - """ + @api.model + def _initialize_partner(self, gen_ld_data, acc_id, prt_id, + foreign_currency): + gen_ld_data[acc_id]['partners'] = True + gen_ld_data[acc_id][prt_id] = {} + gen_ld_data[acc_id][prt_id]['id'] = prt_id + gen_ld_data[acc_id][prt_id]['init_bal'] = {} + gen_ld_data[acc_id][prt_id]['init_bal']['balance'] = 0.0 + gen_ld_data[acc_id][prt_id]['init_bal']['credit'] = 0.0 + gen_ld_data[acc_id][prt_id]['init_bal']['debit'] = 0.0 + gen_ld_data[acc_id][prt_id]['fin_bal'] = {} + gen_ld_data[acc_id][prt_id]['fin_bal']['credit'] = 0.0 + gen_ld_data[acc_id][prt_id]['fin_bal']['debit'] = 0.0 + gen_ld_data[acc_id][prt_id]['fin_bal']['balance'] = 0.0 + if foreign_currency: + gen_ld_data[acc_id][prt_id]['init_bal']['bal_curr'] = 0.0 + gen_ld_data[acc_id][prt_id]['fin_bal']['bal_curr'] = 0.0 + return gen_ld_data + + def _initialize_account(self, gen_ld_data, acc_id, foreign_currency): + gen_ld_data[acc_id] = {} + gen_ld_data[acc_id]['id'] = acc_id + gen_ld_data[acc_id]['partners'] = False + gen_ld_data[acc_id]['init_bal'] = {} + gen_ld_data[acc_id]['init_bal']['balance'] = 0.0 + gen_ld_data[acc_id]['init_bal']['credit'] = 0.0 + gen_ld_data[acc_id]['init_bal']['debit'] = 0.0 + gen_ld_data[acc_id]['fin_bal'] = {} + gen_ld_data[acc_id]['fin_bal']['credit'] = 0.0 + gen_ld_data[acc_id]['fin_bal']['debit'] = 0.0 + gen_ld_data[acc_id]['fin_bal']['balance'] = 0.0 + if foreign_currency: + gen_ld_data[acc_id]['init_bal']['bal_curr'] = 0.0 + gen_ld_data[acc_id]['fin_bal']['bal_curr'] = 0.0 + return gen_ld_data + + def _get_period_ml_data( + self, account_ids, partner_ids, company_id, foreign_currency, + only_posted_moves, hide_account_at_0, date_from, date_to, + partners_data, gen_ld_data, partners_ids, centralize, + analytic_tag_ids, cost_center_ids): + domain = self._get_period_domain(account_ids, partner_ids, + company_id, only_posted_moves, + date_to, date_from, + analytic_tag_ids, cost_center_ids) + ml_fields = [ + 'id', 'name', 'date', 'move_id', 'journal_id', 'account_id', + 'partner_id', 'debit', 'credit', 'balance', 'currency_id', + 'full_reconcile_id', 'tax_ids', 'analytic_tag_ids', + 'amount_currency'] + move_lines = self.env['account.move.line'].search_read( + domain=domain, + fields=ml_fields) + journal_ids = set() + full_reconcile_ids = set() + taxes_ids = set() + tags_ids = set() + full_reconcile_data = {} + acc_prt_account_ids = self._get_acc_prt_accounts_ids(company_id) + for move_line in move_lines: + journal_ids.add(move_line['journal_id'][0]) + for tax_id in move_line['tax_ids']: + taxes_ids.add(tax_id) + for analytic_tag_id in move_line['analytic_tag_ids']: + tags_ids.add(analytic_tag_id) + if move_line['full_reconcile_id']: + rec_id = move_line['full_reconcile_id'][0] + if rec_id not in full_reconcile_ids: + full_reconcile_data.update({ + rec_id: { + 'id': rec_id, + 'name': move_line['full_reconcile_id'][1]} + }) + full_reconcile_ids.add(rec_id) + acc_id = move_line['account_id'][0] + ml_id = move_line['id'] + if move_line['partner_id']: + prt_id = move_line['partner_id'][0] + partner_name = move_line['partner_id'][1] + if acc_id not in gen_ld_data.keys(): + gen_ld_data = self._initialize_account(gen_ld_data, acc_id, + foreign_currency) + if acc_id in acc_prt_account_ids: + if not move_line['partner_id']: + prt_id = 0 + partner_name = 'Missing Partner' + if gen_ld_data: + if prt_id not in gen_ld_data[acc_id]: + if prt_id not in partners_ids: + partners_ids.append(prt_id) + partners_data.update({ + prt_id: {'id': prt_id, + 'name': partner_name} + }) + gen_ld_data = self._initialize_partner( + gen_ld_data, acc_id, prt_id, foreign_currency + ) + else: + partners_ids.append(prt_id) + partners_data.update({ + prt_id: {'id': prt_id, + 'name': partner_name} + }) + gen_ld_data = self._initialize_partner( + gen_ld_data, acc_id, prt_id, foreign_currency + ) + gen_ld_data[acc_id][prt_id][ml_id] = \ + self._get_move_line_data(move_line) + gen_ld_data[acc_id][prt_id]['fin_bal']['credit'] += \ + move_line['credit'] + gen_ld_data[acc_id][prt_id]['fin_bal']['debit'] += \ + move_line['debit'] + gen_ld_data[acc_id][prt_id]['fin_bal']['balance'] += \ + move_line['balance'] + if foreign_currency: + gen_ld_data[acc_id][prt_id]['fin_bal']['bal_curr'] += \ + move_line['amount_currency'] + else: + gen_ld_data[acc_id][ml_id] = self._get_move_line_data(move_line) + gen_ld_data[acc_id]['fin_bal']['credit'] += \ + move_line['credit'] + gen_ld_data[acc_id]['fin_bal']['debit'] += \ + move_line['debit'] + gen_ld_data[acc_id]['fin_bal']['balance'] += \ + move_line['balance'] + if foreign_currency: + gen_ld_data[acc_id]['fin_bal']['bal_curr'] += \ + move_line['amount_currency'] + journals_data = self._get_journals_data(list(journal_ids)) + accounts_data = self._get_accounts_data(gen_ld_data.keys()) + taxes_data = self._get_taxes_data(list(taxes_ids)) + tags_data = self._get_tags_data(list(tags_ids)) + return gen_ld_data, accounts_data, partners_data, journals_data, \ + full_reconcile_data, taxes_data, tags_data - query_inject_move_line_params = () - if self.filter_analytic_tag_ids: - query_inject_move_line_params += ( - self.id, - tuple(self.filter_analytic_tag_ids.ids), - ) - query_inject_move_line_params += ( - self.env.uid, - ) - if self.filter_cost_center_ids: - query_inject_move_line_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_move_line_params += ( - self.id, - ) - if only_unaffected_earnings_account: - query_inject_move_line_params += ( - self.unaffected_earnings_account.id, - ) - query_inject_move_line_params += ( - self.date_from, - self.date_to, - ) - if self.filter_journal_ids: - query_inject_move_line_params += (tuple( - self.filter_journal_ids.ids, - ),) - self.env.cr.execute( - query_inject_move_line, - query_inject_move_line_params - ) + @api.model + def _create_general_ledger(self, gen_led_data, accounts_data): + general_ledger = [] + for acc_id in gen_led_data.keys(): + account = {} + account.update({ + 'code': accounts_data[acc_id]['code'], + 'name': accounts_data[acc_id]['name'], + 'type': 'account', + 'currency_id': accounts_data[acc_id]['currency_id'], + }) + if not gen_led_data[acc_id]['partners']: + move_lines = [] + for ml_id in gen_led_data[acc_id].keys(): + if not isinstance(ml_id, int): + account.update({ml_id: gen_led_data[acc_id][ml_id]}) + else: + move_lines += [gen_led_data[acc_id][ml_id]] + account.update({'move_lines': move_lines}) + else: + list_partner = [] + for prt_id in gen_led_data[acc_id].keys(): + partner = {} + move_lines = [] + if not isinstance(prt_id, int): + account.update({prt_id: gen_led_data[acc_id][prt_id]}) + else: + for ml_id in gen_led_data[acc_id][prt_id].keys(): + if not isinstance(ml_id, int): + partner.update({ml_id: gen_led_data[acc_id][ + prt_id][ml_id]}) + else: + move_lines += [ + gen_led_data[acc_id][prt_id][ml_id]] + partner.update({'move_lines': move_lines}) + list_partner += [partner] + account.update({'list_partner': list_partner}) + general_ledger += [account] + return general_ledger - def _inject_line_centralized_values(self): - """ Inject report values for report_general_ledger_move_line. + @api.model + def _get_centralized_ml(self, partners, date_to): + centralized_ml = {} + if isinstance(date_to, str): + date_to = datetime.datetime.strptime(date_to, '%Y-%m-%d').date() + for partner in partners: + for move_line in partner['move_lines']: + jnl_id = move_line['journal_id'] + month = move_line['date'].month + if jnl_id not in centralized_ml.keys(): + centralized_ml[jnl_id] = {} + if month not in centralized_ml[jnl_id].keys(): + centralized_ml[jnl_id][month] = {} + last_day_month = \ + calendar.monthrange(move_line['date'].year, month) + date = datetime.date( + move_line['date'].year, + month, + last_day_month[1]) + if date > date_to: + date = date_to + centralized_ml[jnl_id][month].update({ + 'journal_id': jnl_id, + 'ref': 'Centralized entries', + 'date': date, + 'debit': 0.0, + 'credit': 0.0, + 'balance': 0.0, + 'bal_curr': 0.0, + 'partner_id': False, + 'rec_id': 0, + 'entry_id': False, + 'tax_ids': [], + 'full_reconcile_id': False, + 'id': False, + 'tag_ids': False, + 'currency_id': False, + }) + centralized_ml[jnl_id][month]['debit'] += move_line['debit'] + centralized_ml[jnl_id][month]['credit'] += move_line['credit'] + centralized_ml[jnl_id][month]['balance'] += move_line['balance'] + centralized_ml[jnl_id][month]['bal_curr'] += move_line['bal_curr'] + list_centralized_ml = [] + for jnl_id in centralized_ml.keys(): + list_centralized_ml += list(centralized_ml[jnl_id].values()) + return list_centralized_ml - Only centralized accounts are computed. - """ - if self.filter_analytic_tag_ids: - query_inject_move_line_centralized = """ - WITH - move_lines_on_tags AS - ( - SELECT - DISTINCT ml.id AS ml_id - FROM - report_general_ledger_account ra - INNER JOIN - account_move_line ml - ON ra.account_id = ml.account_id - INNER JOIN - account_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - WHERE - ra.report_id = %s - AND - aat.id IN %s - ), - """ + @api.multi + def _get_report_values(self, docids, data): + wizard_id = data['wizard_id'] + company = self.env['res.company'].browse(data['company_id']) + company_id = data['company_id'] + date_to = data['date_to'] + date_from = data['date_from'] + partner_ids = data['partner_ids'] + if not partner_ids: + filter_partner_ids = False else: - query_inject_move_line_centralized = """ -WITH - """ - query_inject_move_line_centralized += """ - 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, - ml.currency_id AS currency_id, - ml.journal_id as journal_id - FROM - report_general_ledger_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 - """ - if self.filter_cost_center_ids: - query_inject_move_line_centralized += """ - INNER JOIN - account_analytic_account aa - ON - ml.analytic_account_id = aa.id - AND aa.id IN %s - """ - if self.filter_analytic_tag_ids: - query_inject_move_line_centralized += """ - INNER JOIN - move_lines_on_tags ON ml.id = move_lines_on_tags.ml_id - """ - query_inject_move_line_centralized += """ - 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, ml.currency_id, ml.journal_id - ) -INSERT INTO - report_general_ledger_move_line - ( - report_account_id, - create_uid, - create_date, - date, - account, - journal, - 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, - j.code AS journal, - '""" + _('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_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 -INNER JOIN - account_journal j ON ml.journal_id = j.id -LEFT JOIN - res_currency c ON ml.currency_id = c.id -WHERE - ra.report_id = %s -AND - (a.centralized IS NOT NULL AND a.centralized = TRUE) - """ - if self.filter_journal_ids: - query_inject_move_line_centralized += """ -AND - j.id in %s - """ - query_inject_move_line_centralized += """ -ORDER BY - a.code, ml.date - """ - - query_inject_move_line_centralized_params = () - if self.filter_analytic_tag_ids: - query_inject_move_line_centralized_params += ( - self.id, - tuple(self.filter_analytic_tag_ids.ids), - ) - if self.filter_cost_center_ids: - query_inject_move_line_centralized_params += ( - tuple(self.filter_cost_center_ids.ids), - ) - query_inject_move_line_centralized_params += ( - self.id, - self.date_from, - self.date_to, - self.env.uid, - self.id, - ) - if self.filter_journal_ids: - query_inject_move_line_centralized_params += (tuple( - self.filter_journal_ids.ids, - ),) - self.env.cr.execute( - query_inject_move_line_centralized, - query_inject_move_line_centralized_params - ) - - def _compute_analytic_tags(self): - """ Compute "tags" column""" - query_update_analytic_tags = """ -UPDATE - report_general_ledger_move_line -SET - tags = tags_values.tags -FROM - ( - ( - SELECT - rml.id AS report_id, - array_to_string(array_agg(t.name ORDER BY t.name), ',') AS tags - FROM - account_move_line ml - INNER JOIN - report_general_ledger_move_line rml - ON ml.id = rml.move_line_id - INNER JOIN - report_general_ledger_account ra - ON rml.report_account_id = ra.id - INNER JOIN - account_analytic_tag_account_move_line_rel tml - ON ml.id = tml.account_move_line_id - INNER JOIN - account_analytic_tag t - ON tml.account_analytic_tag_id = t.id - WHERE - ra.report_id = %(report_id)s - GROUP BY - rml.id, - ml.id - ) - UNION - ( - SELECT - rml.id AS report_id, - array_to_string(array_agg(t.name ORDER BY t.name), ',') AS tags - FROM - account_move_line ml - INNER JOIN - report_general_ledger_move_line rml - ON ml.id = rml.move_line_id - INNER JOIN - report_general_ledger_partner rp - ON rml.report_partner_id = rp.id - INNER JOIN - report_general_ledger_account ra - ON rp.report_account_id = ra.id - INNER JOIN - account_analytic_tag_account_move_line_rel tml - ON ml.id = tml.account_move_line_id - INNER JOIN - account_analytic_tag t - ON tml.account_analytic_tag_id = t.id - WHERE - ra.report_id = %(report_id)s - GROUP BY - rml.id, - ml.id - ) - ) AS tags_values -WHERE - report_general_ledger_move_line.id = tags_values.report_id - """ - params = { - 'report_id': self.id, - } - self.env.cr.execute(query_update_analytic_tags, params) - - def _inject_unaffected_earnings_account_values(self): - """Inject the report values of the unaffected earnings account - for report_general_ledger_account.""" - # Fetch the profit and loss accounts - query_unaffected_earnings_account_ids = """ - SELECT a.id - FROM account_account as a - INNER JOIN account_account_type as at - ON at.id = a.user_type_id - WHERE at.include_initial_balance = FALSE - """ - self.env.cr.execute(query_unaffected_earnings_account_ids) - pl_account_ids = [r[0] for r in self.env.cr.fetchall()] - unaffected_earnings_account_ids = \ - pl_account_ids + [self.unaffected_earnings_account.id] - # Fetch the current fiscal year start date - date = fields.Datetime.from_string(self.date_from) - res = self.company_id.compute_fiscalyear_dates(date) - fy_start_date = res['date_from'] - query_select_previous_fy_unaffected_earnings_params = { - 'date_to': fy_start_date, - 'company_id': self.company_id.id, - 'account_ids': tuple(unaffected_earnings_account_ids), - 'analytic_tag_ids': tuple(self.filter_analytic_tag_ids.ids), - } - query_select_previous_fy_unaffected_earnings = '' - q_analytic_tags = '' - if self.filter_analytic_tag_ids: - q_analytic_tags = """ -WITH move_lines_on_tags AS - ( - SELECT - DISTINCT ml.id AS ml_id - FROM - account_account a - INNER JOIN - account_move_line ml - ON a.id = ml.account_id - INNER JOIN - account_analytic_tag_account_move_line_rel atml - ON atml.account_move_line_id = ml.id - INNER JOIN - account_analytic_tag aat - ON - atml.account_analytic_tag_id = aat.id - WHERE - aat.id IN %(analytic_tag_ids)s - ) -""" - query_select_previous_fy_unaffected_earnings += q_analytic_tags - - query_select_previous_fy_unaffected_earnings += """ - SELECT sum(ml.balance) as balance - FROM account_move_line as ml - INNER JOIN account_move as am - ON am.id = ml.move_id - INNER JOIN account_journal j - ON am.journal_id = j.id - """ - if self.filter_cost_center_ids: - query_select_previous_fy_unaffected_earnings += """ - INNER JOIN account_analytic_account aa - ON ml.analytic_account_id = aa.id - AND aa.id IN %(cost_center_ids)s - """ - query_select_previous_fy_unaffected_earnings_params[ - 'cost_center_ids'] = tuple(self.filter_cost_center_ids.ids) - if self.filter_analytic_tag_ids: - query_select_previous_fy_unaffected_earnings += """ - INNER JOIN move_lines_on_tags ON ml.id = - move_lines_on_tags.ml_id - """ - query_select_previous_fy_unaffected_earnings += """ - WHERE ml.date < %(date_to)s - AND ml.company_id = %(company_id)s - AND ml.account_id IN %(account_ids)s - """ - if self.filter_journal_ids: - query_select_previous_fy_unaffected_earnings += """ - AND j.id IN %(journal_ids)s - """ - query_select_previous_fy_unaffected_earnings_params[ - 'journal_ids'] = tuple(self.filter_journal_ids.ids) - if self.only_posted_moves: - query_select_previous_fy_unaffected_earnings += """ - AND am.state = 'posted' - """ - self.env.cr.execute( - query_select_previous_fy_unaffected_earnings, - query_select_previous_fy_unaffected_earnings_params) - res = self.env.cr.fetchone() - unaffected_earnings_initial_balance = res[0] or 0.0 - # Now select the current period unaffected earnings, - # excluding the current period P&L. - query_select_period_unaffected_earnings_params = { - 'date_from': self.date_from, - 'date_to': self.date_to, - 'company_id': self.company_id.id, - 'unaffected_earnings_id': self.unaffected_earnings_account.id, - 'analytic_tag_ids': tuple(self.filter_analytic_tag_ids.ids), - } - query_select_period_unaffected_earnings = '' - if self.filter_analytic_tag_ids: - query_select_period_unaffected_earnings += q_analytic_tags - query_select_period_unaffected_earnings += """ - SELECT - sum(ml.debit) as sum_debit, - sum(ml.credit) as sum_credit, - sum(ml.balance) as balance - FROM account_move_line as ml - INNER JOIN account_move as am - ON am.id = ml.move_id - INNER JOIN account_journal j - ON am.journal_id = j.id - """ - if self.filter_cost_center_ids: - query_select_period_unaffected_earnings += """ - INNER JOIN account_analytic_account aa - ON ml.analytic_account_id = aa.id - AND aa.id IN %(cost_center_ids)s - """ - query_select_period_unaffected_earnings_params[ - 'cost_center_ids'] = tuple(self.filter_cost_center_ids.ids) - if self.filter_analytic_tag_ids: - query_select_period_unaffected_earnings += """ - INNER JOIN move_lines_on_tags - ON ml.id = move_lines_on_tags.ml_id - """ - query_select_period_unaffected_earnings += """ - WHERE am.date >= %(date_from)s - AND ml.date <= %(date_to)s - AND ml.company_id = %(company_id)s - AND ml.account_id = %(unaffected_earnings_id)s - """ - if self.filter_journal_ids: - query_select_period_unaffected_earnings += """ - AND j.id IN %(journal_ids)s - """ - query_select_period_unaffected_earnings_params[ - 'journal_ids'] = tuple(self.filter_journal_ids.ids) - if self.only_posted_moves: - query_select_period_unaffected_earnings += """ - AND am.state = 'posted' - """ - self.env.cr.execute(query_select_period_unaffected_earnings, - query_select_period_unaffected_earnings_params) - res = self.env.cr.fetchone() - unaffected_earnings_period_debit = res[0] or 0.0 - unaffected_earnings_period_credit = res[1] or 0.0 - unaffected_earnings_period_balance = res[2] or 0.0 - # pylint: disable=sql-injection - query_inject_account = """ - INSERT INTO - report_general_ledger_account ( - report_id, - create_uid, - create_date, - account_id, - code, - name, - is_partner_account, - initial_debit, - initial_credit, - initial_balance, - final_debit, - final_credit, - final_balance - ) - VALUES ( - %(report_id)s, - %(user_id)s, - NOW(), - %(account_id)s, - %(code)s, - %(name)s, - False, - %(initial_debit)s, - %(initial_credit)s, - %(initial_balance)s, - %(final_debit)s, - %(final_credit)s, - %(final_balance)s - ) - """ - initial_debit = unaffected_earnings_initial_balance >= 0 and \ - unaffected_earnings_initial_balance or 0 - initial_credit = unaffected_earnings_initial_balance < 0 and \ - -1 * unaffected_earnings_initial_balance or 0 - final_balance = unaffected_earnings_initial_balance + \ - unaffected_earnings_period_balance - query_inject_account_params = { - 'report_id': self.id, - 'user_id': self.env.uid, - 'account_id': self.unaffected_earnings_account.id, - 'code': self.unaffected_earnings_account.code, - 'name': self.unaffected_earnings_account.name, - 'initial_debit': initial_debit, - 'initial_credit': initial_credit, - 'initial_balance': unaffected_earnings_initial_balance, - 'final_debit': initial_debit + unaffected_earnings_period_debit, - 'final_credit': initial_credit + unaffected_earnings_period_credit, - 'final_balance': final_balance, + filter_partner_ids = True + account_ids = data['account_ids'] + analytic_tag_ids = data['analytic_tag_ids'] + cost_center_ids = data['cost_center_ids'] + hide_account_at_0 = data['hide_account_at_0'] + foreign_currency = data['foreign_currency'] + only_posted_moves = data['only_posted_moves'] + unaffected_earnings_account = data['unaffected_earnings_account'] + fy_start_date = data['fy_start_date'] + gen_ld_data, partners_data, partners_ids = \ + self._get_initial_balance_data( + account_ids, partner_ids, company_id, date_from, + foreign_currency, only_posted_moves, hide_account_at_0, + unaffected_earnings_account, fy_start_date, analytic_tag_ids, + cost_center_ids) + centralize = data['centralize'] + gen_ld_data, accounts_data, partners_data, journals_data, \ + full_reconcile_data, taxes_data, tags_data = \ + self._get_period_ml_data( + account_ids, partner_ids, company_id, foreign_currency, + only_posted_moves, hide_account_at_0, date_from, date_to, + partners_data, gen_ld_data, partners_ids, + centralize, analytic_tag_ids, cost_center_ids) + general_ledger = self._create_general_ledger(gen_ld_data, accounts_data) + if centralize: + for account in general_ledger: + if account['partners']: + centralized_ml = self._get_centralized_ml( + account['list_partner'], date_to) + account['move_lines'] = centralized_ml + account['partners'] = False + del account['list_partner'] + general_ledger = natsorted(general_ledger, key=itemgetter('code')) + return { + 'doc_ids': [wizard_id], + 'doc_model': 'general.ledger.report.wizard', + 'docs': self.env['general.ledger.report.wizard'].browse(wizard_id), + 'foreign_currency': data['foreign_currency'], + 'company_name': company.display_name, + 'company_currency': company.currency_id, + 'currency_name': company.currency_id.name, + 'date_from': data['date_from'], + 'date_to': data['date_to'], + 'only_posted_moves': data['only_posted_moves'], + 'hide_account_at_0': data['hide_account_at_0'], + 'show_analytic_tags': data['show_analytic_tags'], + 'general_ledger': general_ledger, + 'accounts_data': accounts_data, + 'partners_data': partners_data, + 'journals_data': journals_data, + 'full_reconcile_data': full_reconcile_data, + 'taxes_data': taxes_data, + 'centralize': centralize, + 'tags_data': tags_data, + 'filter_partner_ids': filter_partner_ids, } - self.env.cr.execute(query_inject_account, - query_inject_account_params) diff --git a/account_financial_report/report/general_ledger_xlsx.py b/account_financial_report/report/general_ledger_xlsx.py index 6f1ffa06..37fa3ab8 100644 --- a/account_financial_report/report/general_ledger_xlsx.py +++ b/account_financial_report/report/general_ledger_xlsx.py @@ -11,9 +11,15 @@ class GeneralLedgerXslx(models.AbstractModel): _name = 'report.a_f_r.report_general_ledger_xlsx' _inherit = 'report.account_financial_report.abstract_report_xlsx' - def _get_report_name(self, report): + def _get_report_name(self, report, data=False): + company_id = data.get('company_id', False) report_name = _('General Ledger') - return self._get_report_complete_name(report, report_name) + if company_id: + company = self.env['res.company'].browse(company_id) + suffix = ' - %s - %s' % ( + company.name, company.currency_id.name) + report_name = report_name + suffix + return report_name def _get_report_columns(self, report): res = { @@ -24,15 +30,15 @@ class GeneralLedgerXslx(models.AbstractModel): 4: {'header': _('Taxes'), 'field': 'taxes_description', 'width': 15}, - 5: {'header': _('Partner'), 'field': 'partner', 'width': 25}, - 6: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, + 5: {'header': _('Partner'), 'field': 'partner_name', 'width': 25}, + 6: {'header': _('Ref - Label'), 'field': 'ref', 'width': 40}, 7: {'header': _('Cost center'), 'field': 'cost_center', 'width': 15}, 8: {'header': _('Tags'), 'field': 'tags', 'width': 10}, - 9: {'header': _('Rec.'), 'field': 'matching_number', 'width': 5}, + 9: {'header': _('Rec.'), 'field': 'rec_name', 'width': 5}, 10: {'header': _('Debit'), 'field': 'debit', 'field_initial_balance': 'initial_debit', @@ -46,7 +52,7 @@ class GeneralLedgerXslx(models.AbstractModel): 'type': 'amount', 'width': 14}, 12: {'header': _('Cumul. Bal.'), - 'field': 'cumul_balance', + 'field': 'balance', 'field_initial_balance': 'initial_balance', 'field_final_balance': 'final_balance', 'type': 'amount', @@ -55,15 +61,15 @@ class GeneralLedgerXslx(models.AbstractModel): if report.foreign_currency: foreign_currency = { 13: {'header': _('Cur.'), - 'field': 'currency_id', - 'field_currency_balance': 'currency_id', - 'type': 'many2one', 'width': 7}, + 'field': 'currency_name', + 'field_currency_balance': 'currency_name', + 'type': 'currency_name', 'width': 7}, 14: {'header': _('Amount cur.'), - 'field': 'amount_currency', + 'field': 'bal_curr', 'field_initial_balance': - 'initial_balance_foreign_currency', + 'initial_bal_curr', 'field_final_balance': - 'final_balance_foreign_currency', + 'final_bal_curr', 'type': 'amount_currency', 'width': 14}, } @@ -78,8 +84,8 @@ class GeneralLedgerXslx(models.AbstractModel): ], [ _('Target moves filter'), - _('All posted entries') if report.only_posted_moves - else _('All entries'), + _('All posted entries') if report.target_move == 'all' else _( + 'All entries'), ], [ _('Account balance at 0 filter'), @@ -114,71 +120,161 @@ class GeneralLedgerXslx(models.AbstractModel): def _get_col_pos_final_balance_label(self): return 5 - def _generate_report_content(self, workbook, report): + def _generate_report_content(self, workbook, report, data): + res_data = self.env[ + 'report.account_financial_report.general_ledger' + ]._get_report_values(report, data) + general_ledger = res_data['general_ledger'] + accounts_data = res_data['accounts_data'] + partners_data = res_data['partners_data'] + journals_data = res_data['journals_data'] + taxes_data = res_data['taxes_data'] + tags_data = res_data['tags_data'] + filter_partner_ids = res_data['filter_partner_ids'] + foreign_currency = res_data['foreign_currency'] # For each account - for account in report.account_ids: + for account in general_ledger: # Write account title - self.write_array_title(account.code + ' - ' + account.name) + self.write_array_title(account['code'] + ' - ' + accounts_data[ + account['id']]['name']) - if not account.partner_ids: + if not account['partners']: # Display array header for move lines self.write_array_header() # Display initial balance line for account - self.write_initial_balance(account) + account.update({ + 'initial_debit': account['init_bal']['debit'], + 'initial_credit': account['init_bal']['credit'], + 'initial_balance': account['init_bal']['balance'], + }) + if foreign_currency: + account.update({ + 'initial_bal_curr': account['init_bal']['bal_curr'], + }) + self.write_initial_balance_from_dict(account) # Display account move lines - for line in account.move_line_ids: - self.write_line(line) + for line in account['move_lines']: + line.update({ + 'account': account['code'], + 'journal': journals_data[line['journal_id']]['code'], + }) + if line['currency_id']: + line.update({ + 'currency_name': line['currency_id'][1], + 'currency_id': line['currency_id'][0], + }) + if line['ref'] != 'Centralized entries': + taxes_description = "" + tags = "" + for tax_id in line['tax_ids']: + taxes_description += str(taxes_data[tax_id][ + 'amount'])+" "+taxes_data[tax_id]['string']+" " + for tag_id in line['tag_ids']: + tags += tags_data[tag_id]['name']+" " + line.update({ + 'taxes_description': taxes_description, + 'tags': tags, + }) + self.write_line_from_dict(line) else: # For each partner - for partner in account.partner_ids: + for partner in account['list_partner']: # Write partner title - self.write_array_title(partner.name) + self.write_array_title(partners_data[partner['id']]['name']) # Display array header for move lines self.write_array_header() # Display initial balance line for partner - self.write_initial_balance(partner) + partner.update({ + 'initial_debit': partner['init_bal']['debit'], + 'initial_credit': partner['init_bal']['credit'], + 'initial_balance': partner['init_bal']['balance'], + 'name': partners_data[partner['id']]['name'], + 'type': 'partner', + 'currency_id': accounts_data[account['id']][ + 'currency_id'], + }) + if foreign_currency: + partner.update({ + 'initial_bal_culrr': partner['init_bal'][ + 'bal_curr'], + }) + self.write_initial_balance_from_dict(partner) # Display account move lines - for line in partner.move_line_ids: - self.write_line(line) + for line in partner['move_lines']: + line.update({ + 'account': account['code'], + 'journal': journals_data[line['journal_id']]['code'] + }) + if line['ref'] != 'Centralized entries': + taxes_description = "" + tags = "" + for tax_id in line['tax_ids']: + taxes_description += \ + str(taxes_data[tax_id]['amount']) + " " + \ + taxes_data[tax_id]['string'] + " " + for tag_id in line['tag_ids']: + tags += tags_data[tag_id]['name'] + " " + line.update({ + 'taxes_description': taxes_description, + 'tags': tags, + }) + self.write_line_from_dict(line) # Display ending balance line for partner - self.write_ending_balance(partner) + partner.update({ + 'final_debit': partner['fin_bal']['debit'], + 'final_credit': partner['fin_bal']['credit'], + 'final_balance': partner['fin_bal']['balance'], + }) + if foreign_currency: + partner.update({ + 'final_bal_curr': partner['fin_bal']['bal_curr'], + }) + self.write_ending_balance_from_dict(partner) # Line break self.row_pos += 1 # Display ending balance line for account - if not report.filter_partner_ids: - self.write_ending_balance(account) + if not filter_partner_ids: + account.update({ + 'final_debit': account['fin_bal']['debit'], + 'final_credit': account['fin_bal']['credit'], + 'final_balance': account['fin_bal']['balance'], + }) + if foreign_currency: + account.update({ + 'final_bal_curr': account['fin_bal']['bal_curr'], + }) + self.write_ending_balance_from_dict(account) # 2 lines break self.row_pos += 2 - def write_initial_balance(self, my_object): + def write_initial_balance_from_dict(self, my_object): """Specific function to write initial balance for General Ledger""" - if 'partner' in my_object._name: + if 'partner' in my_object['type']: label = _('Partner Initial balance') - my_object.currency_id = my_object.report_account_id.currency_id - elif 'account' in my_object._name: + elif 'account' in my_object['type']: label = _('Initial balance') - super(GeneralLedgerXslx, self).write_initial_balance( + super(GeneralLedgerXslx, self).write_initial_balance_from_dict( my_object, label ) - def write_ending_balance(self, my_object): + def write_ending_balance_from_dict(self, my_object): """Specific function to write ending balance for General Ledger""" - if 'partner' in my_object._name: - name = my_object.name + if 'partner' in my_object['type']: + name = my_object['name'] label = _('Partner ending balance') - elif 'account' in my_object._name: - name = my_object.code + ' - ' + my_object.name + elif 'account' in my_object['type']: + name = my_object['code'] + ' - ' + my_object['name'] label = _('Ending balance') - super(GeneralLedgerXslx, self).write_ending_balance( + super(GeneralLedgerXslx, self).write_ending_balance_from_dict( my_object, name, label ) diff --git a/account_financial_report/report/journal_ledger.py b/account_financial_report/report/journal_ledger.py index 3e4d7293..04f19b01 100644 --- a/account_financial_report/report/journal_ledger.py +++ b/account_financial_report/report/journal_ledger.py @@ -1,831 +1,351 @@ -# Copyright 2017 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import models, fields, api - -DIGITS = (16, 2) - - -class ReportJournalLedger(models.TransientModel): - - _name = 'report_journal_ledger' - _inherit = 'account_financial_report_abstract' - - date_from = fields.Date( - required=True - ) - date_to = fields.Date( - required=True - ) - company_id = fields.Many2one( - comodel_name='res.company', - required=True, - ondelete='cascade' - ) - move_target = fields.Selection( - selection='_get_move_targets', - default='all', - required=True, - ) - sort_option = fields.Selection( - selection='_get_sort_options', - default='move_name', - required=True, - ) - group_option = fields.Selection( - selection='_get_group_options', - default='journal', - required=True, - ) - journal_ids = fields.Many2many( - comodel_name='account.journal', - required=True, - ) - report_journal_ledger_ids = fields.One2many( - comodel_name='report_journal_ledger_journal', - inverse_name='report_id', - ) - report_move_ids = fields.One2many( - comodel_name='report_journal_ledger_move', - inverse_name='report_id', - ) - report_move_line_ids = fields.One2many( - comodel_name='report_journal_ledger_move_line', - inverse_name='report_id', - ) - report_journal_ledger_tax_line_ids = fields.One2many( - comodel_name='report_journal_ledger_journal_tax_line', - inverse_name='report_id', - ) - report_tax_line_ids = fields.One2many( - comodel_name='report_journal_ledger_report_tax_line', - inverse_name='report_id', - ) - foreign_currency = fields.Boolean() - with_account_name = fields.Boolean() - - @api.model - def _get_move_targets(self): - return self.env['journal.ledger.report.wizard']._get_move_targets() - - @api.model - def _get_sort_options(self): - return self.env['journal.ledger.report.wizard']._get_sort_options() - - @api.model - def _get_group_options(self): - return self.env['journal.ledger.report.wizard']._get_group_options() - - @api.multi - def compute_data_for_report(self): - self.ensure_one() - self._inject_journal_values() - self._inject_move_values() - self._inject_move_line_values() - self._inject_journal_tax_values() - self._update_journal_report_total_values() - - if self.group_option == 'none': - self._inject_report_tax_values() - - # Refresh cache because all data are computed with SQL requests - self.invalidate_cache() - - @api.multi - def _inject_journal_values(self): - self.ensure_one() - sql = """ - DELETE - FROM report_journal_ledger_journal - WHERE report_id = %s - """ - params = ( - self.id, - ) - self.env.cr.execute(sql, params) - sql = """ - INSERT INTO report_journal_ledger_journal ( - create_uid, - create_date, - report_id, - journal_id, - name, - code, - company_id, - currency_id - ) - SELECT - %s as create_uid, - NOW() as create_date, - %s as report_id, - aj.id as journal_id, - aj.name as name, - aj.code as code, - aj.company_id as company_id, - COALESCE(aj.currency_id, company.currency_id) as currency_id - FROM - account_journal aj - LEFT JOIN - res_company company on (company.id = aj.company_id) - WHERE - aj.id in %s - AND - aj.company_id = %s - ORDER BY - aj.name - """ - params = ( - self.env.uid, - self.id, - tuple(self.journal_ids.ids), - self.company_id.id, - ) - self.env.cr.execute(sql, params) - - @api.multi - def _inject_move_values(self): - self.ensure_one() - sql = """ - DELETE - FROM report_journal_ledger_move - WHERE report_id = %s - """ - params = ( - self.id, - ) - self.env.cr.execute(sql, params) - sql = self._get_inject_move_insert() - sql += self._get_inject_move_select() - sql += self._get_inject_move_where_clause() - sql += self._get_inject_move_order_by() - params = self._get_inject_move_params() - self.env.cr.execute(sql, params) - - @api.multi - def _get_inject_move_insert(self): - return """ - INSERT INTO report_journal_ledger_move ( - create_uid, - create_date, - report_id, - report_journal_ledger_id, - move_id, - name, - company_id - ) - """ - - @api.multi - def _get_inject_move_select(self): - return """ - SELECT - %s as create_uid, - NOW() as create_date, - rjqj.report_id as report_id, - rjqj.id as report_journal_ledger_id, - am.id as move_id, - am.name as name, - am.company_id as company_id - FROM - account_move am - INNER JOIN - report_journal_ledger_journal rjqj - on (rjqj.journal_id = am.journal_id) - """ - - @api.multi - def _get_inject_move_where_clause(self): - self.ensure_one() - where_clause = """ - WHERE - rjqj.report_id = %s - AND - am.date >= %s - AND - am.date <= %s - """ - if self.move_target != 'all': - where_clause += """ - AND - am.state = %s - """ - return where_clause - - @api.multi - def _get_inject_move_order_by(self): - self.ensure_one() - order_by = """ - ORDER BY - """ - if self.sort_option == 'move_name': - order_by += " am.name" - elif self.sort_option == 'date': - order_by += " am.date, am.name" - return order_by - - @api.multi - def _get_inject_move_params(self): - params = [ - self.env.uid, - self.id, - self.date_from, - self.date_to +# Copyright 2019-20 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, models +import operator +import itertools + + +class JournalLedgerReport(models.AbstractModel): + _name = 'report.account_financial_report.journal_ledger' + _description = "Journal Ledger Report" + + def _get_journal_ledger_data(self, journal): + return { + 'id': journal.id, + 'name': journal.name, + 'currency_id': journal.currency_id.id, + 'currency_name': journal.currency_id and + journal.currency_id.name or + journal.company_id.currency_id.name, + 'debit': 0.0, + 'credit': 0.0, + } + + def _get_journal_ledgers_domain(self, wizard, journal_ids, company): + domain = [] + if company: + domain += [('company_id', '=', company.id)] + if journal_ids: + domain += [('id', 'in', journal_ids)] + return domain + + def _get_journal_ledgers(self, wizard, journal_ids, company): + journals = self.env['account.journal'].search( + self._get_journal_ledgers_domain(wizard, journal_ids, company), + order='name asc') + journal_ledgers_data = [] + for journal in journals: + journal_ledgers_data.append( + self._get_journal_ledger_data(journal)) + return journal_ledgers_data + + def _get_moves_domain(self, wizard, journal_ids): + domain = [ + ('journal_id', 'in', journal_ids), + ('date', '>=', wizard.date_from), + ('date', '<=', wizard.date_to), + ] + if wizard.move_target != 'all': + domain += [('state', '=', wizard.move_target)] + return domain + + def _get_moves_order(self, wizard, journal_ids): + search_order = '' + if wizard.sort_option == 'move_name': + search_order = 'name asc' + elif wizard.sort_option == 'date': + search_order = 'date asc, name asc' + return search_order + + def _get_moves_data(self, move): + return { + 'move_id': move.id, + 'journal_id': move.journal_id.id, + 'entry': move.name, + } + + def _get_moves(self, wizard, journal_ids): + moves = self.env['account.move'].search( + self._get_moves_domain(wizard, journal_ids), + order=self._get_moves_order(wizard, journal_ids)) + Moves = [] + move_data = {} + for move in moves: + move_data[move.id] = self._get_moves_data(move) + Moves.append(move_data[move.id]) + return moves.ids, Moves, move_data + + def _get_move_lines_domain(self, move_ids, wizard, journal_ids): + + return [ + ('move_id', 'in', move_ids), ] - if self.move_target != 'all': - params.append(self.move_target) - - return tuple(params) - - @api.multi - def _inject_move_line_values(self): - self.ensure_one() - sql = """ - DELETE - FROM report_journal_ledger_move_line - WHERE report_id = %s - """ - params = ( - self.id, - ) - self.env.cr.execute(sql, params) - sql = """ - INSERT INTO report_journal_ledger_move_line ( - create_uid, - create_date, - report_id, - report_journal_ledger_id, - report_move_id, - move_line_id, - account_id, - account, - account_code, - account_type, - partner_id, - partner, - date, - entry, - label, - debit, - credit, - company_currency_id, - amount_currency, - currency_id, - currency_name, - tax_id, - taxes_description, - company_id - ) - SELECT - %s as create_uid, - NOW() as create_date, - rjqm.report_id as report_id, - rjqm.report_journal_ledger_id as report_journal_ledger_id, - rjqm.id as report_move_id, - aml.id as move_line_id, - aml.account_id as account_id, - aa.name as account, - aa.code as account_code, - aa.internal_type as account_type, - aml.partner_id as partner_id, - p.name as partner, - aml.date as date, - rjqm.name as entry, - aml.name as label, - aml.debit as debit, - aml.credit as credit, - aml.company_currency_id as currency_id, - aml.amount_currency as amount_currency, - aml.currency_id as currency_id, - currency.name as currency_name, - aml.tax_line_id as tax_id, - CASE - WHEN - aml.tax_line_id is not null - THEN - COALESCE(at.description, at.name) - WHEN - aml.tax_line_id is null - THEN - (SELECT - array_to_string( - array_agg(COALESCE(at.description, at.name) - ), ', ') - FROM - account_move_line_account_tax_rel aml_at_rel - LEFT JOIN - account_tax at on (at.id = aml_at_rel.account_tax_id) - WHERE - aml_at_rel.account_move_line_id = aml.id) - ELSE - '' - END as taxes_description, - aml.company_id as company_id - FROM - account_move_line aml - INNER JOIN - report_journal_ledger_move rjqm - on (rjqm.move_id = aml.move_id) - LEFT JOIN - account_account aa - on (aa.id = aml.account_id) - LEFT JOIN - res_partner p - on (p.id = aml.partner_id) - LEFT JOIN - account_tax at - on (at.id = aml.tax_line_id) - LEFT JOIN - res_currency currency - on (currency.id = aml.currency_id) - WHERE - rjqm.report_id = %s - """ - params = ( - self.env.uid, - self.id, - ) - self.env.cr.execute(sql, params) - - @api.multi - def _inject_report_tax_values(self): - self.ensure_one() - sql_distinct_tax_id = """ - SELECT - distinct(jrqjtl.tax_id) - FROM - report_journal_ledger_journal_tax_line jrqjtl - WHERE - jrqjtl.report_id = %s - """ - self.env.cr.execute(sql_distinct_tax_id, (self.id,)) - rows = self.env.cr.fetchall() - tax_ids = set([row[0] for row in rows]) - - sql = """ - INSERT INTO report_journal_ledger_report_tax_line ( - create_uid, - create_date, - report_id, - tax_id, - tax_name, - tax_code, - base_debit, - base_credit, - tax_debit, - tax_credit - ) - SELECT - %s as create_uid, - NOW() as create_date, - %s as report_id, - %s as tax_id, - at.name as tax_name, - at.description as tax_code, - ( - SELECT sum(base_debit) - FROM report_journal_ledger_journal_tax_line jrqjtl2 - WHERE jrqjtl2.report_id = %s - AND jrqjtl2.tax_id = %s - ) as base_debit, - ( - SELECT sum(base_credit) - FROM report_journal_ledger_journal_tax_line jrqjtl2 - WHERE jrqjtl2.report_id = %s - AND jrqjtl2.tax_id = %s - ) as base_credit, - ( - SELECT sum(tax_debit) - FROM report_journal_ledger_journal_tax_line jrqjtl2 - WHERE jrqjtl2.report_id = %s - AND jrqjtl2.tax_id = %s - ) as tax_debit, - ( - SELECT sum(tax_credit) - FROM report_journal_ledger_journal_tax_line jrqjtl2 - WHERE jrqjtl2.report_id = %s - AND jrqjtl2.tax_id = %s - ) as tax_credit - FROM - report_journal_ledger_journal_tax_line jrqjtl - LEFT JOIN - account_tax at - on (at.id = jrqjtl.tax_id) - WHERE - jrqjtl.report_id = %s - AND - jrqjtl.tax_id = %s - """ - - for tax_id in tax_ids: - params = ( - self.env.uid, - self.id, - tax_id, - self.id, - tax_id, - self.id, - tax_id, - self.id, - tax_id, - self.id, - tax_id, - self.id, - tax_id, - ) - self.env.cr.execute(sql, params) - - @api.multi - def _inject_journal_tax_values(self): - self.ensure_one() - sql = """ - DELETE - FROM report_journal_ledger_journal_tax_line - WHERE report_id = %s - """ - params = ( - self.id, - ) - self.env.cr.execute(sql, params) - sql_distinct_tax_id = """ - SELECT - distinct(jrqml.tax_id) - FROM - report_journal_ledger_move_line jrqml - WHERE - jrqml.report_journal_ledger_id = %s - """ - - tax_ids_by_journal_id = {} - for report_journal in self.report_journal_ledger_ids: - if report_journal.id not in tax_ids_by_journal_id: - tax_ids_by_journal_id[report_journal.id] = [] - self.env.cr.execute(sql_distinct_tax_id, (report_journal.id,)) - rows = self.env.cr.fetchall() - tax_ids_by_journal_id[report_journal.id].extend([ - row[0] for row in rows if row[0] - ]) - - sql = """ - INSERT INTO report_journal_ledger_journal_tax_line ( - create_uid, - create_date, - report_id, - report_journal_ledger_id, - tax_id, - tax_name, - tax_code, - base_debit, - base_credit, - tax_debit, - tax_credit - ) - SELECT - %s as create_uid, - NOW() as create_date, - %s as report_id, - %s as report_journal_ledger_id, - %s as tax_id, - at.name as tax_name, - at.description as tax_code, - ( - SELECT sum(debit) - FROM report_journal_ledger_move_line jrqml2 - WHERE jrqml2.report_journal_ledger_id = %s - AND ( - SELECT - count(*) - FROM - account_move_line_account_tax_rel aml_at_rel - WHERE - aml_at_rel.account_move_line_id = - jrqml2.move_line_id - AND - aml_at_rel.account_tax_id = %s - ) > 0 - ) as base_debit, - ( - SELECT sum(credit) - FROM report_journal_ledger_move_line jrqml2 - WHERE jrqml2.report_journal_ledger_id = %s - AND ( - SELECT - count(*) - FROM - account_move_line_account_tax_rel aml_at_rel - WHERE - aml_at_rel.account_move_line_id = - jrqml2.move_line_id - AND - aml_at_rel.account_tax_id = %s - ) > 0 - ) as base_credit, - ( - SELECT sum(debit) - FROM report_journal_ledger_move_line jrqml2 - WHERE jrqml2.report_journal_ledger_id = %s - AND jrqml2.tax_id = %s - ) as tax_debit, - ( - SELECT sum(credit) - FROM report_journal_ledger_move_line jrqml2 - WHERE jrqml2.report_journal_ledger_id = %s - AND jrqml2.tax_id = %s - ) as tax_credit - FROM - report_journal_ledger_journal rjqj + def _get_move_lines_order(self, move_ids, wizard, journal_ids): + return '' + + def _get_move_lines_data(self, ml, wizard, ml_taxes): + base_debit = base_credit = tax_debit = tax_credit = \ + base_balance = tax_balance = 0.0 + if ml.tax_exigible: + base_debit = ml_taxes and ml.debit or 0.0 + base_credit = ml_taxes and ml.credit or 0.0 + base_balance = ml_taxes and ml.balance or 0.0 + tax_debit = ml.tax_line_id and ml.debit or 0.0 + tax_credit = ml.tax_line_id and ml.credit or 0.0 + tax_balance = ml.tax_line_id and ml.balance or 0.0 + return { + 'move_line_id': ml.id, + 'move_id': ml.move_id.id, + 'date': ml.date, + 'journal_id': ml.journal_id.id, + 'account_id': ml.account_id.id, + 'partner_id': ml.partner_id.id, + 'label': ml.name, + 'debit': ml.debit, + 'credit': ml.credit, + 'company_currency_id': ml.company_currency_id.id, + 'amount_currency': ml.amount_currency, + 'currency_id': ml.currency_id.id, + 'tax_line_id': ml.tax_line_id.id, + 'tax_ids': list(ml_taxes.keys()), + 'base_debit': base_debit, + 'base_credit': base_credit, + 'tax_debit': tax_debit, + 'tax_credit': tax_credit, + 'base_balance': base_balance, + 'tax_balance': tax_balance, + } + + def _get_account_data(self, accounts): + data = {} + for account in accounts: + data[account.id] = self._get_account_id_data(account) + return data + + def _get_account_id_data(self, account): + return { + 'name': account.name, + 'code': account.code, + 'internal_type': account.internal_type, + } + + def _get_partner_data(self, partners): + data = {} + for partner in partners: + data[partner.id] = self._get_partner_id_data(partner) + return data + + def _get_partner_id_data(self, partner): + return { + 'name': partner.name, + } + + def _get_currency_data(self, currencies): + data = {} + for currency in currencies: + data[currency.id] = self._get_currency_id_data(currency) + return data + + def _get_currency_id_data(self, currency): + return { + 'name': currency.name, + } + + def _get_tax_line_data(self, taxes): + data = {} + for tax in taxes: + data[tax.id] = self._get_tax_line_id_data(tax) + return data + + def _get_tax_line_id_data(self, tax): + return { + 'name': tax.name, + 'description': tax.description, + } + + def _get_query_taxes(self): + return """ + SELECT aml_at_rel.account_move_line_id, aml_at_rel.account_tax_id, + at.description, at.name + FROM account_move_line_account_tax_rel AS aml_at_rel LEFT JOIN - account_tax at - on (at.id = %s) - WHERE - rjqj.id = %s - """ - - for report_journal_ledger_id in tax_ids_by_journal_id: - tax_ids = tax_ids_by_journal_id[report_journal_ledger_id] - for tax_id in tax_ids: - params = ( - self.env.uid, - self.id, - report_journal_ledger_id, - tax_id, - report_journal_ledger_id, - tax_id, - report_journal_ledger_id, - tax_id, - report_journal_ledger_id, - tax_id, - report_journal_ledger_id, - tax_id, - tax_id, - report_journal_ledger_id, - ) - self.env.cr.execute(sql, params) - - @api.multi - def _update_journal_report_total_values(self): - self.ensure_one() - sql = """ - UPDATE - report_journal_ledger_journal rjqj - SET - debit = ( - SELECT sum(rjqml.debit) - FROM report_journal_ledger_move_line rjqml - WHERE rjqml.report_journal_ledger_id = rjqj.id - ), - credit = ( - SELECT sum(rjqml.credit) - FROM report_journal_ledger_move_line rjqml - WHERE rjqml.report_journal_ledger_id = rjqj.id - ) - WHERE - rjqj.report_id = %s - """ - self.env.cr.execute(sql, (self.id,)) - - @api.multi - def print_report(self, report_type): - self.ensure_one() - if report_type == 'xlsx': - report_name = 'a_f_r.report_journal_ledger_xlsx' - else: - report_name = 'account_financial_report.' \ - 'report_journal_ledger_qweb' - return self.env['ir.actions.report'].search( - [('report_name', '=', report_name), - ('report_type', '=', report_type)], - limit=1).report_action(self, config=False) - - def _get_html(self): - result = {} - rcontext = {} - context = dict(self.env.context) - report = self.browse(context.get('active_id')) - if report: - rcontext['o'] = report - result['html'] = self.env.ref( - 'account_financial_report.report_journal_ledger').render( - rcontext) - return result - - @api.model - def get_html(self, given_context=None): - return self._get_html() - - @api.model - def _transient_vacuum(self, force=False): - """Remove journal ledger subtables first for avoiding a costly - ondelete operation. + account_tax AS at on (at.id = aml_at_rel.account_tax_id) + WHERE account_move_line_id IN %(move_line_ids)s """ - # Next 3 lines adapted from super method for mimicking behavior - cls = type(self) - if not force and (cls._transient_check_count < 21): - return True # no vacuum cleaning this time - self.env.cr.execute("DELETE FROM report_journal_ledger_move_line") - self.env.cr.execute("DELETE FROM report_journal_ledger_move") - return super(ReportJournalLedger, self)._transient_vacuum(force=force) - - -class ReportJournalLedgerJournal(models.TransientModel): - - _name = 'report_journal_ledger_journal' - _inherit = 'account_financial_report_abstract' - - name = fields.Char( - required=True, - ) - code = fields.Char() - report_id = fields.Many2one( - comodel_name='report_journal_ledger', - required=True, - ondelete='cascade' - ) - journal_id = fields.Many2one( - comodel_name='account.journal', - required=True, - ondelete='cascade', - ) - report_move_ids = fields.One2many( - comodel_name='report_journal_ledger_move', - inverse_name='report_journal_ledger_id', - ) - report_tax_line_ids = fields.One2many( - comodel_name='report_journal_ledger_journal_tax_line', - inverse_name='report_journal_ledger_id', - ) - debit = fields.Float( - digits=DIGITS, - ) - credit = fields.Float( - digits=DIGITS, - ) - company_id = fields.Many2one( - comodel_name='res.company', - required=True, - ondelete='cascade' - ) - currency_id = fields.Many2one( - comodel_name='res.currency', - ) - - -class ReportJournalLedgerMove(models.TransientModel): - - _name = 'report_journal_ledger_move' - _inherit = 'account_financial_report_abstract' - - report_id = fields.Many2one( - comodel_name='report_journal_ledger', - required=True, - ondelete='cascade' - ) - report_journal_ledger_id = fields.Many2one( - comodel_name='report_journal_ledger_journal', - required=True, - ondelete='cascade', - ) - move_id = fields.Many2one( - comodel_name='account.move', - required=True, - ondelete='cascade', - ) - report_move_line_ids = fields.One2many( - comodel_name='report_journal_ledger_move_line', - inverse_name='report_move_id', - ) - name = fields.Char() - company_id = fields.Many2one( - comodel_name='res.company', - required=True, - ondelete='cascade' - ) - - -class ReportJournalLedgerMoveLine(models.TransientModel): - _name = 'report_journal_ledger_move_line' - _inherit = 'account_financial_report_abstract' - _order = 'partner_id desc, account_id desc' - - report_id = fields.Many2one( - comodel_name='report_journal_ledger', - required=True, - ondelete='cascade' - ) - report_journal_ledger_id = fields.Many2one( - comodel_name='report_journal_ledger_journal', - required=True, - ondelete='cascade', - ) - report_move_id = fields.Many2one( - comodel_name='report_journal_ledger_move', - required=True, - ondelete='cascade', - ) - move_line_id = fields.Many2one( - comodel_name='account.move.line', - required=True, - ondelete='cascade', - ) - account_id = fields.Many2one( - comodel_name='account.account', - string='Account ID', - ) - account = fields.Char() - account_code = fields.Char() - account_type = fields.Char() - partner = fields.Char() - partner_id = fields.Many2one( - comodel_name='res.partner', - string='Partner ID', - ) - date = fields.Date() - entry = fields.Char() - label = fields.Char() - debit = fields.Float( - digits=DIGITS, - ) - credit = fields.Float( - digits=DIGITS, - ) - company_currency_id = fields.Many2one( - comodel_name='res.currency', - ) - amount_currency = fields.Monetary( - currency_field='currency_id', - ) - currency_id = fields.Many2one( - comodel_name='res.currency', - ) - currency_name = fields.Char() - taxes_description = fields.Char() - tax_id = fields.Many2one( - comodel_name='account.tax' - ) - company_id = fields.Many2one( - comodel_name='res.company', - required=True, - ondelete='cascade' - ) - - -class ReportJournalLedgerReportTaxLine(models.TransientModel): - - _name = 'report_journal_ledger_report_tax_line' - _inherit = 'account_financial_report_abstract' - _order = 'tax_code' - - report_id = fields.Many2one( - comodel_name='report_journal_ledger', - required=True, - ondelete='cascade' - ) - tax_id = fields.Many2one( - comodel_name='account.tax' - ) - tax_name = fields.Char() - tax_code = fields.Char() - base_debit = fields.Float( - digits=DIGITS, - ) - base_credit = fields.Float( - digits=DIGITS, - ) - base_balance = fields.Float( - digits=DIGITS, - compute='_compute_base_balance', - ) - tax_debit = fields.Float( - digits=DIGITS, - ) - tax_credit = fields.Float( - digits=DIGITS, - ) - tax_balance = fields.Float( - digits=DIGITS, - compute='_compute_tax_balance' - ) + def _get_query_taxes_params(self, move_lines): + return { + 'move_line_ids': tuple(move_lines.ids), + } + + def _get_move_lines(self, move_ids, wizard, journal_ids): + move_lines = self.env['account.move.line'].search( + self._get_move_lines_domain(move_ids, wizard, journal_ids), + order=self._get_move_lines_order(move_ids, wizard, journal_ids)) + # Get the taxes ids for the move lines + query_taxes_params = self._get_query_taxes_params(move_lines) + query_taxes = self._get_query_taxes() + move_line_ids_taxes_data = {} + self.env.cr.execute(query_taxes, + query_taxes_params) + # Fetch the taxes associated to the move line + for move_line_id, account_tax_id, tax_description, tax_name in \ + self.env.cr.fetchall(): + if move_line_id not in move_line_ids_taxes_data.keys(): + move_line_ids_taxes_data[move_line_id] = {} + move_line_ids_taxes_data[move_line_id][account_tax_id] = { + 'name': tax_name, + 'description': tax_description + } + Move_Lines = {} + accounts = self.env['account.account'] + partners = self.env['res.partner'] + currencies = self.env['res.currency'] + tax_lines = self.env['account.tax'] + for ml in move_lines: + if ml.account_id not in accounts: + accounts |= ml.account_id + if ml.partner_id not in partners: + partners |= ml.partner_id + if ml.currency_id not in currencies: + currencies |= ml.currency_id + if ml.tax_line_id not in tax_lines: + tax_lines |= ml.tax_line_id + if ml.move_id.id not in Move_Lines.keys(): + Move_Lines[ml.move_id.id] = [] + taxes = ml.id in move_line_ids_taxes_data.keys() and \ + move_line_ids_taxes_data[ml.id] or {} + Move_Lines[ml.move_id.id].append(self._get_move_lines_data( + ml, wizard, taxes)) + account_ids_data = self._get_account_data(accounts) + partner_ids_data = self._get_partner_data(partners) + currency_ids_data = self._get_currency_data(currencies) + tax_line_ids_data = self._get_tax_line_data(tax_lines) + return move_lines.ids, Move_Lines, account_ids_data, \ + partner_ids_data, currency_ids_data, tax_line_ids_data, \ + move_line_ids_taxes_data + + def _get_journal_tax_lines(self, wizard, moves_data): + journals_taxes_data = {} + for move_data in moves_data: + report_move_lines = move_data['report_move_lines'] + for report_move_line in report_move_lines: + ml_data = report_move_line + tax_ids = [] + if ml_data['tax_line_id']: + tax_ids.append(ml_data['tax_line_id']) + if ml_data['tax_ids']: + tax_ids += ml_data['tax_ids'] + tax_ids = list(set(tax_ids)) + journal_id = ml_data['journal_id'] + if journal_id not in journals_taxes_data.keys(): + journals_taxes_data[journal_id] = {} + taxes = self.env['account.tax'].browse(tax_ids) + for tax in taxes: + if tax.id not in journals_taxes_data[journal_id]: + journals_taxes_data[journal_id][tax.id] = { + 'base_debit': 0.0, + 'base_credit': 0.0, + 'base_balance': 0.0, + 'tax_debit': 0.0, + 'tax_credit': 0.0, + 'tax_balance': 0.0, + 'tax_name': tax.name, + 'tax_code': tax.description, + } + field_keys = ['base_debit', 'base_credit', 'base_balance', + 'tax_debit', 'tax_credit', 'tax_balance', + ] + for field_key in field_keys: + journals_taxes_data[journal_id][tax.id][field_key] += \ + ml_data[field_key] + journals_taxes_data_2 = {} + for journal_id in journals_taxes_data.keys(): + journals_taxes_data_2[journal_id] = [] + for tax_id in journals_taxes_data[journal_id].keys(): + journals_taxes_data_2[journal_id] += \ + [journals_taxes_data[journal_id][tax_id]] + return journals_taxes_data_2 @api.multi - def _compute_base_balance(self): - for rec in self: - rec.base_balance = rec.base_debit - rec.base_credit - - @api.multi - def _compute_tax_balance(self): - for rec in self: - rec.tax_balance = rec.tax_debit - rec.tax_credit - - -class ReportJournalLedgerJournalTaxLine(models.TransientModel): - - _name = 'report_journal_ledger_journal_tax_line' - _inherit = 'report_journal_ledger_report_tax_line' - _order = 'tax_code' - - report_journal_ledger_id = fields.Many2one( - comodel_name='report_journal_ledger_journal', - required=True, - ondelete='cascade', - ) + def _get_report_values(self, docids, data): + wizard_id = data['wizard_id'] + wizard = self.env['journal.ledger.report.wizard'].browse(wizard_id) + company = self.env['res.company'].browse(data['company_id']) + journal_ids = data['journal_ids'] + journal_ledgers_data = self._get_journal_ledgers( + wizard, journal_ids, company) + move_ids, moves_data, move_ids_data = self._get_moves( + wizard, journal_ids) + journal_moves_data = {} + for key, items in itertools.groupby( + moves_data, operator.itemgetter('journal_id')): + if key not in journal_moves_data.keys(): + journal_moves_data[key] = [] + journal_moves_data[key] += list(items) + move_lines_data = account_ids_data = partner_ids_data = \ + currency_ids_data = tax_line_ids_data = \ + move_line_ids_taxes_data = {} + if move_ids: + move_line_ids, move_lines_data, account_ids_data, \ + partner_ids_data, currency_ids_data, tax_line_ids_data, \ + move_line_ids_taxes_data = self._get_move_lines( + move_ids, wizard, journal_ids) + for move_data in moves_data: + move_id = move_data['move_id'] + move_data['report_move_lines'] = [] + if move_id in move_lines_data.keys(): + move_data['report_move_lines'] += move_lines_data[move_id] + journals_taxes_data = {} + if moves_data: + journals_taxes_data = self._get_journal_tax_lines( + wizard, moves_data) + for journal_ledger_data in journal_ledgers_data: + journal_id = journal_ledger_data['id'] + journal_ledger_data['tax_lines'] = \ + journals_taxes_data.get(journal_id, []) + journal_totals = {} + for move_id in move_lines_data.keys(): + for move_line_data in move_lines_data[move_id]: + journal_id = move_line_data['journal_id'] + if journal_id not in journal_totals.keys(): + journal_totals[journal_id] = { + 'debit': 0.0, + 'credit': 0.0, + } + for item in ['debit', 'credit']: + journal_totals[journal_id][item] += move_line_data[item] + for journal_ledger_data in journal_ledgers_data: + journal_id = journal_ledger_data['id'] + if journal_id in journal_moves_data.keys(): + journal_ledger_data['report_moves'] = \ + journal_moves_data[journal_id] + else: + journal_ledger_data['report_moves'] = [] + if journal_id in journal_totals.keys(): + for item in ['debit', 'credit']: + journal_ledger_data[item] += \ + journal_totals[journal_id][item] + return { + 'doc_ids': [wizard_id], + 'doc_model': 'journal.ledger.report.wizard', + 'docs': self.env['journal.ledger.report.wizard'].browse(wizard_id), + 'group_option': data['group_option'], + 'foreign_currency': data['foreign_currency'], + 'with_account_name': data['with_account_name'], + 'company_name': company.display_name, + 'currency_name': company.currency_id.name, + 'date_from': data['date_from'], + 'date_to': data['date_to'], + 'move_target': data['move_target'], + 'account_ids_data': account_ids_data, + 'partner_ids_data': partner_ids_data, + 'currency_ids_data': currency_ids_data, + 'move_ids_data': move_ids_data, + 'tax_line_data': tax_line_ids_data, + 'move_line_ids_taxes_data': move_line_ids_taxes_data, + 'Journal_Ledgers': journal_ledgers_data, + 'Moves': moves_data, + } diff --git a/account_financial_report/report/journal_ledger_xlsx.py b/account_financial_report/report/journal_ledger_xlsx.py index 0d0bc559..d8159b43 100644 --- a/account_financial_report/report/journal_ledger_xlsx.py +++ b/account_financial_report/report/journal_ledger_xlsx.py @@ -10,9 +10,15 @@ class JournalLedgerXslx(models.AbstractModel): _name = 'report.a_f_r.report_journal_ledger_xlsx' _inherit = 'report.account_financial_report.abstract_report_xlsx' - def _get_report_name(self, report): + def _get_report_name(self, report, data=False): + company_id = data.get('company_id', False) report_name = _('Journal Ledger') - return self._get_report_complete_name(report, report_name) + if company_id: + company = self.env['res.company'].browse(company_id) + suffix = ' - %s - %s' % ( + company.name, company.currency_id.name) + report_name = report_name + suffix + return report_name def _get_report_columns(self, report): columns = [ @@ -36,7 +42,7 @@ class JournalLedgerXslx(models.AbstractModel): if report.with_account_name: columns.append({ 'header': _('Account Name'), - 'field': 'account', + 'field': 'account_name', 'width': 15 }) @@ -74,9 +80,9 @@ class JournalLedgerXslx(models.AbstractModel): columns += [ { 'header': _('Currency'), - 'field': 'currency_id', - 'type': 'many2one', - 'width': 14 + 'field': 'currency_name', + 'width': 14, + 'type': 'currency_name', }, { 'header': _('Amount Currency'), @@ -181,51 +187,61 @@ class JournalLedgerXslx(models.AbstractModel): _('Journals'), ', '.join([ "%s - %s" % (report_journal.code, report_journal.name) - for report_journal in report.report_journal_ledger_ids + for report_journal in report.journal_ids ]) ] ] - def _generate_report_content(self, workbook, report): + def _generate_report_content(self, workbook, report, data): + res_data = self.env[ + 'report.account_financial_report.journal_ledger' + ]._get_report_values(report, data) group_option = report.group_option if group_option == 'journal': - for report_journal in report.report_journal_ledger_ids: - self._generate_journal_content(workbook, report_journal) + for ledger in res_data['Journal_Ledgers']: + self._generate_journal_content(workbook, report, res_data, + ledger) elif group_option == 'none': - self._generate_no_group_content(workbook, report) + self._generate_no_group_content(workbook, report, + res_data) - def _generate_no_group_content(self, workbook, report): + def _generate_no_group_content(self, workbook, report, res_data): self._generate_moves_content( - workbook, report, "Report", report.report_move_ids) - self._generate_no_group_taxes_summary(workbook, report) + workbook, "Report", report, res_data, res_data['Moves']) + self._generate_no_group_taxes_summary(workbook, report, res_data) - def _generate_journal_content(self, workbook, report_journal): + def _generate_journal_content(self, workbook, report, res_data, ledger): + journal = self.env['account.journal'].browse(ledger['id']) + currency_name = journal.currency_id and journal.currency_id.name or \ + journal.company_id.currency_id.name sheet_name = "%s (%s) - %s" % ( - report_journal.code, - report_journal.currency_id.name, - report_journal.name, + journal.code, + currency_name, + journal.name, ) self._generate_moves_content( - workbook, report_journal.report_id, sheet_name, - report_journal.report_move_ids) - self._generate_journal_taxes_summary(workbook, report_journal) + workbook, sheet_name, report, res_data, ledger['report_moves']) + self._generate_journal_taxes_summary(workbook, ledger) - def _generate_no_group_taxes_summary(self, workbook, report): + def _generate_no_group_taxes_summary(self, workbook, report, res_data): self._generate_taxes_summary( - workbook, report, "Tax Report", report.report_tax_line_ids) + workbook, "Tax Report", res_data['tax_line_data']) - def _generate_journal_taxes_summary(self, workbook, report_journal): + def _generate_journal_taxes_summary(self, workbook, ledger): + journal = self.env['account.journal'].browse(ledger['id']) + currency_name = journal.currency_id and journal.currency_id.name or \ + journal.company_id.currency_id.name sheet_name = "Tax - %s (%s) - %s" % ( - report_journal.code, - report_journal.currency_id.name, - report_journal.name, + journal.code, + currency_name, + journal.name, ) - report = report_journal.report_id self._generate_taxes_summary( - workbook, report, sheet_name, report_journal.report_tax_line_ids) + workbook, sheet_name, ledger['tax_lines']) - def _generate_moves_content(self, workbook, report, sheet_name, moves): + def _generate_moves_content(self, workbook, sheet_name, report, res_data, + moves): self.workbook = workbook self.sheet = workbook.add_worksheet(sheet_name) self._set_column_width() @@ -236,15 +252,45 @@ class JournalLedgerXslx(models.AbstractModel): self.row_pos += 2 self.write_array_header() + account_ids_data = res_data['account_ids_data'] + partner_ids_data = res_data['partner_ids_data'] + currency_ids_data = res_data['currency_ids_data'] + move_ids_data = res_data['move_ids_data'] for move in moves: - for line in move.report_move_line_ids: - self.write_line(line) + for line in move['report_move_lines']: + currency_data = currency_ids_data.get( + line['currency_id'], False) + currency_name = currency_data and currency_data['name'] or '' + account_data = account_ids_data.get(line['account_id'], False) + account_name = account_data and account_data['name'] or '' + account_code = account_data and account_data['code'] or '' + move_data = move_ids_data.get(line['move_id'], False) + move_entry = move_data and move_data['entry'] or '' + line['partner'] = self._get_partner_name(line['partner_id'], + partner_ids_data) + line['account_code'] = account_code + line['account_name'] = account_name + line['currency_name'] = currency_name + line['entry'] = move_entry + line['taxes_description'] = \ + report._get_ml_tax_description( + line, res_data['tax_line_data'].get( + line['tax_line_id']), + res_data['move_line_ids_taxes_data'].get( + line['move_line_id'], False)) + self.write_line_from_dict(line) self.row_pos += 1 - def _generate_taxes_summary(self, workbook, report, sheet_name, tax_lines): + def _generate_taxes_summary(self, workbook, sheet_name, tax_lines_dict): self.workbook = workbook self.sheet = workbook.add_worksheet(sheet_name) self.row_pos = 1 self.write_array_title(sheet_name) self.row_pos += 2 + + def _get_partner_name(self, partner_id, partner_data): + if partner_id in partner_data.keys(): + return partner_data[partner_id]['name'] + else: + return '' diff --git a/account_financial_report/report/open_items.py b/account_financial_report/report/open_items.py index 1ad2a944..e484f063 100644 --- a/account_financial_report/report/open_items.py +++ b/account_financial_report/report/open_items.py @@ -1,957 +1,296 @@ - # © 2016 Julien Coux (Camptocamp) +# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api, _ - - -class OpenItemsReport(models.TransientModel): - """ Here, we just define class fields. - For methods, go more bottom at this file. - - The class hierarchy is : - * OpenItemsReport - ** OpenItemsReportAccount - *** OpenItemsReportPartner - **** OpenItemsReportMoveLine - """ - - _name = 'report_open_items' - _inherit = 'account_financial_report_abstract' - - # Filters fields, used for data computation - date_at = fields.Date() - only_posted_moves = fields.Boolean() - hide_account_at_0 = fields.Boolean() - foreign_currency = 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') - - # Data fields, used to browse report data - account_ids = fields.One2many( - comodel_name='report_open_items_account', - inverse_name='report_id' - ) - - -class OpenItemsReportAccount(models.TransientModel): - - _name = 'report_open_items_account' - _inherit = 'account_financial_report_abstract' - _order = 'code ASC' - - report_id = fields.Many2one( - comodel_name='report_open_items', - ondelete='cascade', - index=True - ) +from odoo import models, api +from odoo.tools import float_is_zero +from odoo.osv import expression +from datetime import date, datetime +import pandas as pd - # Data fields, used to keep link with real object - account_id = fields.Many2one( - 'account.account', - index=True - ) - # Data fields, used for report display - code = fields.Char() - name = fields.Char() - currency_id = fields.Many2one('res.currency') - final_amount_residual = fields.Float(digits=(16, 2)) - final_amount_total_due = fields.Float(digits=(16, 2)) - final_amount_residual_currency = fields.Float(digits=(16, 2)) - final_amount_total_due_currency = fields.Float(digits=(16, 2)) - - # Data fields, used to browse report data - partner_ids = fields.One2many( - comodel_name='report_open_items_partner', - inverse_name='report_account_id' - ) - - -class OpenItemsReportPartner(models.TransientModel): - - _name = 'report_open_items_partner' - _inherit = 'account_financial_report_abstract' - - report_account_id = fields.Many2one( - comodel_name='report_open_items_account', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - partner_id = fields.Many2one( - 'res.partner', - index=True - ) - - # Data fields, used for report display - name = fields.Char() - currency_id = fields.Many2one('res.currency') - final_amount_residual = fields.Float(digits=(16, 2)) - final_amount_total_due = fields.Float(digits=(16, 2)) - final_amount_residual_currency = fields.Float(digits=(16, 2)) - final_amount_total_due_currency = fields.Float(digits=(16, 2)) - - # Data fields, used to browse report data - move_line_ids = fields.One2many( - comodel_name='report_open_items_move_line', - inverse_name='report_partner_id' - ) +class OpenItemsReport(models.AbstractModel): + _name = 'report.account_financial_report.open_items' + _description = "Open Items Report" @api.model - def _generate_order_by(self, order_spec, query): - """Custom order to display "No partner allocated" at last position.""" - return """ -ORDER BY - CASE - WHEN "report_open_items_partner"."partner_id" IS NOT NULL - THEN 0 - ELSE 1 - END, - "report_open_items_partner"."name" - """ - - -class OpenItemsReportMoveLine(models.TransientModel): - - _name = 'report_open_items_move_line' - _inherit = 'account_financial_report_abstract' - - report_partner_id = fields.Many2one( - comodel_name='report_open_items_partner', - ondelete='cascade', - index=True - ) - - # Data fields, used to keep link with real object - move_line_id = fields.Many2one('account.move.line') - - # Data fields, used for report display - date = fields.Date() - date_due = fields.Date() - entry = fields.Char() - journal = fields.Char() - account = fields.Char() - partner = fields.Char() - label = fields.Char() - amount_total_due = fields.Float(digits=(16, 2)) - amount_residual = fields.Float(digits=(16, 2)) - currency_id = fields.Many2one('res.currency') - amount_total_due_currency = fields.Float(digits=(16, 2)) - amount_residual_currency = fields.Float(digits=(16, 2)) - - -class OpenItemsReportCompute(models.TransientModel): - """ Here, we just define methods. - For class fields, go more top at this file. - """ - - _inherit = 'report_open_items' - - @api.multi - def print_report(self, report_type): - self.ensure_one() - if report_type == 'xlsx': - report_name = 'a_f_r.report_open_items_xlsx' - else: - report_name = 'account_financial_report.' \ - 'report_open_items_qweb' - return self.env['ir.actions.report'].search( - [('report_name', '=', report_name), - ('report_type', '=', report_type)], - limit=1).report_action(self, config=False) + def get_html(self, given_context=None): + return self._get_html() def _get_html(self): result = {} rcontext = {} context = dict(self.env.context) - report = self.browse(context.get('active_id')) - if report: - rcontext['o'] = report - result['html'] = self.env.ref( - 'account_financial_report.report_open_items').render( - rcontext) + rcontext.update(context.get('data')) + active_id = context.get('active_id') + wiz = self.env['open.items.report.wizard'].browse(active_id) + rcontext['o'] = wiz + result['html'] = self.env.ref( + 'account_financial_report.report_open_items').render(rcontext) return result - @api.model - def get_html(self, given_context=None): - return self._get_html() - - @api.multi - def compute_data_for_report(self): - self.ensure_one() - # Compute report data - self._inject_account_values() - self._inject_partner_values() - self._inject_line_values() - self._inject_line_values(only_empty_partner_line=True) - self._clean_partners_and_accounts() - self._compute_partners_and_accounts_cumul() - if self.hide_account_at_0: - self._clean_partners_and_accounts( - only_delete_account_balance_at_0=True + def _get_account_partial_reconciled(self, move_lines_data, date_at_object): + reconciled_ids = [] + for move_line in move_lines_data: + if move_line['reconciled']: + reconciled_ids += [move_line['id']] + domain = [('max_date', '>=', date_at_object)] + domain += expression.OR( + [[('debit_move_id', 'in', reconciled_ids)], + [('credit_move_id', 'in', reconciled_ids)]]) + fields = ['debit_move_id', 'credit_move_id', 'amount'] + accounts_partial_reconcile = \ + self.env['account.partial.reconcile'].search_read( + domain=domain, + fields=fields ) - # Refresh cache because all data are computed with SQL requests - self.invalidate_cache() + debit_accounts_partial_amount = {} + credit_accounts_partial_amount = {} + for account_partial_reconcile_data in accounts_partial_reconcile: + debit_move_id = account_partial_reconcile_data['debit_move_id'][0] + credit_move_id = account_partial_reconcile_data['credit_move_id'][0] + if debit_move_id not in debit_accounts_partial_amount.keys(): + debit_accounts_partial_amount[debit_move_id] = 0.0 + debit_accounts_partial_amount[debit_move_id] += \ + account_partial_reconcile_data['amount'] + if credit_move_id not in credit_accounts_partial_amount.keys(): + credit_accounts_partial_amount[credit_move_id] = 0.0 + credit_accounts_partial_amount[credit_move_id] += \ + account_partial_reconcile_data['amount'] + account_partial_reconcile_data.update({ + 'debit_move_id': debit_move_id, + 'credit_move_id': credit_move_id, + }) + return accounts_partial_reconcile, debit_accounts_partial_amount, \ + credit_accounts_partial_amount - def _inject_account_values(self): - """Inject report values for report_open_items_account.""" - query_inject_account = """ -WITH - accounts AS - ( - SELECT - a.id, - a.code, - a.name, - a.user_type_id, - c.id as currency_id - FROM - account_account a - INNER JOIN - account_move_line ml ON a.id = ml.account_id AND ml.date <= %s - LEFT JOIN - res_currency c ON a.currency_id = c.id - """ - if self.filter_partner_ids: - query_inject_account += """ - INNER JOIN - res_partner p ON ml.partner_id = p.id - """ - if self.only_posted_moves: - query_inject_account += """ - INNER JOIN - account_move m ON ml.move_id = m.id AND m.state = 'posted' - """ - query_inject_account += """ - WHERE - a.company_id = %s - AND a.reconcile IS true - """ - 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 - """ - query_inject_account += """ - GROUP BY - a.id, c.id - ) -INSERT INTO - report_open_items_account - ( - report_id, - create_uid, - create_date, - account_id, - currency_id, - code, - name - ) -SELECT - %s AS report_id, - %s AS create_uid, - NOW() AS create_date, - a.id AS account_id, - a.currency_id, - a.code, - a.name -FROM - accounts a - """ - query_inject_account_params = ( - self.date_at, - 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.id, - self.env.uid, - ) - self.env.cr.execute(query_inject_account, query_inject_account_params) - - def _inject_partner_values(self): - """ Inject report values for report_open_items_partner. """ - # pylint: disable=sql-injection - 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_open_items_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 AND ml.date <= %s - """ - if self.only_posted_moves: - query_inject_partner += """ - INNER JOIN - account_move m ON ml.move_id = m.id AND m.state = 'posted' - """ - query_inject_partner += """ - LEFT JOIN - res_partner p ON ml.partner_id = p.id - WHERE - ra.report_id = %s - """ - 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 - ) -INSERT INTO - report_open_items_partner - ( - report_account_id, - create_uid, - create_date, - partner_id, - name - ) -SELECT - ap.report_account_id, - %s AS create_uid, - NOW() AS create_date, - ap.partner_id, - ap.partner_name -FROM - accounts_partners ap - """ - query_inject_partner_params = ( - self.date_at, - self.id, - ) - if self.filter_partner_ids: - query_inject_partner_params += ( - tuple(self.filter_partner_ids.ids), - ) - query_inject_partner_params += ( - self.env.uid, - ) - self.env.cr.execute(query_inject_partner, query_inject_partner_params) - - def _get_line_sub_query_move_lines(self, - only_empty_partner_line=False, - positive_balance=True): - """ Return subquery used to compute sum amounts on lines """ - sub_query = """ - SELECT - ml.id, - ml.balance, - SUM( - CASE - WHEN ml_past.id IS NOT NULL - THEN pr.amount - ELSE NULL - END - ) AS partial_amount, - ml.amount_currency, - SUM( - CASE - WHEN ml_past.id IS NOT NULL - THEN pr.amount_currency - ELSE NULL - END - ) AS partial_amount_currency, - ml.currency_id - FROM - report_open_items_partner rp - INNER JOIN - report_open_items_account ra - ON rp.report_account_id = ra.id - INNER JOIN - account_move_line ml - ON ra.account_id = ml.account_id - """ - if not only_empty_partner_line: - sub_query += """ - AND rp.partner_id = ml.partner_id - """ - elif only_empty_partner_line: - sub_query += """ - AND ml.partner_id IS NULL - """ - if not positive_balance: - sub_query += """ - LEFT JOIN - account_partial_reconcile pr - ON ml.balance < 0 AND pr.credit_move_id = ml.id - LEFT JOIN - account_move_line ml_future - ON ml.balance < 0 AND pr.debit_move_id = ml_future.id - AND ml_future.date > %s - LEFT JOIN - account_move_line ml_past - ON ml.balance < 0 AND pr.debit_move_id = ml_past.id - AND ml_past.date <= %s - """ + @api.model + def _get_query_domain(self, account_ids, partner_ids, date_at_object, + target_move, company_id, date_from): + query = """ + WHERE aml.account_id in %s and aml.company_id = %s + """ % (tuple(account_ids) if len(account_ids) > 1 else "(%s)" % + account_ids[0], company_id) + if date_from: + query += " and aml.date >= '%s'" % date_from + if partner_ids: + query += " and aml.partner_id in %s" % (tuple(partner_ids),) + if target_move == 'posted': + query += " and am.state = 'posted'" + if date_at_object >= date.today(): + query += " and aml.reconciled IS FALSE" else: - sub_query += """ - LEFT JOIN - account_partial_reconcile pr - ON ml.balance > 0 AND pr.debit_move_id = ml.id - LEFT JOIN - account_move_line ml_future - ON ml.balance > 0 AND pr.credit_move_id = ml_future.id - AND ml_future.date > %s - LEFT JOIN - account_move_line ml_past - ON ml.balance > 0 AND pr.credit_move_id = ml_past.id - AND ml_past.date <= %s - """ - sub_query += """ - WHERE - ra.report_id = %s - GROUP BY - ml.id, - ml.balance, - ml.amount_currency - HAVING - ( - ml.full_reconcile_id IS NULL - OR MAX(ml_future.id) IS NOT NULL - ) - """ - return sub_query - - def _inject_line_values(self, only_empty_partner_line=False): - """ Inject report values for report_open_items_move_line. - - The "only_empty_partner_line" value is used - to compute data without partner. - """ - query_inject_move_line = """ -WITH - move_lines_amount AS - ( - """ - query_inject_move_line += self._get_line_sub_query_move_lines( - only_empty_partner_line=only_empty_partner_line, - positive_balance=True - ) - query_inject_move_line += """ - UNION - """ - query_inject_move_line += self._get_line_sub_query_move_lines( - only_empty_partner_line=only_empty_partner_line, - positive_balance=False - ) - query_inject_move_line += """ - ), - move_lines AS - ( - SELECT - id, - CASE - WHEN SUM(partial_amount) > 0 - THEN - CASE - WHEN balance > 0 - THEN balance - SUM(partial_amount) - ELSE balance + SUM(partial_amount) - END - ELSE balance - END AS amount_residual, - CASE - WHEN SUM(partial_amount_currency) > 0 - THEN - CASE - WHEN amount_currency > 0 - THEN amount_currency - SUM(partial_amount_currency) - ELSE amount_currency + SUM(partial_amount_currency) - END - ELSE amount_currency - END AS amount_residual_currency, - currency_id - FROM - move_lines_amount - GROUP BY - id, - balance, - amount_currency, - currency_id - ) -INSERT INTO - report_open_items_move_line - ( - report_partner_id, - create_uid, - create_date, - move_line_id, - date, - date_due, - entry, - journal, - account, - partner, - label, - amount_total_due, - amount_residual, - currency_id, - amount_total_due_currency, - amount_residual_currency - ) -SELECT - rp.id AS report_partner_id, - %s AS create_uid, - NOW() AS create_date, - ml.id AS move_line_id, - ml.date, - ml.date_maturity, - 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, - ml.balance, - ml2.amount_residual, - c.id AS currency_id, - ml.amount_currency, - ml2.amount_residual_currency -FROM - report_open_items_partner rp -INNER JOIN - report_open_items_account ra ON rp.report_account_id = ra.id -INNER JOIN - account_move_line ml ON ra.account_id = ml.account_id -INNER JOIN - move_lines ml2 - ON ml.id = ml2.id - AND ml2.amount_residual IS NOT NULL - AND ml2.amount_residual != 0 -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 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 ml2.currency_id = c.id -WHERE - ra.report_id = %s -AND - ml.date <= %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 not only_empty_partner_line: - query_inject_move_line += """ -ORDER BY - a.code, p.name, ml.date, ml.id - """ - elif 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.date_at, - self.date_at, - self.id, - self.date_at, - self.date_at, - self.id, - self.env.uid, - self.id, - self.date_at,) - ) + query += """ and ((aml.reconciled IS FALSE OR aml.date >= '%s') + OR aml.full_reconcile_id IS NOT NULL)""" % date_at_object + return query - def _compute_partners_and_accounts_cumul(self): - """ Compute cumulative amount for - report_open_items_partner and report_open_items_account. - """ - self._compute_partner_cumul() - self._compute_account_cumul() - - def _compute_partner_cumul(self): - query_computer_partner_residual_cumul = """ -UPDATE - report_open_items_partner -SET - final_amount_residual = - ( - SELECT - SUM(rml.amount_residual) AS final_amount_residual - FROM - report_open_items_move_line rml - WHERE - rml.report_partner_id = report_open_items_partner.id - ) -WHERE - id IN - ( - SELECT - rp.id - FROM - report_open_items_account ra - INNER JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - WHERE - ra.report_id = %s - ) -""" - params_compute_partners_residual_cumul = (self.id,) - self.env.cr.execute(query_computer_partner_residual_cumul, - params_compute_partners_residual_cumul) - - query_compute_partners_due_cumul = """ -UPDATE - report_open_items_partner -SET - final_amount_total_due = - ( - SELECT - SUM(rml.amount_total_due) AS final_amount_total_due - FROM - report_open_items_move_line rml - WHERE - rml.report_partner_id = report_open_items_partner.id - ) -WHERE - id IN - ( - SELECT - rp.id - FROM - report_open_items_account ra - INNER JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - WHERE - ra.report_id = %s - ) -""" - params_compute_partner_due_cumul = (self.id,) - self.env.cr.execute(query_compute_partners_due_cumul, - params_compute_partner_due_cumul) - - # Manage currency in partner - query_compute_partners_cur_id_cumul = """ -UPDATE - report_open_items_partner -SET - currency_id = - ( - SELECT - MAX(currency_id) as currency_id - FROM - report_open_items_move_line rml - WHERE - rml.report_partner_id = report_open_items_partner.id - ) -WHERE - id IN - ( - SELECT - rp.id - FROM - report_open_items_account ra - INNER JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - WHERE - ra.report_id = %s AND ra.currency_id IS NOT NULL - ) -""" - params_compute_partners_cur_id_cumul = (self.id,) - self.env.cr.execute(query_compute_partners_cur_id_cumul, - params_compute_partners_cur_id_cumul) - - query_compute_partners_cur_residual_cumul = """ -UPDATE - report_open_items_partner -SET - final_amount_residual_currency = - ( - SELECT - SUM(rml.amount_residual_currency) - AS final_amount_residual_currency - FROM - report_open_items_move_line rml - WHERE - rml.report_partner_id = report_open_items_partner.id - ) -WHERE - id IN - ( - SELECT - rp.id - FROM - report_open_items_account ra - INNER JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - WHERE - ra.report_id = %s AND ra.currency_id IS NOT NULL - ) -""" - params_compute_partners_cur_residual_cumul = (self.id,) - self.env.cr.execute(query_compute_partners_cur_residual_cumul, - params_compute_partners_cur_residual_cumul) - - query_compute_partners_cur_due_cumul = """ -UPDATE - report_open_items_partner -SET - final_amount_total_due_currency = - ( - SELECT - SUM(rml.amount_total_due_currency) - AS final_amount_total_due_currency - FROM - report_open_items_move_line rml - WHERE - rml.report_partner_id = report_open_items_partner.id - ) -WHERE - id IN - ( - SELECT - rp.id - FROM - report_open_items_account ra - INNER JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - WHERE - ra.report_id = %s AND ra.currency_id IS NOT NULL - ) -""" - params_compute_partners_cur_due_cumul = (self.id,) - self.env.cr.execute(query_compute_partners_cur_due_cumul, - params_compute_partners_cur_due_cumul) - - def _compute_account_cumul(self): - query_compute_accounts_residual_cumul = """ -UPDATE - report_open_items_account -SET - final_amount_residual = - ( - SELECT - SUM(rp.final_amount_residual) AS final_amount_residual - FROM - report_open_items_partner rp - WHERE - rp.report_account_id = report_open_items_account.id - ) -WHERE - report_id = %s - """ - params_compute_accounts_residual_cumul = (self.id,) - self.env.cr.execute(query_compute_accounts_residual_cumul, - params_compute_accounts_residual_cumul) - - query_compute_accounts_cur_residual_cumul = """ -UPDATE - report_open_items_account -SET - final_amount_residual_currency = - ( - SELECT - SUM(rp.final_amount_residual_currency) - AS final_amount_residual_currency - FROM - report_open_items_partner rp - WHERE - rp.report_account_id = report_open_items_account.id - ) -WHERE - report_id = %s - """ - params_compute_accounts_cur_residual_cumul = (self.id,) - self.env.cr.execute(query_compute_accounts_cur_residual_cumul, - params_compute_accounts_cur_residual_cumul) - - query_compute_accounts_due_cumul = """ -UPDATE - report_open_items_account -SET - final_amount_total_due = - ( - SELECT - SUM(rp.final_amount_total_due) AS final_amount_total_due - FROM - report_open_items_partner rp - WHERE - rp.report_account_id = report_open_items_account.id - ) -WHERE - report_id = %s + @api.model + def _get_query(self, account_ids, partner_ids, date_at_object, + target_move, company_id, date_from): + aml_fields = [ + 'id', 'date', 'move_id', 'journal_id', 'account_id', 'partner_id', + 'ref', 'date_maturity', 'amount_residual', 'amount_currency', + 'amount_residual_currency', 'debit', 'credit', 'currency_id', + 'reconciled', 'full_reconcile_id'] + query = "" + + # SELECT + for field in aml_fields: + if not query: + query = "SELECT aml.%s" % field + else: + query += ", aml.%s" % field + # name from res_partner + query += ", rp.name as partner_name" + # name from res_currency + query += ", rc.name as currency_name" + # state and name from account_move + query += ", am.state, am.name as move_name" + + # FROM + query += """ + FROM account_move_line as aml + LEFT JOIN res_partner as rp + ON aml.partner_id = rp.id + LEFT JOIN res_currency as rc + ON aml.currency_id = rc.id + LEFT JOIN account_move as am + ON am.id = aml.move_id """ - params_compute_accounts_due_cumul = (self.id,) - self.env.cr.execute(query_compute_accounts_due_cumul, - params_compute_accounts_due_cumul) - query_compute_accounts_cur_due_cumul = """ -UPDATE - report_open_items_account -SET - final_amount_total_due_currency = - ( - SELECT - SUM(rp.final_amount_total_due_currency) - AS final_amount_total_due_currency - FROM - report_open_items_partner rp - WHERE - rp.report_account_id = report_open_items_account.id - ) -WHERE - report_id = %s - """ - params_compute_accounts_cur_due_cumul = (self.id,) - self.env.cr.execute(query_compute_accounts_cur_due_cumul, - params_compute_accounts_cur_due_cumul) + # WHERE + query += self._get_query_domain(account_ids, partner_ids, + date_at_object, target_move, + company_id, date_from) + return query + + def _get_accounts_data(self, accounts_ids): + accounts = self.env['account.account'].browse(accounts_ids) + accounts_data = {} + for account in accounts: + accounts_data.update({account.id: { + 'id': account.id, + 'code': account.code, + 'name': account.name, + 'hide_account': False, + 'currency_id': account.currency_id or False, + 'currency_name': account.currency_id.name} + }) + return accounts_data + + def _get_journals_data(self, journals_ids): + journals = self.env['account.journal'].browse(journals_ids) + journals_data = {} + for journal in journals: + journals_data.update({journal.id: {'id': journal.id, + 'code': journal.code}}) + return journals_data + + def _get_data(self, account_ids, partner_ids, date_at_object, target_move, + company_id, date_from): + query = self._get_query(account_ids, partner_ids, date_at_object, + target_move, company_id, date_from) + self._cr.execute(query) + move_lines_data = pd.DataFrame(self._cr.dictfetchall()) + account_ids = set(move_lines_data.account_id.to_list()) + accounts_data = self._get_accounts_data(list(account_ids)) + journal_ids = set(move_lines_data.journal_id.to_list()) + journals_data = self._get_journals_data(list(journal_ids)) + move_lines_data = move_lines_data.fillna(0).to_dict(orient='records') + + if date_at_object < date.today(): + accounts_partial_reconcile, debit_accounts_partial_amount, \ + credit_accounts_partial_amount = \ + self._get_account_partial_reconciled(move_lines_data, + date_at_object) + if accounts_partial_reconcile: + accounts_partial_reconcile_data = pd.DataFrame( + accounts_partial_reconcile) + debit_ids = set(accounts_partial_reconcile_data.debit_move_id + .to_list()) + credit_ids = set( + accounts_partial_reconcile_data.credit_move_id.to_list()) + for move_line in move_lines_data: + if move_line['id'] in debit_ids: + move_line['amount_residual'] += \ + debit_accounts_partial_amount[move_line['id']] + if move_line['id'] in credit_ids: + move_line['amount_residual'] -= \ + credit_accounts_partial_amount[move_line['id']] + moves_lines_to_remove = [] + for move_line in move_lines_data: + if move_line['date'] > date_at_object or float_is_zero( + move_line['amount_residual'], precision_digits=2): + moves_lines_to_remove.append(move_line) + if len(moves_lines_to_remove) > 0: + for move_line_to_remove in moves_lines_to_remove: + move_lines_data.remove(move_line_to_remove) + partners_data = { + 0: { + 'id': 0, + 'name': 'Missing Partner' + } + } + open_items_move_lines_data = {} + for move_line in move_lines_data: + no_partner = True + # Partners data + if move_line['partner_id'] and not pd.isna(move_line['partner_id']): + no_partner = False + partners_data.update({ + move_line['partner_id']: { + 'id': move_line['partner_id'], + 'name': move_line['partner_name'], + 'currency_id': accounts_data[move_line[ + 'account_id']]['currency_id'], + } + }) + else: + partners_data[0]['currency_id'] = accounts_data[move_line[ + 'account_id']]['currency_id'] + + # Move line update + original = 0 + + if not float_is_zero(move_line['credit'], precision_digits=2): + original = move_line['credit']*(-1) + if not float_is_zero(move_line['debit'], precision_digits=2): + original = move_line['debit'] + + move_line.update({ + 'date': move_line['date'].strftime("%d/%m/%Y"), + 'date_maturity': move_line['date_maturity'].strftime("%d/%m/%Y"), + 'original': original, + 'partner_id': 0 if no_partner else move_line['partner_id'], + 'partner_name': '' if no_partner else move_line['partner_name'], + 'ref': '' if not move_line['ref'] else move_line['ref'], + 'account': accounts_data[move_line['account_id']]['code'], + 'journal': journals_data[move_line['journal_id']]['code'], + }) + + # Open Items Move Lines Data + if move_line['account_id'] not in open_items_move_lines_data.keys(): + open_items_move_lines_data[move_line['account_id']] = { + move_line['partner_id']: [move_line]} + else: + if move_line['partner_id'] not in \ + open_items_move_lines_data[move_line[ + 'account_id']].keys(): + open_items_move_lines_data[move_line['account_id']][ + move_line['partner_id']] = [move_line] + else: + open_items_move_lines_data[move_line['account_id']][ + move_line['partner_id']].append(move_line) + return move_lines_data, partners_data, journals_data, accounts_data, \ + open_items_move_lines_data - def _clean_partners_and_accounts(self, - only_delete_account_balance_at_0=False): - """ Delete empty data for - report_open_items_partner and report_open_items_account. + @api.model + def _calculate_amounts(self, open_items_move_lines_data): + total_amount = {} + for account_id in open_items_move_lines_data.keys(): + total_amount[account_id] = {} + total_amount[account_id]['residual'] = 0.0 + for partner_id in open_items_move_lines_data[account_id].keys(): + total_amount[account_id][partner_id] = {} + total_amount[account_id][partner_id]['residual'] = 0.0 + for move_line in open_items_move_lines_data[account_id][ + partner_id]: + total_amount[account_id][partner_id]['residual'] += \ + move_line['amount_residual'] + total_amount[account_id]['residual'] += move_line[ + 'amount_residual'] + return total_amount - The "only_delete_account_balance_at_0" value is used - to delete also the data with cumulative amounts at 0. - """ - query_clean_partners = """ -DELETE FROM - report_open_items_partner -WHERE - id IN - ( - SELECT - DISTINCT rp.id - FROM - report_open_items_account ra - INNER JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - LEFT JOIN - report_open_items_move_line rml - ON rp.id = rml.report_partner_id - WHERE - ra.report_id = %s - """ - if not only_delete_account_balance_at_0: - query_clean_partners += """ - AND rml.id IS NULL - """ - elif only_delete_account_balance_at_0: - query_clean_partners += """ - AND ( - rp.final_amount_residual IS NULL - OR rp.final_amount_residual = 0 - ) - """ - query_clean_partners += """ - ) - """ - params_clean_partners = (self.id,) - self.env.cr.execute(query_clean_partners, params_clean_partners) - query_clean_accounts = """ -DELETE FROM - report_open_items_account -WHERE - id IN - ( - SELECT - DISTINCT ra.id - FROM - report_open_items_account ra - LEFT JOIN - report_open_items_partner rp - ON ra.id = rp.report_account_id - WHERE - ra.report_id = %s - """ - if not only_delete_account_balance_at_0: - query_clean_accounts += """ - AND rp.id IS NULL - """ - elif only_delete_account_balance_at_0: - query_clean_accounts += """ - AND ( - ra.final_amount_residual IS NULL - OR ra.final_amount_residual = 0 - ) - """ - query_clean_accounts += """ - ) - """ - params_clean_accounts = (self.id,) - self.env.cr.execute(query_clean_accounts, params_clean_accounts) + @api.multi + def _get_report_values(self, docids, data): + wizard_id = data['wizard_id'] + company = self.env['res.company'].browse(data['company_id']) + company_id = data['company_id'] + account_ids = data['account_ids'] + partner_ids = data['partner_ids'] + date_at = data['date_at'] + date_at_object = datetime.strptime(date_at, '%Y-%m-%d').date() + date_from = data['date_from'] + target_move = data['target_move'] + + move_lines_data, partners_data, journals_data, accounts_data, \ + open_items_move_lines_data = self._get_data( + account_ids, partner_ids, date_at_object, + target_move, company_id, date_from) + + total_amount = self._calculate_amounts(open_items_move_lines_data) + return{ + 'doc_ids': [wizard_id], + 'doc_model': 'open.items.report.wizard', + 'docs': self.env['open.items.report.wizard'].browse(wizard_id), + 'foreign_currency': data['foreign_currency'], + 'company_name': company.display_name, + 'currency_name': company.currency_id.name, + 'date_at': date_at_object.strftime("%d/%m/%Y"), + 'hide_account_at_0': data['hide_account_at_0'], + 'target_move': data['target_move'], + 'partners_data': partners_data, + 'accounts_data': accounts_data, + 'total_amount': total_amount, + 'Open_Items': open_items_move_lines_data, + } diff --git a/account_financial_report/report/open_items_xlsx.py b/account_financial_report/report/open_items_xlsx.py index 8bb2dcde..c3cb3a2a 100644 --- a/account_financial_report/report/open_items_xlsx.py +++ b/account_financial_report/report/open_items_xlsx.py @@ -9,44 +9,51 @@ class OpenItemsXslx(models.AbstractModel): _name = 'report.a_f_r.report_open_items_xlsx' _inherit = 'report.account_financial_report.abstract_report_xlsx' - def _get_report_name(self, report): + def _get_report_name(self, report, data=False): + company_id = data.get('company_id', False) report_name = _('Open Items') - return self._get_report_complete_name(report, report_name) + if company_id: + company = self.env['res.company'].browse(company_id) + suffix = ' - %s - %s' % ( + company.name, company.currency_id.name) + report_name = report_name + suffix + return report_name def _get_report_columns(self, report): res = { 0: {'header': _('Date'), 'field': 'date', 'width': 11}, - 1: {'header': _('Entry'), 'field': 'entry', 'width': 18}, + 1: {'header': _('Entry'), 'field': 'move_id_name', 'width': 18}, 2: {'header': _('Journal'), 'field': 'journal', 'width': 8}, 3: {'header': _('Account'), 'field': 'account', 'width': 9}, 4: {'header': _('Partner'), 'field': 'partner', 'width': 25}, - 5: {'header': _('Ref - Label'), 'field': 'label', 'width': 40}, - 6: {'header': _('Due date'), 'field': 'date_due', 'width': 11}, + 5: {'header': _('Ref - Label'), 'field': 'ref', 'width': 40}, + 6: {'header': _('Due date'), 'field': 'date_maturity', 'width': 11}, 7: {'header': _('Original'), - 'field': 'amount_total_due', + 'field': 'original', 'type': 'amount', 'width': 14}, 8: {'header': _('Residual'), 'field': 'amount_residual', - 'field_final_balance': 'final_amount_residual', + 'field_final_balance': 'residual', 'type': 'amount', 'width': 14}, } if report.foreign_currency: foreign_currency = { - 9: {'header': _('Cur.'), 'field': 'currency_id', - 'field_currency_balance': 'currency_id', - 'type': 'many2one', 'width': 7}, + 9: {'header': _('Cur.'), 'field': 'currency_name', + 'field_currency_balance': 'currency_name', + 'type': 'currency_name', + 'width': 7}, 10: {'header': _('Cur. Original'), - 'field': 'amount_total_due_currency', + 'field': 'amount_currency', 'field_final_balance': - 'final_amount_total_due_currency', + 'amount_currency', 'type': 'amount_currency', 'width': 14}, 11: {'header': _('Cur. Residual'), 'field': 'amount_residual_currency', 'field_final_balance': - 'final_amount_residual_currency', + 'amount_currency', 'type': 'amount_currency', 'width': 14}, } @@ -55,9 +62,9 @@ class OpenItemsXslx(models.AbstractModel): def _get_report_filters(self, report): return [ - [_('Date at filter'), report.date_at], + [_('Date at filter'), report.date_at.strftime("%d/%m/%Y")], [_('Target moves filter'), - _('All posted entries') if report.only_posted_moves else _( + _('All posted entries') if report.target_move == 'posted' else _( 'All entries')], [_('Account balance at 0 filter'), _('Hide') if report.hide_account_at_0 else _('Show')], @@ -77,43 +84,65 @@ class OpenItemsXslx(models.AbstractModel): def _get_col_pos_final_balance_label(self): return 5 - def _generate_report_content(self, workbook, report): + def _generate_report_content(self, workbook, report, data): + res_data = self.env[ + 'report.account_financial_report.open_items']._get_report_values( + report, data) # For each account - for account in report.account_ids: + Open_items = res_data['Open_Items'] + accounts_data = res_data['accounts_data'] + partners_data = res_data['partners_data'] + total_amount = res_data['total_amount'] + for account_id in Open_items.keys(): # Write account title - self.write_array_title(account.code + ' - ' + account.name) + self.write_array_title(accounts_data[account_id]['code'] + ' - ' + + accounts_data[account_id]['name']) # For each partner - for partner in account.partner_ids: - # Write partner title - self.write_array_title(partner.name) - - # Display array header for move lines - self.write_array_header() - - # Display account move lines - for line in partner.move_line_ids: - self.write_line(line) - - # Display ending balance line for partner - self.write_ending_balance(partner, 'partner') - - # Line break - self.row_pos += 1 - - # Display ending balance line for account - self.write_ending_balance(account, 'account') - - # 2 lines break - self.row_pos += 2 - - def write_ending_balance(self, my_object, type_object): + if Open_items[account_id]: + for partner_id in Open_items[account_id]: + type_object = 'partner' + # Write partner title + self.write_array_title(partners_data[partner_id]['name']) + + # Display array header for move lines + self.write_array_header() + + # Display account move lines + for line in Open_items[account_id][partner_id]: + self.write_line_from_dict(line) + + # Display ending balance line for partner + self.write_ending_balance_from_dict( + partners_data[partner_id], type_object, total_amount, + account_id, partner_id) + + # Line break + self.row_pos += 1 + + # Display ending balance line for account + type_object = 'account' + self.write_ending_balance_from_dict(accounts_data[account_id], + type_object, + total_amount, + account_id) + + # 2 lines break + self.row_pos += 2 + + def write_ending_balance_from_dict(self, my_object, type_object, + total_amount, account_id=False, + partner_id=False): """Specific function to write ending balance for Open Items""" if type_object == 'partner': - name = my_object.name + name = my_object['name'] + my_object['residual'] = total_amount[account_id][partner_id][ + 'residual'] label = _('Partner ending balance') - my_object.currency_id = my_object.report_account_id.currency_id elif type_object == 'account': - name = my_object.code + ' - ' + my_object.name + name = my_object['code'] + ' - ' + my_object['name'] + my_object['residual'] = total_amount[account_id][ + 'residual'] label = _('Ending balance') - super(OpenItemsXslx, self).write_ending_balance(my_object, name, label) + super(OpenItemsXslx, self).write_ending_balance_from_dict( + my_object, name, label) diff --git a/account_financial_report/report/templates/aged_partner_balance.xml b/account_financial_report/report/templates/aged_partner_balance.xml index d6816058..f738306f 100644 --- a/account_financial_report/report/templates/aged_partner_balance.xml +++ b/account_financial_report/report/templates/aged_partner_balance.xml @@ -1,7 +1,7 @@ -