[MODULE] +v1.1.0 from https://www.odoo.com/apps/7.0/account_financial_report_webkit/
[burette/account_financial_report_webkit.git] / report / common_balance_reports.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Author: Guewen Baconnier
5 # Copyright Camptocamp SA 2011
6 # SQL inspired from OpenERP original code
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
17 #
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 from operator import add
24
25 from .common_reports import CommonReportHeaderWebkit
26
27
28 class CommonBalanceReportHeaderWebkit(CommonReportHeaderWebkit):
29 """Define common helper for balance (trial balance, P&L, BS oriented financial report"""
30
31 def _get_numbers_display(self, data):
32 return self._get_form_param('numbers_display', data)
33
34 @staticmethod
35 def find_key_by_value_in_list(dic, value):
36 return [key for key, val in dic.iteritems() if value in val][0]
37
38 def _get_account_details(self, account_ids, target_move, fiscalyear, main_filter, start, stop, initial_balance_mode, context=None):
39 """
40 Get details of accounts to display on the report
41 @param account_ids: ids of accounts to get details
42 @param target_move: selection filter for moves (all or posted)
43 @param fiscalyear: browse of the fiscalyear
44 @param main_filter: selection filter period / date or none
45 @param start: start date or start period browse instance
46 @param stop: stop date or stop period browse instance
47 @param initial_balance_mode: False: no calculation, 'opening_balance': from the opening period, 'initial_balance': computed from previous year / periods
48 @return: dict of list containing accounts details, keys are the account ids
49 """
50 if context is None:
51 context = {}
52
53 account_obj = self.pool.get('account.account')
54 period_obj = self.pool.get('account.period')
55 use_period_ids = main_filter in ('filter_no', 'filter_period', 'filter_opening')
56
57 if use_period_ids:
58 if main_filter == 'filter_opening':
59 period_ids = [start.id]
60 else:
61 period_ids = period_obj.build_ctx_periods(self.cursor, self.uid, start.id, stop.id)
62 # never include the opening in the debit / credit amounts
63 period_ids = self.exclude_opening_periods(period_ids)
64
65 init_balance = False
66 if initial_balance_mode == 'opening_balance':
67 init_balance = self._read_opening_balance(account_ids, start)
68 elif initial_balance_mode:
69 init_balance = self._compute_initial_balances(account_ids, start, fiscalyear)
70
71 ctx = context.copy()
72 ctx.update({'state': target_move,
73 'all_fiscalyear': True})
74
75 if use_period_ids:
76 ctx.update({'periods': period_ids})
77 elif main_filter == 'filter_date':
78 ctx.update({'date_from': start,
79 'date_to': stop})
80
81 accounts = account_obj.read(
82 self.cursor,
83 self.uid,
84 account_ids,
85 ['type', 'code', 'name', 'debit', 'credit', 'balance', 'parent_id', 'level', 'child_id'],
86 ctx)
87
88 accounts_by_id = {}
89 for account in accounts:
90 if init_balance:
91 # sum for top level views accounts
92 child_ids = account_obj._get_children_and_consol(self.cursor, self.uid, account['id'], ctx)
93 if child_ids:
94 child_init_balances = [
95 init_bal['init_balance']
96 for acnt_id, init_bal in init_balance.iteritems()
97 if acnt_id in child_ids]
98 top_init_balance = reduce(add, child_init_balances)
99 account['init_balance'] = top_init_balance
100 else:
101 account.update(init_balance[account['id']])
102 account['balance'] = account['init_balance'] + account['debit'] - account['credit']
103 accounts_by_id[account['id']] = account
104 return accounts_by_id
105
106 def _get_comparison_details(self, data, account_ids, target_move, comparison_filter, index):
107 """
108
109 @param data: data of the wizard form
110 @param account_ids: ids of the accounts to get details
111 @param comparison_filter: selected filter on the form for the comparison (filter_no, filter_year, filter_period, filter_date)
112 @param index: index of the fields to get (ie. comp1_fiscalyear_id where 1 is the index)
113 @return: dict of account details (key = account id)
114 """
115 fiscalyear = self._get_info(data, "comp%s_fiscalyear_id" % (index,), 'account.fiscalyear')
116 start_period = self._get_info(data, "comp%s_period_from" % (index,), 'account.period')
117 stop_period = self._get_info(data, "comp%s_period_to" % (index,), 'account.period')
118 start_date = self._get_form_param("comp%s_date_from" % (index,), data)
119 stop_date = self._get_form_param("comp%s_date_to" % (index,), data)
120 init_balance = self.is_initial_balance_enabled(comparison_filter)
121
122 accounts_by_ids = {}
123 comp_params = {}
124 details_filter = comparison_filter
125 if comparison_filter != 'filter_no':
126 start_period, stop_period, start, stop = \
127 self._get_start_stop_for_filter(comparison_filter, fiscalyear, start_date, stop_date, start_period, stop_period)
128 if comparison_filter == 'filter_year':
129 details_filter = 'filter_no'
130
131 initial_balance_mode = init_balance and self._get_initial_balance_mode(start) or False
132 accounts_by_ids = self._get_account_details(account_ids, target_move, fiscalyear, details_filter,
133 start, stop, initial_balance_mode)
134 comp_params = {
135 'comparison_filter': comparison_filter,
136 'fiscalyear': fiscalyear,
137 'start': start,
138 'stop': stop,
139 'initial_balance': init_balance,
140 'initial_balance_mode': initial_balance_mode,
141 }
142
143 return accounts_by_ids, comp_params
144
145 def _get_diff(self, balance, previous_balance):
146 """
147 @param balance: current balance
148 @param previous_balance: last balance
149 @return: dict of form {'diff': difference, 'percent_diff': diff in percentage}
150 """
151 diff = balance - previous_balance
152
153 obj_precision = self.pool.get('decimal.precision')
154 precision = obj_precision.precision_get(self.cursor, self.uid, 'Account')
155 # round previous balance with account precision to avoid big numbers if previous
156 # balance is 0.0000001 or a any very small number
157 if round(previous_balance, precision) == 0:
158 percent_diff = False
159 else:
160 percent_diff = round(diff / previous_balance * 100, precision)
161
162 return {'diff': diff, 'percent_diff': percent_diff}
163
164 def _comp_filters(self, data, comparison_number):
165 """
166 @param data: data of the report
167 @param comparison_number: number of comparisons
168 @return: list of comparison filters, nb of comparisons used and comparison mode (no_comparison, single, multiple)
169 """
170 comp_filters = []
171 for index in range(comparison_number):
172 comp_filters.append(self._get_form_param("comp%s_filter" % (index,), data, default='filter_no'))
173
174 nb_comparisons = len([comp_filter for comp_filter in comp_filters if comp_filter != 'filter_no'])
175 if not nb_comparisons:
176 comparison_mode = 'no_comparison'
177 elif nb_comparisons > 1:
178 comparison_mode = 'multiple'
179 else:
180 comparison_mode = 'single'
181 return comp_filters, nb_comparisons, comparison_mode
182
183 def _get_start_stop_for_filter(self, main_filter, fiscalyear, start_date, stop_date, start_period, stop_period):
184 if main_filter in ('filter_no', 'filter_year'):
185 start_period = self.get_first_fiscalyear_period(fiscalyear)
186 stop_period = self.get_last_fiscalyear_period(fiscalyear)
187 elif main_filter == 'filter_opening':
188 opening_period = self._get_st_fiscalyear_period(fiscalyear, special=True)
189 start_period = stop_period = opening_period
190 if main_filter == 'filter_date':
191 start = start_date
192 stop = stop_date
193 else:
194 start = start_period
195 stop = stop_period
196
197 return start_period, stop_period, start, stop
198
199 def compute_balance_data(self, data, filter_report_type=None):
200 new_ids = data['form']['account_ids'] or data['form']['chart_account_id']
201 max_comparison = self._get_form_param('max_comparison', data, default=0)
202 main_filter = self._get_form_param('filter', data, default='filter_no')
203
204 comp_filters, nb_comparisons, comparison_mode = self._comp_filters(data, max_comparison)
205
206 fiscalyear = self.get_fiscalyear_br(data)
207
208 start_period = self.get_start_period_br(data)
209 stop_period = self.get_end_period_br(data)
210
211 target_move = self._get_form_param('target_move', data, default='all')
212 start_date = self._get_form_param('date_from', data)
213 stop_date = self._get_form_param('date_to', data)
214 chart_account = self._get_chart_account_id_br(data)
215
216 start_period, stop_period, start, stop = \
217 self._get_start_stop_for_filter(main_filter, fiscalyear, start_date, stop_date, start_period, stop_period)
218
219 init_balance = self.is_initial_balance_enabled(main_filter)
220 initial_balance_mode = init_balance and self._get_initial_balance_mode(start) or False
221
222 # Retrieving accounts
223 account_ids = self.get_all_accounts(new_ids, only_type=filter_report_type)
224
225 # get details for each accounts, total of debit / credit / balance
226 accounts_by_ids = self._get_account_details(account_ids, target_move, fiscalyear, main_filter, start, stop, initial_balance_mode)
227
228 comparison_params = []
229 comp_accounts_by_ids = []
230 for index in range(max_comparison):
231 if comp_filters[index] != 'filter_no':
232 comparison_result, comp_params = self._get_comparison_details(data, account_ids, target_move, comp_filters[index], index)
233 comparison_params.append(comp_params)
234 comp_accounts_by_ids.append(comparison_result)
235
236 to_display = dict.fromkeys(account_ids, True)
237 objects = []
238 for account in self.pool.get('account.account').browse(self.cursor, self.uid, account_ids):
239 if not account.parent_id: # hide top level account
240 continue
241 if account.type == 'consolidation':
242 to_display.update(dict([(a.id, False) for a in account.child_consol_ids]))
243 elif account.type == 'view':
244 to_display.update(dict([(a.id, True) for a in account.child_id]))
245 account.debit = accounts_by_ids[account.id]['debit']
246 account.credit = accounts_by_ids[account.id]['credit']
247 account.balance = accounts_by_ids[account.id]['balance']
248 account.init_balance = accounts_by_ids[account.id].get('init_balance', 0.0)
249
250 display_account = False # if any amount is != 0 in comparisons, we have to display the whole account
251 comp_accounts = []
252 for comp_account_by_id in comp_accounts_by_ids:
253 values = comp_account_by_id.get(account.id)
254 values.update(self._get_diff(account.balance, values['balance']))
255 display_account = any((values.get('credit', 0.0), values.get('debit', 0.0), values.get('balance', 0.0), values.get('init_balance', 0.0)))
256 comp_accounts.append(values)
257 account.comparisons = comp_accounts
258 # we have to display the account if a comparison as an amount or if we have an amount in the main column
259 # we set it as a property to let the data in the report if someone want to use it in a custom report
260 display_account = display_account or any((account.debit, account.credit, account.balance, account.init_balance))
261 to_display.update({account.id: display_account and to_display[account.id]})
262 objects.append(account)
263
264 for account in objects:
265 account.to_display = to_display[account.id]
266
267 context_report_values = {
268 'fiscalyear': fiscalyear,
269 'start_date': start_date,
270 'stop_date': stop_date,
271 'start_period': start_period,
272 'stop_period': stop_period,
273 'chart_account': chart_account,
274 'comparison_mode': comparison_mode,
275 'nb_comparison': nb_comparisons,
276 'initial_balance': init_balance,
277 'initial_balance_mode': initial_balance_mode,
278 'comp_params': comparison_params,
279 }
280 return objects, new_ids, context_report_values