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.

369 lines
16 KiB

7 years ago
  1. # -*- coding: 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. from operator import add
  23. from .common_reports import CommonReportHeaderWebkit
  24. from openerp import tools
  25. class CommonBalanceReportHeaderWebkit(CommonReportHeaderWebkit):
  26. """Define common helper for balance (trial balance, P&L, BS oriented
  27. financial report"""
  28. def _get_numbers_display(self, data):
  29. return self._get_form_param('numbers_display', data)
  30. @staticmethod
  31. def find_key_by_value_in_list(dic, value):
  32. return [key for key, val in dic.iteritems() if value in val][0]
  33. def _get_account_details(self, account_ids, target_move, fiscalyear,
  34. main_filter, start, stop, initial_balance_mode,
  35. context=None):
  36. """
  37. Get details of accounts to display on the report
  38. @param account_ids: ids of accounts to get details
  39. @param target_move: selection filter for moves (all or posted)
  40. @param fiscalyear: browse of the fiscalyear
  41. @param main_filter: selection filter period / date or none
  42. @param start: start date or start period browse instance
  43. @param stop: stop date or stop period browse instance
  44. @param initial_balance_mode: False: no calculation,
  45. 'opening_balance': from the opening period,
  46. 'initial_balance': computed from previous year / periods
  47. @return: dict of list containing accounts details, keys are
  48. the account ids
  49. """
  50. if context is None:
  51. context = {}
  52. account_obj = self.pool.get('account.account')
  53. period_obj = self.pool.get('account.period')
  54. use_period_ids = main_filter in (
  55. 'filter_no', 'filter_period', 'filter_opening')
  56. if use_period_ids:
  57. if main_filter == 'filter_opening':
  58. period_ids = [start.id]
  59. else:
  60. period_ids = period_obj.build_ctx_periods(
  61. 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. init_balance = False
  65. if initial_balance_mode == 'opening_balance':
  66. init_balance = self._read_opening_balance(account_ids, start)
  67. elif initial_balance_mode:
  68. init_balance = self._compute_initial_balances(
  69. account_ids, start, fiscalyear)
  70. ctx = context.copy()
  71. ctx.update({'state': target_move,
  72. 'all_fiscalyear': True})
  73. if use_period_ids:
  74. ctx.update({'periods': period_ids})
  75. elif main_filter == 'filter_date':
  76. ctx.update({'date_from': start,
  77. 'date_to': stop})
  78. # in tests (when installing and testing at the same time),
  79. # the read below might fail because it relies on the order
  80. # given by parent_store
  81. if tools.config['test_enable']:
  82. account_obj._parent_store_compute(self.cursor)
  83. accounts = account_obj.read(
  84. self.cursor,
  85. self.uid,
  86. account_ids,
  87. ['type', 'code', 'name', 'debit', 'credit',
  88. 'balance', 'parent_id', 'level', 'child_id'],
  89. context=ctx)
  90. accounts_by_id = {}
  91. for account in accounts:
  92. if init_balance:
  93. # sum for top level views accounts
  94. child_ids = account_obj._get_children_and_consol(
  95. self.cursor, self.uid, account['id'], ctx)
  96. if child_ids:
  97. child_init_balances = [
  98. init_bal['init_balance']
  99. for acnt_id, init_bal in init_balance.iteritems()
  100. if acnt_id in child_ids]
  101. top_init_balance = reduce(add, child_init_balances)
  102. account['init_balance'] = top_init_balance
  103. else:
  104. account.update(init_balance[account['id']])
  105. account['balance'] = account['init_balance'] + \
  106. account['debit'] - account['credit']
  107. accounts_by_id[account['id']] = account
  108. return accounts_by_id
  109. def _get_comparison_details(self, data, account_ids, target_move,
  110. comparison_filter, index, context=None):
  111. """
  112. @param data: data of the wizard form
  113. @param account_ids: ids of the accounts to get details
  114. @param comparison_filter: selected filter on the form for
  115. the comparison (filter_no, filter_year, filter_period,
  116. filter_date)
  117. @param index: index of the fields to get
  118. (ie. comp1_fiscalyear_id where 1 is the index)
  119. @return: dict of account details (key = account id)
  120. """
  121. fiscalyear = self._get_info(
  122. data, "comp%s_fiscalyear_id" % (index,), 'account.fiscalyear')
  123. start_period = self._get_info(
  124. data, "comp%s_period_from" % (index,), 'account.period')
  125. stop_period = self._get_info(
  126. data, "comp%s_period_to" % (index,), 'account.period')
  127. start_date = self._get_form_param("comp%s_date_from" % (index,), data)
  128. stop_date = self._get_form_param("comp%s_date_to" % (index,), data)
  129. init_balance = self.is_initial_balance_enabled(comparison_filter)
  130. accounts_by_ids = {}
  131. comp_params = {}
  132. details_filter = comparison_filter
  133. if comparison_filter != 'filter_no':
  134. start_period, stop_period, start, stop = \
  135. self._get_start_stop_for_filter(
  136. comparison_filter, fiscalyear, start_date, stop_date,
  137. start_period, stop_period)
  138. if comparison_filter == 'filter_year':
  139. details_filter = 'filter_no'
  140. initial_balance_mode = init_balance \
  141. and self._get_initial_balance_mode(start) or False
  142. accounts_by_ids = self._get_account_details(
  143. account_ids, target_move, fiscalyear, details_filter,
  144. start, stop, initial_balance_mode, context=context)
  145. comp_params = {
  146. 'comparison_filter': comparison_filter,
  147. 'fiscalyear': fiscalyear,
  148. 'start': start,
  149. 'stop': stop,
  150. 'initial_balance': init_balance,
  151. 'initial_balance_mode': initial_balance_mode,
  152. }
  153. return accounts_by_ids, comp_params
  154. def _get_diff(self, balance, previous_balance):
  155. """
  156. @param balance: current balance
  157. @param previous_balance: last balance
  158. @return: dict of form {'diff': difference,
  159. 'percent_diff': diff in percentage}
  160. """
  161. diff = balance - previous_balance
  162. obj_precision = self.pool.get('decimal.precision')
  163. precision = obj_precision.precision_get(
  164. self.cursor, self.uid, 'Account')
  165. # round previous balance with account precision to avoid big numbers
  166. # if previous balance is 0.0000001 or a any very small number
  167. if round(previous_balance, precision) == 0:
  168. percent_diff = False
  169. else:
  170. percent_diff = round(diff / previous_balance * 100, precision)
  171. return {'diff': diff, 'percent_diff': percent_diff}
  172. def _comp_filters(self, data, comparison_number):
  173. """
  174. @param data: data of the report
  175. @param comparison_number: number of comparisons
  176. @return: list of comparison filters, nb of comparisons used and
  177. comparison mode (no_comparison, single, multiple)
  178. """
  179. comp_filters = []
  180. for index in range(comparison_number):
  181. comp_filters.append(
  182. self._get_form_param("comp%s_filter" % (index,), data,
  183. default='filter_no'))
  184. nb_comparisons = len(
  185. [comp_filter for comp_filter in comp_filters
  186. if comp_filter != 'filter_no'])
  187. if not nb_comparisons:
  188. comparison_mode = 'no_comparison'
  189. elif nb_comparisons > 1:
  190. comparison_mode = 'multiple'
  191. else:
  192. comparison_mode = 'single'
  193. return comp_filters, nb_comparisons, comparison_mode
  194. def _get_start_stop_for_filter(self, main_filter, fiscalyear, start_date,
  195. stop_date, start_period, stop_period):
  196. if main_filter in ('filter_no', 'filter_year'):
  197. start_period = self.get_first_fiscalyear_period(fiscalyear)
  198. stop_period = self.get_last_fiscalyear_period(fiscalyear)
  199. elif main_filter == 'filter_opening':
  200. opening_period = self._get_st_fiscalyear_period(
  201. fiscalyear, special=True)
  202. start_period = stop_period = opening_period
  203. if main_filter == 'filter_date':
  204. start = start_date
  205. stop = stop_date
  206. else:
  207. start = start_period
  208. stop = stop_period
  209. return start_period, stop_period, start, stop
  210. def compute_balance_data(self, data, filter_report_type=None):
  211. lang = self.localcontext.get('lang')
  212. lang_ctx = lang and {'lang': lang} or {}
  213. new_ids = (data['form']['account_ids'] or
  214. [data['form']['chart_account_id']])
  215. max_comparison = self._get_form_param(
  216. 'max_comparison', data, default=0)
  217. main_filter = self._get_form_param('filter', data, default='filter_no')
  218. comp_filters, nb_comparisons, comparison_mode = self._comp_filters(
  219. data, max_comparison)
  220. fiscalyear = self.get_fiscalyear_br(data)
  221. start_period = self.get_start_period_br(data)
  222. stop_period = self.get_end_period_br(data)
  223. target_move = self._get_form_param('target_move', data, default='all')
  224. start_date = self._get_form_param('date_from', data)
  225. stop_date = self._get_form_param('date_to', data)
  226. chart_account = self._get_chart_account_id_br(data)
  227. start_period, stop_period, start, stop = \
  228. self._get_start_stop_for_filter(main_filter, fiscalyear,
  229. start_date, stop_date,
  230. start_period, stop_period)
  231. init_balance = self.is_initial_balance_enabled(main_filter)
  232. initial_balance_mode = init_balance and self._get_initial_balance_mode(
  233. start) or False
  234. # Retrieving accounts
  235. ctx = {}
  236. if data['form'].get('account_level'):
  237. # Filter by account level
  238. ctx['account_level'] = int(data['form']['account_level'])
  239. account_ids = self.get_all_accounts(
  240. new_ids, only_type=filter_report_type, context=ctx)
  241. # get details for each account, total of debit / credit / balance
  242. accounts_by_ids = self._get_account_details(
  243. account_ids, target_move, fiscalyear, main_filter, start, stop,
  244. initial_balance_mode, context=lang_ctx)
  245. comparison_params = []
  246. comp_accounts_by_ids = []
  247. for index in range(max_comparison):
  248. if comp_filters[index] != 'filter_no':
  249. comparison_result, comp_params = self._get_comparison_details(
  250. data, account_ids, target_move, comp_filters[index], index,
  251. context=lang_ctx)
  252. comparison_params.append(comp_params)
  253. comp_accounts_by_ids.append(comparison_result)
  254. objects = self.pool.get('account.account').browse(self.cursor,
  255. self.uid,
  256. account_ids,
  257. context=lang_ctx)
  258. to_display_accounts = dict.fromkeys(account_ids, True)
  259. init_balance_accounts = dict.fromkeys(account_ids, False)
  260. comparisons_accounts = dict.fromkeys(account_ids, [])
  261. debit_accounts = dict.fromkeys(account_ids, False)
  262. credit_accounts = dict.fromkeys(account_ids, False)
  263. balance_accounts = dict.fromkeys(account_ids, False)
  264. for account in objects:
  265. if account.type == 'consolidation':
  266. to_display_accounts.update(
  267. dict([(a.id, False) for a in account.child_consol_ids]))
  268. elif account.type == 'view':
  269. to_display_accounts.update(
  270. dict([(a.id, True) for a in account.child_id]))
  271. debit_accounts[account.id] = \
  272. accounts_by_ids[account.id]['debit']
  273. credit_accounts[account.id] = \
  274. accounts_by_ids[account.id]['credit']
  275. balance_accounts[account.id] = \
  276. accounts_by_ids[account.id]['balance']
  277. init_balance_accounts[account.id] = \
  278. accounts_by_ids[account.id].get('init_balance', 0.0)
  279. # if any amount is != 0 in comparisons, we have to display the
  280. # whole account
  281. display_account = False
  282. comp_accounts = []
  283. for comp_account_by_id in comp_accounts_by_ids:
  284. values = comp_account_by_id.get(account.id)
  285. values.update(
  286. self._get_diff(balance_accounts[account.id],
  287. values['balance']))
  288. display_account = any((values.get('credit', 0.0),
  289. values.get('debit', 0.0),
  290. values.get('balance', 0.0),
  291. values.get('init_balance', 0.0)))
  292. comp_accounts.append(values)
  293. comparisons_accounts[account.id] = comp_accounts
  294. # we have to display the account if a comparison as an amount or
  295. # if we have an amount in the main column
  296. # we set it as a property to let the data in the report if someone
  297. # want to use it in a custom report
  298. display_account = display_account\
  299. or any((debit_accounts[account.id],
  300. credit_accounts[account.id],
  301. balance_accounts[account.id],
  302. init_balance_accounts[account.id]))
  303. to_display_accounts.update(
  304. {account.id: display_account and
  305. to_display_accounts[account.id]})
  306. context_report_values = {
  307. 'fiscalyear': fiscalyear,
  308. 'start_date': start_date,
  309. 'stop_date': stop_date,
  310. 'start_period': start_period,
  311. 'stop_period': stop_period,
  312. 'chart_account': chart_account,
  313. 'comparison_mode': comparison_mode,
  314. 'nb_comparison': nb_comparisons,
  315. 'initial_balance': init_balance,
  316. 'initial_balance_mode': initial_balance_mode,
  317. 'comp_params': comparison_params,
  318. 'to_display_accounts': to_display_accounts,
  319. 'init_balance_accounts': init_balance_accounts,
  320. 'comparisons_accounts': comparisons_accounts,
  321. 'debit_accounts': debit_accounts,
  322. 'credit_accounts': credit_accounts,
  323. 'balance_accounts': balance_accounts,
  324. }
  325. return objects, new_ids, context_report_values