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.

243 lines
10 KiB

  1. # -*- encoding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Author: Nicolas Bessi, Guewen Baconnier
  5. # Copyright Camptocamp SA 2011
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as
  9. # published by the Free Software Foundation, either version 3 of the
  10. # License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. from operator import itemgetter
  22. from itertools import groupby
  23. from datetime import datetime
  24. from openerp.report import report_sxw
  25. from openerp import pooler
  26. from openerp.tools.translate import _
  27. from .common_reports import CommonReportHeaderWebkit
  28. from .webkit_parser_header_fix import HeaderFooterTextWebKitParser
  29. class GeneralLedgerWebkit(report_sxw.rml_parse, CommonReportHeaderWebkit):
  30. def __init__(self, cursor, uid, name, context):
  31. super(GeneralLedgerWebkit, self).__init__(
  32. cursor, uid, name, context=context)
  33. self.pool = pooler.get_pool(self.cr.dbname)
  34. self.cursor = self.cr
  35. company = self.pool.get('res.users').browse(
  36. self.cr, uid, uid, context=context).company_id
  37. header_report_name = ' - '.join(
  38. (_('GENERAL LEDGER'), company.name, company.currency_id.name))
  39. footer_date_time = self.formatLang(
  40. str(datetime.today()), date_time=True)
  41. self.localcontext.update({
  42. 'cr': cursor,
  43. 'uid': uid,
  44. 'report_name': _('General Ledger'),
  45. 'display_account': self._get_display_account,
  46. 'display_account_raw': self._get_display_account_raw,
  47. 'filter_form': self._get_filter,
  48. 'target_move': self._get_target_move,
  49. 'initial_balance': self._get_initial_balance,
  50. 'amount_currency': self._get_amount_currency,
  51. 'display_target_move': self._get_display_target_move,
  52. 'accounts': self._get_accounts_br,
  53. 'additional_args': [
  54. ('--header-font-name', 'Helvetica'),
  55. ('--footer-font-name', 'Helvetica'),
  56. ('--header-font-size', '10'),
  57. ('--footer-font-size', '6'),
  58. ('--header-left', header_report_name),
  59. ('--header-spacing', '2'),
  60. ('--footer-left', footer_date_time),
  61. ('--footer-right',
  62. ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
  63. ('--footer-line',),
  64. ],
  65. })
  66. def set_context(self, objects, data, ids, report_type=None):
  67. """Populate a ledger_lines attribute on each browse record that will be
  68. used by mako template"""
  69. new_ids = data['form']['account_ids'] or data[
  70. 'form']['chart_account_id']
  71. # Account initial balance memoizer
  72. init_balance_memoizer = {}
  73. # Reading form
  74. main_filter = self._get_form_param('filter', data, default='filter_no')
  75. target_move = self._get_form_param('target_move', data, default='all')
  76. start_date = self._get_form_param('date_from', data)
  77. stop_date = self._get_form_param('date_to', data)
  78. do_centralize = self._get_form_param('centralize', data)
  79. start_period = self.get_start_period_br(data)
  80. stop_period = self.get_end_period_br(data)
  81. fiscalyear = self.get_fiscalyear_br(data)
  82. chart_account = self._get_chart_account_id_br(data)
  83. if main_filter == 'filter_no':
  84. start_period = self.get_first_fiscalyear_period(fiscalyear)
  85. stop_period = self.get_last_fiscalyear_period(fiscalyear)
  86. # computation of ledger lines
  87. if main_filter == 'filter_date':
  88. start = start_date
  89. stop = stop_date
  90. else:
  91. start = start_period
  92. stop = stop_period
  93. initial_balance = self.is_initial_balance_enabled(main_filter)
  94. initial_balance_mode = initial_balance \
  95. and self._get_initial_balance_mode(start) or False
  96. # Retrieving accounts
  97. accounts = self.get_all_accounts(new_ids, exclude_type=['view'])
  98. if initial_balance_mode == 'initial_balance':
  99. init_balance_memoizer = self._compute_initial_balances(
  100. accounts, start, fiscalyear)
  101. elif initial_balance_mode == 'opening_balance':
  102. init_balance_memoizer = self._read_opening_balance(accounts, start)
  103. ledger_lines_memoizer = self._compute_account_ledger_lines(
  104. accounts, init_balance_memoizer, main_filter, target_move, start,
  105. stop)
  106. objects = self.pool.get('account.account').browse(self.cursor,
  107. self.uid,
  108. accounts)
  109. init_balance = {}
  110. ledger_lines = {}
  111. for account in objects:
  112. if do_centralize and account.centralized \
  113. and ledger_lines_memoizer.get(account.id):
  114. ledger_lines[account.id] = self._centralize_lines(
  115. main_filter, ledger_lines_memoizer.get(account.id, []))
  116. else:
  117. ledger_lines[account.id] = ledger_lines_memoizer.get(
  118. account.id, [])
  119. init_balance[account.id] = init_balance_memoizer.get(account.id,
  120. {})
  121. self.localcontext.update({
  122. 'fiscalyear': fiscalyear,
  123. 'start_date': start_date,
  124. 'stop_date': stop_date,
  125. 'start_period': start_period,
  126. 'stop_period': stop_period,
  127. 'chart_account': chart_account,
  128. 'initial_balance_mode': initial_balance_mode,
  129. 'init_balance': init_balance,
  130. 'ledger_lines': ledger_lines,
  131. })
  132. return super(GeneralLedgerWebkit, self).set_context(
  133. objects, data, new_ids, report_type=report_type)
  134. def _centralize_lines(self, filter, ledger_lines, context=None):
  135. """ Group by period in filter mode 'period' or on one line in filter
  136. mode 'date' ledger_lines parameter is a list of dict built
  137. by _get_ledger_lines"""
  138. def group_lines(lines):
  139. if not lines:
  140. return {}
  141. sums = reduce(lambda line, memo:
  142. dict((key, value + memo[key]) for key, value
  143. in line.iteritems() if key in
  144. ('balance', 'debit', 'credit')), lines)
  145. res_lines = {
  146. 'balance': sums['balance'],
  147. 'debit': sums['debit'],
  148. 'credit': sums['credit'],
  149. 'lname': _('Centralized Entries'),
  150. 'account_id': lines[0]['account_id'],
  151. }
  152. return res_lines
  153. centralized_lines = []
  154. if filter == 'filter_date':
  155. # by date we centralize all entries in only one line
  156. centralized_lines.append(group_lines(ledger_lines))
  157. else: # by period
  158. # by period we centralize all entries in one line per period
  159. period_obj = self.pool.get('account.period')
  160. # we need to sort the lines per period in order to use groupby
  161. # unique ids of each used period id in lines
  162. period_ids = list(
  163. set([line['lperiod_id'] for line in ledger_lines]))
  164. # search on account.period in order to sort them by date_start
  165. sorted_period_ids = period_obj.search(
  166. self.cr, self.uid, [('id', 'in', period_ids)],
  167. order='special desc, date_start', context=context)
  168. sorted_ledger_lines = sorted(
  169. ledger_lines, key=lambda x: sorted_period_ids.
  170. index(x['lperiod_id']))
  171. for period_id, lines_per_period_iterator in groupby(
  172. sorted_ledger_lines, itemgetter('lperiod_id')):
  173. lines_per_period = list(lines_per_period_iterator)
  174. if not lines_per_period:
  175. continue
  176. group_per_period = group_lines(lines_per_period)
  177. group_per_period.update({
  178. 'lperiod_id': period_id,
  179. # period code is anyway the same on each line per period
  180. 'period_code': lines_per_period[0]['period_code'],
  181. })
  182. centralized_lines.append(group_per_period)
  183. return centralized_lines
  184. def _compute_account_ledger_lines(self, accounts_ids,
  185. init_balance_memoizer, main_filter,
  186. target_move, start, stop):
  187. res = {}
  188. for acc_id in accounts_ids:
  189. move_line_ids = self.get_move_lines_ids(
  190. acc_id, main_filter, start, stop, target_move)
  191. if not move_line_ids:
  192. res[acc_id] = []
  193. continue
  194. lines = self._get_ledger_lines(move_line_ids, acc_id)
  195. res[acc_id] = lines
  196. return res
  197. def _get_ledger_lines(self, move_line_ids, account_id):
  198. if not move_line_ids:
  199. return []
  200. res = self._get_move_line_datas(move_line_ids)
  201. # computing counter part is really heavy in term of ressouces
  202. # consuption looking for a king of SQL to help me improve it
  203. move_ids = [x.get('move_id') for x in res]
  204. counter_parts = self._get_moves_counterparts(move_ids, account_id)
  205. for line in res:
  206. line['counterparts'] = counter_parts.get(line.get('move_id'), '')
  207. return res
  208. HeaderFooterTextWebKitParser(
  209. 'report.account.account_report_general_ledger_webkit',
  210. 'account.account',
  211. 'addons/account_financial_report_webkit/report/templates/\
  212. account_report_general_ledger.mako',
  213. parser=GeneralLedgerWebkit)