diff --git a/addons/base_accounting_kit/report/cash_flow_report.py b/addons/base_accounting_kit/report/cash_flow_report.py new file mode 100644 index 0000000..8c21d0d --- /dev/null +++ b/addons/base_accounting_kit/report/cash_flow_report.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2025-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import time +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportFinancial(models.AbstractModel): + _name = 'report.base_accounting_kit.report_cash_flow' + _description = 'Cash Flow Report' + + def _compute_account_balance(self, accounts): + mapping = { + 'balance': "COALESCE(SUM(debit),0) - COALESCE(SUM(credit), 0) as balance", + 'debit': "COALESCE(SUM(debit), 0) as debit", + 'credit': "COALESCE(SUM(credit), 0) as credit", + } + + res = {} + for account in accounts: + res[account.id] = dict.fromkeys(mapping, 0.0) + if accounts: + tables, where_clause, where_params = self.env[ + 'account.move.line']._query_get() + tables = tables.replace('"', '') if tables else "account_move_line" + wheres = [""] + if where_clause.strip(): + wheres.append(where_clause.strip()) + filters = " AND ".join(wheres) + request = "SELECT account_id as id, " + ', '.join( + mapping.values()) + \ + " FROM " + tables + \ + " WHERE account_id IN %s " \ + + filters + \ + " GROUP BY account_id" + params = (tuple(accounts._ids),) + tuple(where_params) + self.env.cr.execute(request, params) + for row in self.env.cr.dictfetchall(): + res[row['id']] = row + return res + + def _compute_report_balance(self, reports): + res = {} + fields = ['credit', 'debit', 'balance'] + for report in reports: + if report.id in res: + continue + res[report.id] = dict((fn, 0.0) for fn in fields) + if report.type == 'accounts': + # it's the sum of credit or debit + res2 = self._compute_report_balance(report.parent_id) + for key, value in res2.items(): + cash_in_operation = self.env.ref( + 'base_accounting_kit.cash_in_from_operation0') + cash_out_operation = self.env.ref( + 'base_accounting_kit.cash_out_operation1') + cash_in_financial = self.env.ref( + 'base_accounting_kit.cash_in_financial0') + cash_out_financial = self.env.ref( + 'base_accounting_kit.cash_out_financial1') + cash_in_investing = self.env.ref( + 'base_accounting_kit.cash_in_investing0') + cash_out_investing = self.env.ref( + 'base_accounting_kit.cash_out_investing1') + if report == cash_in_operation or report == cash_in_financial or report == cash_in_investing: + res[report.id]['debit'] += value['debit'] + res[report.id]['balance'] += value['debit'] + elif report == cash_out_operation or report == cash_out_financial or report == cash_out_investing: + res[report.id]['credit'] += value['credit'] + res[report.id]['balance'] += -(value['credit']) + elif report.type == 'account_type': + # it's the sum the leaf accounts with such an account type + accounts = self.env['account.account'].search( + [('account_type', 'in', report.account_type_ids)]) + res[report.id]['account'] = self._compute_account_balance( + accounts) + for value in res[report.id]['account'].values(): + for field in fields: + res[report.id][field] += value.get(field) + elif report.type == 'account_report' and report.account_report_id: + # it's the amount of the linked + res[report.id]['account'] = self._compute_account_balance( + report.account_ids) + for value in res[report.id]['account'].values(): + for field in fields: + res[report.id][field] += value.get(field) + + elif report.type == 'sum': + # it's the sum of the linked accounts + res[report.id]['account'] = self._compute_account_balance( + report.account_ids) + for values in res[report.id]['account'].values(): + for field in fields: + res[report.id][field] += values.get(field) + return res + + def get_account_lines(self, data): + lines = [] + account_report = self.env['account.financial.report'].search( + [('id', '=', data['account_report_id'][0])]) + child_reports = account_report._get_children_by_order() + res = self.with_context( + data.get('used_context'))._compute_report_balance(child_reports) + if data['enable_filter']: + comparison_res = self.with_context( + data.get('comparison_context'))._compute_report_balance( + child_reports) + for report_id, value in comparison_res.items(): + res[report_id]['comp_bal'] = value['balance'] + report_acc = res[report_id].get('account') + if report_acc: + for account_id, val in comparison_res[report_id].get( + 'account').items(): + report_acc[account_id]['comp_bal'] = val['balance'] + + for report in child_reports: + vals = { + 'name': report.name, + 'balance': res[report.id]['balance'] * int(report.sign), + 'type': 'report', + 'level': bool(report.style_overwrite) and int( + report.style_overwrite) or report.level, + 'account_type': report.type or False, + # used to underline the financial report balances + } + if data['debit_credit']: + vals['debit'] = res[report.id]['debit'] + vals['credit'] = res[report.id]['credit'] + + if data['enable_filter']: + vals['balance_cmp'] = res[report.id]['comp_bal'] * int( + report.sign) + + lines.append(vals) + if report.display_detail == 'no_detail': + # the rest of the loop is used to display the details of the financial report, so it's not needed here. + continue + if res[report.id].get('account'): + # if res[report.id].get('debit'): + sub_lines = [] + for account_id, value in res[report.id]['account'].items(): + # if there are accounts to display, we add them to the + # lines with a level equals to their level in + # the COA + 1 (to avoid having them with a too low level + # that would conflicts with the level of data + # financial reports for Assets, liabilities...) + flag = False + account = self.env['account.account'].browse(account_id) + vals = { + 'name': account.code + ' ' + account.name, + 'balance': value['balance'] * int(report.sign) or 0.0, + 'type': 'account', + 'level': report.display_detail == 'detail_with_hierarchy' and 4, + 'account_type': account.internal_type, + } + if data['debit_credit']: + vals['debit'] = value['debit'] + vals['credit'] = value['credit'] + if not account.company_id.currency_id.is_zero( + vals[ + 'debit']) or not account.company_id.currency_id.is_zero( + vals['credit']): + flag = True + if not account.company_id.currency_id.is_zero( + vals['balance']): + flag = True + if data['enable_filter']: + vals['balance_cmp'] = value['comp_bal'] * int( + report.sign) + if not account.company_id.currency_id.is_zero( + vals['balance_cmp']): + flag = True + if flag: + sub_lines.append(vals) + lines += sorted(sub_lines, + key=lambda sub_line: sub_line['name']) + return lines + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form') or not self.env.context.get( + 'active_model') or not self.env.context.get('active_id'): + raise UserError( + _("Form content is missing, this report cannot be printed.")) + + model = self.env.context.get('active_model') + docs = self.env[model].browse(self.env.context.get('active_id')) + report_lines = self.get_account_lines(data.get('form')) + return { + 'doc_ids': self.ids, + 'doc_model': model, + 'data': data['form'], + 'docs': docs, + 'time': time, + 'get_account_lines': report_lines, + }