You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							453 lines
						
					
					
						
							22 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							453 lines
						
					
					
						
							22 KiB
						
					
					
				| # -*- coding: utf-8 -*- | |
| ################################################################################ | |
| # | |
| #    Cybrosys Technologies Pvt. Ltd. | |
| # | |
| #    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | |
| #    Author: Bhagyadev KP (<https://www.cybrosys.com>) | |
| # | |
| #    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 <http://www.gnu.org/licenses/>. | |
| # | |
| ################################################################################ | |
| import calendar | |
| import io | |
| import json | |
| from datetime import datetime | |
| import xlsxwriter | |
| from odoo import api, fields, models | |
| from odoo.tools.date_utils import get_month, get_fiscal_year, \ | |
|     get_quarter_number, subtract | |
| 
 | |
| 
 | |
| class AccountTrialBalance(models.TransientModel): | |
|     """For creating Trial Balance report""" | |
|     _name = 'account.trial.balance' | |
|     _description = 'Trial Balance Report' | |
| 
 | |
|     @api.model | |
|     def view_report(self): | |
|         """ | |
|         Generates a trial balance report for multiple accounts. | |
|         Retrieves account information and calculates total debit and credit | |
|         amounts for each account within the specified date range. Returns a list | |
|         of dictionaries containing account details and transaction totals. | |
|  | |
|         :return: List of dictionaries representing the trial balance report. | |
|         :rtype: list | |
|         """ | |
|         account_ids = self.env['account.move.line'].search([]).mapped( | |
|             'account_id') | |
|         today = fields.Date.today() | |
|         move_line_list = [] | |
|         for account_id in account_ids: | |
|             initial_move_line_ids = self.env['account.move.line'].search( | |
|                 [('date', '<', get_month(today)[0]), | |
|                  ('account_id', '=', account_id.id), | |
|                  ('parent_state', '=', 'posted')]) | |
|             initial_total_debit = round( | |
|                 sum(initial_move_line_ids.mapped('debit')), 2) | |
|             initial_total_credit = round( | |
|                 sum(initial_move_line_ids.mapped('credit')), 2) | |
|             move_line_ids = self.env['account.move.line'].search( | |
|                 [('date', '>=', get_month(today)[0]), | |
|                  ('account_id', '=', account_id.id), | |
|                  ('date', '<=', get_month(today)[1]), | |
|                  ('parent_state', '=', 'posted')]) | |
|             total_debit = round(sum(move_line_ids.mapped('debit')), 2) | |
|             total_credit = round(sum(move_line_ids.mapped('credit')), 2) | |
|             sum_debit = initial_total_debit + total_debit | |
|             sum_credit = initial_total_credit + total_credit | |
|             diff_credit_debit = sum_debit - sum_credit | |
|             if diff_credit_debit > 0: | |
|                 end_total_debit = diff_credit_debit | |
|                 end_total_credit = 0.0 | |
|             else: | |
|                 end_total_debit = 0.0 | |
|                 end_total_credit = abs(diff_credit_debit) | |
|             data = { | |
|                 'account': account_id.display_name, | |
|                 'account_id': account_id.id, | |
|                 'journal_ids': self.env['account.journal'].search_read([], [ | |
|                     'name']), | |
|                 'initial_total_debit': initial_total_debit, | |
|                 'initial_total_credit': initial_total_credit, | |
|                 'total_debit': total_debit, | |
|                 'total_credit': total_credit, | |
|                 'end_total_debit': end_total_debit, | |
|                 'end_total_credit': end_total_credit | |
|             } | |
|             move_line_list.append(data) | |
|         journal = { | |
|             'journal_ids': self.env['account.journal'].search_read([], [ | |
|                 'name']) | |
|         } | |
|         return move_line_list, journal | |
| 
 | |
|     @api.model | |
|     def get_filter_values(self, start_date, end_date, comparison_number, | |
|                           comparison_type, journal_list, analytic, options, | |
|                           method): | |
|         """ | |
|         Retrieves and calculates filtered values for generating a financial | |
|         report. | |
|         Retrieves and processes account movement data based on the provided | |
|         filters. Calculates initial, dynamic, and end total debit and credit | |
|         amounts for each account,considering date range, comparison type, and | |
|         other filter criteria. | |
|  | |
|         :param str start_date: Start date of the reporting period. | |
|         :param str end_date: End date of the reporting period. | |
|         :param int comparison_number: Number of periods for comparison. | |
|         :param str comparison_type: Type of comparison (month, year, quarter). | |
|         :param list[int] journal_list: List of selected journal IDs. | |
|         :param list[int] analytic: List of selected analytic line IDs. | |
|         :param dict options: Additional filtering options (e.g., 'draft'). | |
|         :param dict method: Find the method. | |
|         :return: List of dictionaries representing the financial report. | |
|         :rtype: list | |
|         """ | |
|         if options == {}: | |
|             options = None | |
|         if options is None: | |
|             option_domain = ['posted'] | |
|         elif 'draft' in options: | |
|             option_domain = ['posted', 'draft'] | |
|         if method == {}: | |
|             method = None | |
|         dynamic_total_debit = {} | |
|         dynamic_date_num = {} | |
|         dynamic_total_credit = {} | |
|         account_ids = self.env['account.move.line'].search([]).mapped( | |
|             'account_id') | |
|         move_line_list = [] | |
|         start_date_first = \ | |
|             get_fiscal_year(datetime.strptime(start_date, "%Y-%m-%d").date())[ | |
|                 0] if comparison_type == 'year' else datetime.strptime( | |
|                 start_date, "%Y-%m-%d").date() | |
|         end_date_first = \ | |
|             get_fiscal_year(datetime.strptime(end_date, "%Y-%m-%d").date())[ | |
|                 1] if comparison_type == 'year' else datetime.strptime(end_date, | |
|                                                                        "%Y-%m-%d").date() | |
|         for account_id in account_ids: | |
|             start_date = start_date_first | |
|             end_date = end_date_first | |
|             if comparison_number: | |
|                 if comparison_type == 'month': | |
|                     initial_start_date = subtract(start_date, months=eval( | |
|                         comparison_number)) | |
|                 elif comparison_type == 'year': | |
|                     initial_start_date = subtract(start_date, years=eval( | |
|                         comparison_number)) | |
|                 else: | |
|                     initial_start_date = subtract(start_date, months=eval( | |
|                         comparison_number) * 3) | |
|             else: | |
|                 initial_start_date = start_date | |
|             domain = [('date', '<', initial_start_date), | |
|                       ('account_id', '=', account_id.id), | |
|                       ('parent_state', 'in', option_domain), ] | |
|             if journal_list: | |
|                 domain.append( | |
|                     ('journal_id', 'in', journal_list), ) | |
|             if analytic: | |
|                 domain.append( | |
|                     ('analytic_line_ids', 'in', analytic)) | |
|             if method is not None and 'cash' in method: | |
|                 domain.append(('journal_id', 'in', | |
|                                self.env.company.tax_cash_basis_journal_id.ids)) | |
|             initial_move_line_ids = self.env['account.move.line'].search( | |
|                 domain) | |
|             initial_total_debit = round( | |
|                 sum(initial_move_line_ids.mapped('debit')), 2) | |
|             initial_total_credit = round( | |
|                 sum(initial_move_line_ids.mapped('credit')), 2) | |
|             if comparison_number: | |
|                 if comparison_type == 'year': | |
|                     for i in range(1, eval(comparison_number) + 1): | |
|                         com_start_date = subtract(start_date, years=i) | |
|                         com_end_date = subtract(end_date, years=i) | |
|                         domain = [('date', '>=', com_start_date), | |
|                                   ('account_id', '=', account_id.id), | |
|                                   ('date', '<=', com_end_date), | |
|                                   ('parent_state', 'in', option_domain), ] | |
|                         if journal_list: | |
|                             domain.append( | |
|                                 ('journal_id', 'in', journal_list), ) | |
|                         if analytic: | |
|                             domain.append( | |
|                                 ('analytic_line_ids', 'in', analytic)) | |
|                         if method is not None and 'cash' in method: | |
|                             domain.append(('journal_id', 'in', | |
|                                            self.env.company.tax_cash_basis_journal_id.ids)) | |
|                         move_lines = self.env['account.move.line'].search( | |
|                             domain) | |
|                         dynamic_total_debit[ | |
|                             f"dynamic_total_debit_{i}"] = round( | |
|                             sum(move_lines.mapped('debit')), 2) | |
|                         dynamic_total_credit[ | |
|                             f"dynamic_total_credit_{i}"] = round( | |
|                             sum(move_lines.mapped('credit')), 2) | |
|                 if comparison_type == 'month': | |
|                     dynamic_date_num[ | |
|                         f"dynamic_date_num{0}"] = self.get_month_name( | |
|                         start_date) + ' ' + str( | |
|                         start_date.year) | |
|                     for i in range(1, eval(comparison_number) + 1): | |
|                         com_start_date = subtract(start_date, months=i) | |
|                         com_end_date = subtract(end_date, months=i) | |
|                         domain = [('date', '>=', com_start_date), | |
|                                   ('account_id', '=', account_id.id), | |
|                                   ('date', '<=', com_end_date), | |
|                                   ('parent_state', 'in', option_domain), ] | |
|                         if journal_list: | |
|                             domain.append( | |
|                                 ('journal_id', 'in', journal_list), ) | |
|                         if analytic: | |
|                             domain.append( | |
|                                 ('analytic_line_ids', 'in', analytic)) | |
|                         if method is not None and 'cash' in method: | |
|                             domain.append(('journal_id', 'in', | |
|                                            self.env.company.tax_cash_basis_journal_id.ids), ) | |
|                         move_lines = self.env['account.move.line'].search( | |
|                             domain) | |
|                         dynamic_date_num[ | |
|                             f"dynamic_date_num{i}"] = self.get_month_name( | |
|                             com_start_date) + ' ' + str( | |
|                             com_start_date.year) | |
|                         dynamic_total_debit[ | |
|                             f"dynamic_total_debit_{i}"] = round( | |
|                             sum(move_lines.mapped('debit')), 2) | |
|                         dynamic_total_credit[ | |
|                             f"dynamic_total_credit_{i}"] = round( | |
|                             sum(move_lines.mapped('credit')), 2) | |
|                 if comparison_type == 'quarter': | |
|                     dynamic_date_num[ | |
|                         f"dynamic_date_num{0}"] = 'Q' + ' ' + str( | |
|                         get_quarter_number(start_date)) + ' ' + str( | |
|                         start_date.year) | |
|                     for i in range(1, eval(comparison_number) + 1): | |
|                         com_start_date = subtract(start_date, months=i * 3) | |
|                         com_end_date = subtract(end_date, months=i * 3) | |
|                         domain = [('date', '>=', com_start_date), | |
|                                   ('account_id', '=', account_id.id), | |
|                                   ('date', '<=', com_end_date), | |
|                                   ('parent_state', 'in', option_domain), ] | |
|                         if journal_list: | |
|                             domain.append( | |
|                                 ('journal_id', 'in', journal_list), ) | |
|                         if analytic: | |
|                             domain.append( | |
|                                 ('analytic_line_ids', 'in', analytic)) | |
|                         if method is not None and 'cash' in method: | |
|                             domain.append(('journal_id', 'in', | |
|                                            self.env.company.tax_cash_basis_journal_id.ids)) | |
|                         move_lines = self.env['account.move.line'].search( | |
|                             domain) | |
|                         dynamic_date_num[ | |
|                             f"dynamic_date_num{i}"] = 'Q' + ' ' + str( | |
|                             get_quarter_number(com_start_date)) + ' ' + str( | |
|                             com_start_date.year) | |
|                         dynamic_total_debit[ | |
|                             f"dynamic_total_debit_{i}"] = round( | |
|                             sum(move_lines.mapped('debit')), 2) | |
|                         dynamic_total_credit[ | |
|                             f"dynamic_total_credit_{i}"] = round( | |
|                             sum(move_lines.mapped('credit')), 2) | |
|             domain = [('date', '>=', start_date), | |
|                       ('account_id', '=', account_id.id), | |
|                       ('date', '<=', end_date), | |
|                       ('parent_state', 'in', option_domain), ] | |
|             if journal_list: | |
|                 domain.append( | |
|                     ('journal_id', 'in', journal_list), ) | |
|             if analytic: | |
|                 domain.append( | |
|                     ('analytic_line_ids', 'in', analytic)) | |
|             if method is not None and 'cash' in method: | |
|                 domain.append(('journal_id', 'in', | |
|                                self.env.company.tax_cash_basis_journal_id.ids)) | |
|             move_line_ids = self.env['account.move.line'].search(domain) | |
|             total_debit = round(sum(move_line_ids.mapped('debit')), 2) | |
|             total_credit = round(sum(move_line_ids.mapped('credit')), 2) | |
|             sum_debit = initial_total_debit + sum( | |
|                 dynamic_total_debit.values()) + total_debit | |
|             sum_credit = initial_total_credit + sum( | |
|                 dynamic_total_credit.values()) + total_credit | |
|             diff_credit_debit = sum_debit - sum_credit | |
|             if diff_credit_debit > 0: | |
|                 end_total_debit = diff_credit_debit | |
|                 end_total_credit = 0.0 | |
|             else: | |
|                 end_total_debit = 0.0 | |
|                 end_total_credit = abs(diff_credit_debit) | |
|             data = { | |
|                 'account': account_id.display_name, | |
|                 'account_id': account_id.id, | |
|                 'journal_ids': self.env['account.journal'].search_read([], [ | |
|                     'name']), | |
|                 'initial_total_debit': initial_total_debit, | |
|                 'initial_total_credit': initial_total_credit, | |
|                 'total_debit': total_debit, | |
|                 'total_credit': total_credit, | |
|                 'end_total_debit': end_total_debit, | |
|                 'end_total_credit': end_total_credit | |
|             } | |
|             if comparison_number: | |
|                 if dynamic_date_num: | |
|                     data['dynamic_date_num'] = dynamic_date_num | |
|                 for i in range(1, eval(comparison_number) + 1): | |
|                     data[f'dynamic_total_debit_{i}'] = dynamic_total_debit.get( | |
|                         f"dynamic_total_debit_{eval(comparison_number) + 1 - i}", | |
|                         0.0) | |
|                     data[ | |
|                         f'dynamic_total_credit_{i}'] = dynamic_total_credit.get( | |
|                         f"dynamic_total_credit_{eval(comparison_number) + 1 - i}", | |
|                         0.0) | |
|             move_line_list.append(data) | |
|         return move_line_list | |
| 
 | |
|     @api.model | |
|     def get_month_name(self, date): | |
|         """ | |
|         Retrieve the abbreviated name of the month for a given date. | |
|         :param date: The date for which to retrieve the month's abbreviated name. | |
|         :type date: datetime.date | |
|         :return: Abbreviated name of the month (e.g., 'Jan', 'Feb', ..., 'Dec'). | |
|         :rtype: str | |
|         """ | |
|         month_names = calendar.month_abbr | |
|         return month_names[date.month] | |
| 
 | |
|     @api.model | |
|     def get_xlsx_report(self, data, response, report_name, report_action): | |
|         """ | |
|         Generate an XLSX report based on provided data and response stream. | |
|         Generates an Excel workbook with specified report format, including | |
|         subheadings,column headers, and row data for the given financial report | |
|         data. | |
|         :param str data: JSON-encoded data for the report. | |
|         :param response: Response object to stream the generated report. | |
|         :param str report_name: Name of the financial report. | |
|         """ | |
|         data = json.loads(data) | |
|         output = io.BytesIO() | |
|         workbook = xlsxwriter.Workbook(output, {'in_memory': True}) | |
|         start_date = data['filters']['start_date'] if \ | |
|             data['filters']['start_date'] else '' | |
|         end_date = data['filters']['end_date'] if \ | |
|             data['filters']['end_date'] else '' | |
|         head = workbook.add_format( | |
|             {'font_size': 15, 'align': 'center', 'bold': True}) | |
|         sheet = workbook.add_worksheet() | |
|         sub_heading = workbook.add_format( | |
|             {'align': 'center', 'bold': True, 'font_size': '10px', | |
|              'border': 1, 'bg_color': '#D3D3D3', | |
|              'border_color': 'black'}) | |
|         filter_head = workbook.add_format( | |
|             {'align': 'center', 'bold': True, 'font_size': '10px', | |
|              'border': 1, 'bg_color': '#D3D3D3', | |
|              'border_color': 'black'}) | |
|         filter_body = workbook.add_format( | |
|             {'align': 'center', 'bold': True, 'font_size': '10px'}) | |
|         side_heading_sub = workbook.add_format( | |
|             {'align': 'left', 'bold': True, 'font_size': '10px', | |
|              'border': 1, | |
|              'border_color': 'black'}) | |
|         side_heading_sub.set_indent(1) | |
|         txt_name = workbook.add_format({'font_size': '10px', 'border': 1}) | |
|         txt_name.set_indent(2) | |
|         sheet.set_column(0, 0, 30) | |
|         sheet.set_column(1, 1, 20) | |
|         sheet.set_column(2, 2, 15) | |
|         sheet.set_column(3, 3, 15) | |
|         col = 0 | |
|         sheet.write('A1:b1', report_name, head) | |
|         sheet.write('B3:b4', 'Date Range', filter_head) | |
|         sheet.write('B4:b4', 'Comparison', filter_head) | |
|         sheet.write('B5:b4', 'Journal', filter_head) | |
|         sheet.write('B6:b4', 'Account', filter_head) | |
|         sheet.write('B7:b4', 'Option', filter_head) | |
|         if start_date or end_date: | |
|             sheet.merge_range('C3:G3', f"{start_date} to {end_date}", | |
|                               filter_body) | |
|         if data['filters']['comparison_number_range']: | |
|             sheet.merge_range('C4:G4', | |
|                               f"{data['filters']['comparison_type']} : {data['filters']['comparison_number_range']}", | |
|                               filter_body) | |
|         if data['filters']['journal']: | |
|             display_names = [journal for | |
|                              journal in data['filters']['journal']] | |
|             display_names_str = ', '.join(display_names) | |
|             sheet.merge_range('C5:G5', display_names_str, filter_body) | |
|         if data['filters']['account']: | |
|             account_keys = [account.get('display_name', 'undefined') for | |
|                             account in data['filters']['account']] | |
|             account_keys_str = ', '.join(account_keys) | |
|             sheet.merge_range('C6:G6', account_keys_str, filter_body) | |
|         if data['filters']['options']: | |
|             option_keys = list(data['filters']['options'].keys()) | |
|             option_keys_str = ', '.join(option_keys) | |
|             sheet.merge_range('C7:G7', option_keys_str, filter_body) | |
|         sheet.write(9, col, '', sub_heading) | |
|         sheet.merge_range(9, col + 1, 9, col + 2, 'Initial Balance', | |
|                           sub_heading) | |
|         i = 3 | |
|         for date_view in data['date_viewed']: | |
|             sheet.merge_range(9, col + i, 9, col + i + 1, date_view, | |
|                               sub_heading) | |
|             i += 2 | |
|         sheet.merge_range(9, col + i, 9, col + i + 1, 'End Balance', | |
|                           sub_heading) | |
|         sheet.write(10, col, '', sub_heading) | |
|         sheet.write(10, col + 1, 'Debit', sub_heading) | |
|         sheet.write(10, col + 2, 'Credit', sub_heading) | |
|         i = 3 | |
|         for date_views in data['date_viewed']: | |
|             sheet.write(10, col + i, 'Debit', sub_heading) | |
|             i += 1 | |
|             sheet.write(10, col + i, 'Credit', sub_heading) | |
|             i += 1 | |
|         sheet.write(10, col + i, 'Debit', sub_heading) | |
|         sheet.write(10, col + (i + 1), 'Credit', sub_heading) | |
|         if data: | |
|             if report_action == 'dynamic_accounts_report.action_trial_balance': | |
|                 row = 11 | |
|                 for move_line in data['data']: | |
|                     sheet.write(row, col, move_line['account'], | |
|                                 side_heading_sub) | |
|                     sheet.write(row, col + 1, move_line['initial_total_debit'], | |
|                                 txt_name) | |
|                     sheet.write(row, col + 2, | |
|                                 move_line['initial_total_credit'], txt_name) | |
|                     j = 3 | |
|                     if data['apply_comparison']: | |
|                         number_of_periods = data['comparison_number_range'] | |
|                         for num in number_of_periods: | |
|                             sheet.write(row, col + j, move_line[ | |
|                                 'dynamic_total_debit_' + str(num)], txt_name) | |
|                             sheet.write(row, col + j + 1, move_line[ | |
|                                 'dynamic_total_credit_' + str(num)], txt_name) | |
|                             j += 2 | |
|                     sheet.write(row, col + j, move_line['total_debit'], | |
|                                 txt_name) | |
|                     sheet.write(row, col + j + 1, move_line['total_credit'], | |
|                                 txt_name) | |
|                     sheet.write(row, col + j + 2, move_line['end_total_debit'], | |
|                                 txt_name) | |
|                     sheet.write(row, col + j + 3, | |
|                                 move_line['end_total_credit'], txt_name) | |
|                     row += 1 | |
|         workbook.close() | |
|         output.seek(0) | |
|         response.stream.write(output.read()) | |
|         output.close()
 | |
| 
 |