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.

337 lines
15 KiB

  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 collections import defaultdict
  23. from operator import add
  24. from .common_balance_reports import CommonBalanceReportHeaderWebkit
  25. from .common_partner_reports import CommonPartnersReportHeaderWebkit
  26. class CommonPartnerBalanceReportHeaderWebkit(CommonBalanceReportHeaderWebkit,
  27. CommonPartnersReportHeaderWebkit):
  28. """Define common helper for balance (trial balance, P&L,
  29. BS oriented financial report"""
  30. def _get_account_partners_details(self, account_by_ids, main_filter,
  31. target_move, start, stop,
  32. initial_balance_mode,
  33. partner_filter_ids=False):
  34. res = {}
  35. filter_from = False
  36. if main_filter in ('filter_period', 'filter_no', 'filter_opening'):
  37. filter_from = 'period'
  38. elif main_filter == 'filter_date':
  39. filter_from = 'date'
  40. partners_init_balances_by_ids = {}
  41. for account_id, account_details in account_by_ids.iteritems():
  42. partners_init_balances_by_ids.update(
  43. self._get_partners_initial_balances(
  44. account_id, start, initial_balance_mode,
  45. partner_filter_ids=partner_filter_ids,
  46. # we'll never exclude reconciled entries in the legal
  47. # reports
  48. exclude_reconcile=False))
  49. opening_mode = 'exclude_opening'
  50. if main_filter == 'filter_opening':
  51. opening_mode = 'include_opening'
  52. # get credit and debit for partner
  53. details = self._get_partners_totals_account(
  54. filter_from,
  55. account_id,
  56. start,
  57. stop,
  58. target_move,
  59. partner_filter_ids=partner_filter_ids,
  60. mode=opening_mode)
  61. # merge initial balances in partner details
  62. if partners_init_balances_by_ids.get(account_id):
  63. for partner_id, initial_balances in \
  64. partners_init_balances_by_ids[account_id].iteritems():
  65. if initial_balances.get('init_balance'):
  66. details[partner_id].update(
  67. {'init_balance': initial_balances['init_balance']})
  68. # compute balance for the partner
  69. for partner_id, partner_details in details.iteritems():
  70. details[partner_id]['balance'] = details[partner_id].\
  71. get('init_balance', 0.0) + \
  72. details[partner_id].get('debit', 0.0) - \
  73. details[partner_id].get('credit', 0.0)
  74. res[account_id] = details
  75. return res
  76. def _get_partners_initial_balances(self, account_ids, start_period,
  77. initial_balance_mode,
  78. partner_filter_ids=None,
  79. exclude_reconcile=False):
  80. # we get the initial balance from the opening period (opening_balance)
  81. # when the opening period is included in the start period and
  82. # when there is at least one entry in the opening period. Otherwise we
  83. # compute it from previous periods
  84. if initial_balance_mode == 'opening_balance':
  85. opening_period_selected = self.get_included_opening_period(
  86. start_period)
  87. res = self._compute_partners_initial_balances(
  88. account_ids, start_period, partner_filter_ids,
  89. force_period_ids=opening_period_selected,
  90. exclude_reconcile=exclude_reconcile)
  91. elif initial_balance_mode == 'initial_balance':
  92. res = self._compute_partners_initial_balances(
  93. account_ids, start_period, partner_filter_ids,
  94. exclude_reconcile=exclude_reconcile)
  95. else:
  96. res = {}
  97. return res
  98. def _get_partners_totals_account(self, filter_from, account_id, start,
  99. stop, target_move,
  100. partner_filter_ids=None,
  101. mode='exclude_opening'):
  102. final_res = defaultdict(dict)
  103. sql_select = """
  104. SELECT account_move_line.partner_id,
  105. sum(account_move_line.debit) AS debit,
  106. sum(account_move_line.credit) AS credit
  107. FROM account_move_line"""
  108. sql_joins = ''
  109. sql_where = "WHERE account_move_line.account_id = %(account_id)s \
  110. AND account_move_line.state = 'valid' "
  111. method = getattr(self, '_get_query_params_from_' + filter_from + 's')
  112. sql_conditions, search_params = method(start, stop, mode=mode)
  113. sql_where += sql_conditions
  114. if partner_filter_ids:
  115. sql_where += " AND account_move_line.partner_id \
  116. in %(partner_ids)s"
  117. search_params.update({'partner_ids': tuple(partner_filter_ids)})
  118. if target_move == 'posted':
  119. sql_joins += "INNER JOIN account_move \
  120. ON account_move_line.move_id = account_move.id"
  121. sql_where += " AND account_move.state = %(target_move)s"
  122. search_params.update({'target_move': target_move})
  123. sql_groupby = "GROUP BY account_move_line.partner_id"
  124. search_params.update({'account_id': account_id})
  125. query = ' '.join((sql_select, sql_joins, sql_where, sql_groupby))
  126. self.cursor.execute(query, search_params)
  127. res = self.cursor.dictfetchall()
  128. if res:
  129. for row in res:
  130. final_res[row['partner_id']] = row
  131. return final_res
  132. def _get_filter_type(self, result_selection):
  133. filter_type = ('payable', 'receivable')
  134. if result_selection == 'customer':
  135. filter_type = ('receivable',)
  136. if result_selection == 'supplier':
  137. filter_type = ('payable',)
  138. return filter_type
  139. def _get_partners_comparison_details(self, data, account_ids, target_move,
  140. comparison_filter, index,
  141. partner_filter_ids=False):
  142. """
  143. @param data: data of the wizard form
  144. @param account_ids: ids of the accounts to get details
  145. @param comparison_filter: selected filter on the form for
  146. the comparison (filter_no, filter_year, filter_period, filter_date)
  147. @param index: index of the fields to get (ie. comp1_fiscalyear_id
  148. where 1 is the index)
  149. @param partner_filter_ids: list of ids of partners to select
  150. @return: dict of account details (key = account id)
  151. """
  152. fiscalyear = self._get_info(
  153. data, "comp%s_fiscalyear_id" % (index,), 'account.fiscalyear')
  154. start_period = self._get_info(
  155. data, "comp%s_period_from" % (index,), 'account.period')
  156. stop_period = self._get_info(
  157. data, "comp%s_period_to" % (index,), 'account.period')
  158. start_date = self._get_form_param("comp%s_date_from" % (index,), data)
  159. stop_date = self._get_form_param("comp%s_date_to" % (index,), data)
  160. init_balance = self.is_initial_balance_enabled(comparison_filter)
  161. comp_params = {}
  162. accounts_details_by_ids = defaultdict(dict)
  163. if comparison_filter != 'filter_no':
  164. start_period, stop_period, start, stop = \
  165. self._get_start_stop_for_filter(
  166. comparison_filter, fiscalyear, start_date, stop_date,
  167. start_period, stop_period)
  168. details_filter = comparison_filter
  169. if comparison_filter == 'filter_year':
  170. details_filter = 'filter_no'
  171. initial_balance_mode = init_balance \
  172. and self._get_initial_balance_mode(start) or False
  173. accounts_by_ids = self._get_account_details(
  174. account_ids, target_move, fiscalyear, details_filter, start,
  175. stop, initial_balance_mode)
  176. partner_details_by_ids = self._get_account_partners_details(
  177. accounts_by_ids, details_filter,
  178. target_move, start, stop, initial_balance_mode,
  179. partner_filter_ids=partner_filter_ids)
  180. for account_id in account_ids:
  181. accounts_details_by_ids[account_id][
  182. 'account'] = accounts_by_ids[account_id]
  183. accounts_details_by_ids[account_id][
  184. 'partners_amounts'] = partner_details_by_ids[account_id]
  185. comp_params = {
  186. 'comparison_filter': comparison_filter,
  187. 'fiscalyear': fiscalyear,
  188. 'start': start,
  189. 'stop': stop,
  190. 'initial_balance_mode': initial_balance_mode,
  191. }
  192. return accounts_details_by_ids, comp_params
  193. def compute_partner_balance_data(self, data, filter_report_type=None):
  194. new_ids = data['form']['account_ids'] or data[
  195. 'form']['chart_account_id']
  196. max_comparison = self._get_form_param(
  197. 'max_comparison', data, default=0)
  198. main_filter = self._get_form_param('filter', data, default='filter_no')
  199. comp_filters, nb_comparisons, comparison_mode = self._comp_filters(
  200. data, max_comparison)
  201. fiscalyear = self.get_fiscalyear_br(data)
  202. start_period = self.get_start_period_br(data)
  203. stop_period = self.get_end_period_br(data)
  204. target_move = self._get_form_param('target_move', data, default='all')
  205. start_date = self._get_form_param('date_from', data)
  206. stop_date = self._get_form_param('date_to', data)
  207. chart_account = self._get_chart_account_id_br(data)
  208. result_selection = self._get_form_param('result_selection', data)
  209. partner_ids = self._get_form_param('partner_ids', data)
  210. filter_type = self._get_filter_type(result_selection)
  211. start_period, stop_period, start, stop = \
  212. self._get_start_stop_for_filter(
  213. main_filter, fiscalyear, start_date, stop_date, start_period,
  214. stop_period)
  215. initial_balance = self.is_initial_balance_enabled(main_filter)
  216. initial_balance_mode = initial_balance \
  217. and self._get_initial_balance_mode(start) or False
  218. # Retrieving accounts
  219. account_ids = self.get_all_accounts(
  220. new_ids, only_type=filter_type,
  221. filter_report_type=filter_report_type)
  222. # get details for each accounts, total of debit / credit / balance
  223. accounts_by_ids = self._get_account_details(
  224. account_ids, target_move, fiscalyear, main_filter, start, stop,
  225. initial_balance_mode)
  226. partner_details_by_ids = self._get_account_partners_details(
  227. accounts_by_ids, main_filter, target_move, start, stop,
  228. initial_balance_mode, partner_filter_ids=partner_ids)
  229. comparison_params = []
  230. comp_accounts_by_ids = []
  231. for index in range(max_comparison):
  232. if comp_filters[index] != 'filter_no':
  233. comparison_result, comp_params = self.\
  234. _get_partners_comparison_details(
  235. data, account_ids,
  236. target_move,
  237. comp_filters[index],
  238. index,
  239. partner_filter_ids=partner_ids)
  240. comparison_params.append(comp_params)
  241. comp_accounts_by_ids.append(comparison_result)
  242. objects = []
  243. for account in self.pool.get('account.account').browse(
  244. self.cursor, self.uid, account_ids, context=self.localcontext):
  245. if not account.parent_id: # hide top level account
  246. continue
  247. account.debit = accounts_by_ids[account.id]['debit']
  248. account.credit = accounts_by_ids[account.id]['credit']
  249. account.balance = accounts_by_ids[account.id]['balance']
  250. account.init_balance = accounts_by_ids[
  251. account.id].get('init_balance', 0.0)
  252. account.partners_amounts = partner_details_by_ids[account.id]
  253. comp_accounts = []
  254. for comp_account_by_id in comp_accounts_by_ids:
  255. values = comp_account_by_id.get(account.id)
  256. values['account'].update(
  257. self._get_diff(account.balance,
  258. values['account'].get('balance', 0.0)))
  259. comp_accounts.append(values)
  260. for partner_id, partner_values in \
  261. values['partners_amounts'].copy().iteritems():
  262. base_partner_balance = account.partners_amounts[
  263. partner_id]['balance'] if \
  264. account.partners_amounts.get(partner_id) else 0.0
  265. partner_values.update(self._get_diff(
  266. base_partner_balance,
  267. partner_values.get('balance', 0.0)))
  268. values['partners_amounts'][
  269. partner_id].update(partner_values)
  270. account.comparisons = comp_accounts
  271. all_partner_ids = reduce(add, [comp['partners_amounts'].keys()
  272. for comp in comp_accounts],
  273. account.partners_amounts.keys())
  274. account.partners_order = self._order_partners(all_partner_ids)
  275. objects.append(account)
  276. context_report_values = {
  277. 'fiscalyear': fiscalyear,
  278. 'start_date': start_date,
  279. 'stop_date': stop_date,
  280. 'start_period': start_period,
  281. 'stop_period': stop_period,
  282. 'chart_account': chart_account,
  283. 'comparison_mode': comparison_mode,
  284. 'nb_comparison': nb_comparisons,
  285. 'comp_params': comparison_params,
  286. 'initial_balance_mode': initial_balance_mode,
  287. 'compute_diff': self._get_diff,
  288. }
  289. return objects, new_ids, context_report_values