1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Author: Guewen Baconnier
5 # Copyright Camptocamp SA 2011
6 # SQL inspired from OpenERP original code
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.
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.
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/>.
21 ##############################################################################
23 from operator
import add
25 from .common_reports
import CommonReportHeaderWebkit
28 class CommonBalanceReportHeaderWebkit(CommonReportHeaderWebkit
):
29 """Define common helper for balance (trial balance, P&L, BS oriented financial report"""
31 def _get_numbers_display(self
, data
):
32 return self
._get
_form
_param
('numbers_display', data
)
35 def find_key_by_value_in_list(dic
, value
):
36 return [key
for key
, val
in dic
.iteritems() if value
in val
][0]
38 def _get_account_details(self
, account_ids
, target_move
, fiscalyear
, main_filter
, start
, stop
, initial_balance_mode
, context
=None):
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
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')
58 if main_filter
== 'filter_opening':
59 period_ids
= [start
.id]
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
)
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
)
72 ctx
.update({'state': target_move
,
73 'all_fiscalyear': True})
76 ctx
.update({'periods': period_ids
})
77 elif main_filter
== 'filter_date':
78 ctx
.update({'date_from': start
,
81 accounts
= account_obj
.read(
85 ['type', 'code', 'name', 'debit', 'credit', 'balance', 'parent_id', 'level', 'child_id'],
89 for account
in accounts
:
91 # sum for top level views accounts
92 child_ids
= account_obj
._get
_children
_and
_consol
(self
.cursor
, self
.uid
, account
['id'], ctx
)
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
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
106 def _get_comparison_details(self
, data
, account_ids
, target_move
, comparison_filter
, index
):
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)
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
)
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'
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
)
135 'comparison_filter': comparison_filter
,
136 'fiscalyear': fiscalyear
,
139 'initial_balance': init_balance
,
140 'initial_balance_mode': initial_balance_mode
,
143 return accounts_by_ids
, comp_params
145 def _get_diff(self
, balance
, previous_balance
):
147 @param balance: current balance
148 @param previous_balance: last balance
149 @return: dict of form {'diff': difference, 'percent_diff': diff in percentage}
151 diff
= balance
- previous_balance
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:
160 percent_diff
= round(diff
/ previous_balance
* 100, precision
)
162 return {'diff': diff
, 'percent_diff': percent_diff
}
164 def _comp_filters(self
, data
, comparison_number
):
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)
171 for index
in range(comparison_number
):
172 comp_filters
.append(self
._get
_form
_param
("comp%s_filter" % (index
,), data
, default
='filter_no'))
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'
180 comparison_mode
= 'single'
181 return comp_filters
, nb_comparisons
, comparison_mode
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':
197 return start_period
, stop_period
, start
, stop
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')
204 comp_filters
, nb_comparisons
, comparison_mode
= self
._comp
_filters
(data
, max_comparison
)
206 fiscalyear
= self
.get_fiscalyear_br(data
)
208 start_period
= self
.get_start_period_br(data
)
209 stop_period
= self
.get_end_period_br(data
)
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
)
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
)
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
222 # Retrieving accounts
223 account_ids
= self
.get_all_accounts(new_ids
, only_type
=filter_report_type
)
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
)
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
)
236 to_display
= dict.fromkeys(account_ids
, True)
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
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)
250 display_account
= False # if any amount is != 0 in comparisons, we have to display the whole account
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
)
264 for account
in objects
:
265 account
.to_display
= to_display
[account
.id]
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
,
280 return objects
, new_ids
, context_report_values