X-Git-Url: https://git.cyclocoop.org/?a=blobdiff_plain;f=report%2Fcommon_balance_reports.py;fp=report%2Fcommon_balance_reports.py;h=7d0945a774819235cecf3b0d91a9dab314f3ac46;hb=cddef38e342c880228694525c45df3f7dd8c2148;hp=0000000000000000000000000000000000000000;hpb=bffd331c220385010fe9abad9e228fc2004a293a;p=burette%2Faccount_financial_report_webkit.git diff --git a/report/common_balance_reports.py b/report/common_balance_reports.py new file mode 100644 index 0000000..7d0945a --- /dev/null +++ b/report/common_balance_reports.py @@ -0,0 +1,280 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Author: Guewen Baconnier +# Copyright Camptocamp SA 2011 +# SQL inspired from OpenERP original code +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from operator import add + +from .common_reports import CommonReportHeaderWebkit + + +class CommonBalanceReportHeaderWebkit(CommonReportHeaderWebkit): + """Define common helper for balance (trial balance, P&L, BS oriented financial report""" + + def _get_numbers_display(self, data): + return self._get_form_param('numbers_display', data) + + @staticmethod + def find_key_by_value_in_list(dic, value): + return [key for key, val in dic.iteritems() if value in val][0] + + def _get_account_details(self, account_ids, target_move, fiscalyear, main_filter, start, stop, initial_balance_mode, context=None): + """ + Get details of accounts to display on the report + @param account_ids: ids of accounts to get details + @param target_move: selection filter for moves (all or posted) + @param fiscalyear: browse of the fiscalyear + @param main_filter: selection filter period / date or none + @param start: start date or start period browse instance + @param stop: stop date or stop period browse instance + @param initial_balance_mode: False: no calculation, 'opening_balance': from the opening period, 'initial_balance': computed from previous year / periods + @return: dict of list containing accounts details, keys are the account ids + """ + if context is None: + context = {} + + account_obj = self.pool.get('account.account') + period_obj = self.pool.get('account.period') + use_period_ids = main_filter in ('filter_no', 'filter_period', 'filter_opening') + + if use_period_ids: + if main_filter == 'filter_opening': + period_ids = [start.id] + else: + period_ids = period_obj.build_ctx_periods(self.cursor, self.uid, start.id, stop.id) + # never include the opening in the debit / credit amounts + period_ids = self.exclude_opening_periods(period_ids) + + init_balance = False + if initial_balance_mode == 'opening_balance': + init_balance = self._read_opening_balance(account_ids, start) + elif initial_balance_mode: + init_balance = self._compute_initial_balances(account_ids, start, fiscalyear) + + ctx = context.copy() + ctx.update({'state': target_move, + 'all_fiscalyear': True}) + + if use_period_ids: + ctx.update({'periods': period_ids}) + elif main_filter == 'filter_date': + ctx.update({'date_from': start, + 'date_to': stop}) + + accounts = account_obj.read( + self.cursor, + self.uid, + account_ids, + ['type', 'code', 'name', 'debit', 'credit', 'balance', 'parent_id', 'level', 'child_id'], + ctx) + + accounts_by_id = {} + for account in accounts: + if init_balance: + # sum for top level views accounts + child_ids = account_obj._get_children_and_consol(self.cursor, self.uid, account['id'], ctx) + if child_ids: + child_init_balances = [ + init_bal['init_balance'] + for acnt_id, init_bal in init_balance.iteritems() + if acnt_id in child_ids] + top_init_balance = reduce(add, child_init_balances) + account['init_balance'] = top_init_balance + else: + account.update(init_balance[account['id']]) + account['balance'] = account['init_balance'] + account['debit'] - account['credit'] + accounts_by_id[account['id']] = account + return accounts_by_id + + def _get_comparison_details(self, data, account_ids, target_move, comparison_filter, index): + """ + + @param data: data of the wizard form + @param account_ids: ids of the accounts to get details + @param comparison_filter: selected filter on the form for the comparison (filter_no, filter_year, filter_period, filter_date) + @param index: index of the fields to get (ie. comp1_fiscalyear_id where 1 is the index) + @return: dict of account details (key = account id) + """ + fiscalyear = self._get_info(data, "comp%s_fiscalyear_id" % (index,), 'account.fiscalyear') + start_period = self._get_info(data, "comp%s_period_from" % (index,), 'account.period') + stop_period = self._get_info(data, "comp%s_period_to" % (index,), 'account.period') + start_date = self._get_form_param("comp%s_date_from" % (index,), data) + stop_date = self._get_form_param("comp%s_date_to" % (index,), data) + init_balance = self.is_initial_balance_enabled(comparison_filter) + + accounts_by_ids = {} + comp_params = {} + details_filter = comparison_filter + if comparison_filter != 'filter_no': + start_period, stop_period, start, stop = \ + self._get_start_stop_for_filter(comparison_filter, fiscalyear, start_date, stop_date, start_period, stop_period) + if comparison_filter == 'filter_year': + details_filter = 'filter_no' + + initial_balance_mode = init_balance and self._get_initial_balance_mode(start) or False + accounts_by_ids = self._get_account_details(account_ids, target_move, fiscalyear, details_filter, + start, stop, initial_balance_mode) + comp_params = { + 'comparison_filter': comparison_filter, + 'fiscalyear': fiscalyear, + 'start': start, + 'stop': stop, + 'initial_balance': init_balance, + 'initial_balance_mode': initial_balance_mode, + } + + return accounts_by_ids, comp_params + + def _get_diff(self, balance, previous_balance): + """ + @param balance: current balance + @param previous_balance: last balance + @return: dict of form {'diff': difference, 'percent_diff': diff in percentage} + """ + diff = balance - previous_balance + + obj_precision = self.pool.get('decimal.precision') + precision = obj_precision.precision_get(self.cursor, self.uid, 'Account') + # round previous balance with account precision to avoid big numbers if previous + # balance is 0.0000001 or a any very small number + if round(previous_balance, precision) == 0: + percent_diff = False + else: + percent_diff = round(diff / previous_balance * 100, precision) + + return {'diff': diff, 'percent_diff': percent_diff} + + def _comp_filters(self, data, comparison_number): + """ + @param data: data of the report + @param comparison_number: number of comparisons + @return: list of comparison filters, nb of comparisons used and comparison mode (no_comparison, single, multiple) + """ + comp_filters = [] + for index in range(comparison_number): + comp_filters.append(self._get_form_param("comp%s_filter" % (index,), data, default='filter_no')) + + nb_comparisons = len([comp_filter for comp_filter in comp_filters if comp_filter != 'filter_no']) + if not nb_comparisons: + comparison_mode = 'no_comparison' + elif nb_comparisons > 1: + comparison_mode = 'multiple' + else: + comparison_mode = 'single' + return comp_filters, nb_comparisons, comparison_mode + + def _get_start_stop_for_filter(self, main_filter, fiscalyear, start_date, stop_date, start_period, stop_period): + if main_filter in ('filter_no', 'filter_year'): + start_period = self.get_first_fiscalyear_period(fiscalyear) + stop_period = self.get_last_fiscalyear_period(fiscalyear) + elif main_filter == 'filter_opening': + opening_period = self._get_st_fiscalyear_period(fiscalyear, special=True) + start_period = stop_period = opening_period + if main_filter == 'filter_date': + start = start_date + stop = stop_date + else: + start = start_period + stop = stop_period + + return start_period, stop_period, start, stop + + def compute_balance_data(self, data, filter_report_type=None): + new_ids = data['form']['account_ids'] or data['form']['chart_account_id'] + max_comparison = self._get_form_param('max_comparison', data, default=0) + main_filter = self._get_form_param('filter', data, default='filter_no') + + comp_filters, nb_comparisons, comparison_mode = self._comp_filters(data, max_comparison) + + fiscalyear = self.get_fiscalyear_br(data) + + start_period = self.get_start_period_br(data) + stop_period = self.get_end_period_br(data) + + target_move = self._get_form_param('target_move', data, default='all') + start_date = self._get_form_param('date_from', data) + stop_date = self._get_form_param('date_to', data) + chart_account = self._get_chart_account_id_br(data) + + start_period, stop_period, start, stop = \ + self._get_start_stop_for_filter(main_filter, fiscalyear, start_date, stop_date, start_period, stop_period) + + init_balance = self.is_initial_balance_enabled(main_filter) + initial_balance_mode = init_balance and self._get_initial_balance_mode(start) or False + + # Retrieving accounts + account_ids = self.get_all_accounts(new_ids, only_type=filter_report_type) + + # get details for each accounts, total of debit / credit / balance + accounts_by_ids = self._get_account_details(account_ids, target_move, fiscalyear, main_filter, start, stop, initial_balance_mode) + + comparison_params = [] + comp_accounts_by_ids = [] + for index in range(max_comparison): + if comp_filters[index] != 'filter_no': + comparison_result, comp_params = self._get_comparison_details(data, account_ids, target_move, comp_filters[index], index) + comparison_params.append(comp_params) + comp_accounts_by_ids.append(comparison_result) + + to_display = dict.fromkeys(account_ids, True) + objects = [] + for account in self.pool.get('account.account').browse(self.cursor, self.uid, account_ids): + if not account.parent_id: # hide top level account + continue + if account.type == 'consolidation': + to_display.update(dict([(a.id, False) for a in account.child_consol_ids])) + elif account.type == 'view': + to_display.update(dict([(a.id, True) for a in account.child_id])) + account.debit = accounts_by_ids[account.id]['debit'] + account.credit = accounts_by_ids[account.id]['credit'] + account.balance = accounts_by_ids[account.id]['balance'] + account.init_balance = accounts_by_ids[account.id].get('init_balance', 0.0) + + display_account = False # if any amount is != 0 in comparisons, we have to display the whole account + comp_accounts = [] + for comp_account_by_id in comp_accounts_by_ids: + values = comp_account_by_id.get(account.id) + values.update(self._get_diff(account.balance, values['balance'])) + display_account = any((values.get('credit', 0.0), values.get('debit', 0.0), values.get('balance', 0.0), values.get('init_balance', 0.0))) + comp_accounts.append(values) + account.comparisons = comp_accounts + # we have to display the account if a comparison as an amount or if we have an amount in the main column + # we set it as a property to let the data in the report if someone want to use it in a custom report + display_account = display_account or any((account.debit, account.credit, account.balance, account.init_balance)) + to_display.update({account.id: display_account and to_display[account.id]}) + objects.append(account) + + for account in objects: + account.to_display = to_display[account.id] + + context_report_values = { + 'fiscalyear': fiscalyear, + 'start_date': start_date, + 'stop_date': stop_date, + 'start_period': start_period, + 'stop_period': stop_period, + 'chart_account': chart_account, + 'comparison_mode': comparison_mode, + 'nb_comparison': nb_comparisons, + 'initial_balance': init_balance, + 'initial_balance_mode': initial_balance_mode, + 'comp_params': comparison_params, + } + return objects, new_ids, context_report_values