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.

328 lines
19 KiB

  1. # -*- encoding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # OpenERP, Open Source Management Solution
  5. #
  6. # Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
  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. import xlwt
  23. import time
  24. from openerp.report import report_sxw
  25. from openerp.addons.report_xls.report_xls import report_xls
  26. from openerp.addons.report_xls.utils import rowcol_to_cell
  27. from openerp.addons.account_financial_report_webkit.report.partner_balance import PartnerBalanceWebkit
  28. from openerp.tools.translate import _
  29. #import logging
  30. #_logger = logging.getLogger(__name__)
  31. def display_line(all_comparison_lines):
  32. return any([line.get('balance') for line in all_comparison_lines])
  33. class partners_balance_xls(report_xls):
  34. column_sizes = [12,40,25,17,17,17,17,17]
  35. def print_title(self, ws, _p, row_position, xlwt,_xs):
  36. cell_style = xlwt.easyxf(_xs['xls_title'])
  37. report_name = ' - '.join([_p.report_name.upper(), _p.company.partner_id.name, _p.company.currency_id.name])
  38. c_specs = [
  39. ('report_name', 1, 0, 'text', report_name),
  40. ]
  41. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  42. row_position = self.xls_write_row(ws, row_position, row_data, row_style=cell_style)
  43. return row_position
  44. def print_empty_row(self, ws, row_position):
  45. c_sizes = self.column_sizes
  46. c_specs = [('empty%s'%i, 1, c_sizes[i], 'text', None) for i in range(0,len(c_sizes))]
  47. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  48. row_position = self.xls_write_row(ws, row_position, row_data, set_column_size=True)
  49. return row_position
  50. def print_header_titles(self, ws, _p, data, row_position, xlwt, _xs):
  51. cell_format = _xs['bold'] + _xs['fill_blue'] + _xs['borders_all']
  52. cell_style = xlwt.easyxf(cell_format)
  53. cell_style_center = xlwt.easyxf(cell_format + _xs['center'])
  54. c_specs = [
  55. ('fy', 1, 0, 'text', _('Fiscal Year'), None, cell_style_center),
  56. ('af', 1, 0, 'text', _('Accounts Filter'), None, cell_style_center),
  57. ('df', 1, 0, 'text', _p.filter_form(data) == 'filter_date' and _('Dates Filter') or _('Periods Filter'), None, cell_style_center),
  58. ('pf', 1, 0, 'text', _('Partners Filter'), None, cell_style_center),
  59. ('tm', 1, 0, 'text', _('Target Moves'), None, cell_style_center),
  60. ('ib', 1, 0, 'text', _('Initial Balance'), None, cell_style_center),
  61. ('coa', 1, 0, 'text', _('Chart of Account'), None, cell_style_center),
  62. ]
  63. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  64. row_position = self.xls_write_row(ws, row_position, row_data, row_style=cell_style)
  65. return row_position
  66. def print_header_data(self, ws, _p, data, row_position, xlwt, _xs, initial_balance_text):
  67. cell_format = _xs['borders_all'] + _xs['wrap'] + _xs['top']
  68. cell_style = xlwt.easyxf(cell_format)
  69. cell_style_center = xlwt.easyxf(cell_format + _xs['center'])
  70. c_specs = [
  71. ('fy', 1, 0, 'text', _p.fiscalyear.name if _p.fiscalyear else '-', None, cell_style_center),
  72. ('af', 1, 0, 'text', _p.accounts(data) and ', '.join([account.code for account in _p.accounts(data)]) or _('All'), None, cell_style_center),
  73. ]
  74. df = _('From') + ': '
  75. if _p.filter_form(data) == 'filter_date':
  76. df += _p.start_date if _p.start_date else u''
  77. else:
  78. df += _p.start_period.name if _p.start_period else u''
  79. df += ' ' + _('\nTo') + ': '
  80. if _p.filter_form(data) == 'filter_date':
  81. df += _p.stop_date if _p.stop_date else u''
  82. else:
  83. df += _p.stop_period.name if _p.stop_period else u''
  84. c_specs += [
  85. ('df', 1, 0, 'text', df, None, cell_style_center),
  86. ('tm', 1, 0, 'text', _p.display_partner_account(data), None, cell_style_center),
  87. ('pf', 1, 0, 'text', _p.display_target_move(data), None, cell_style_center),
  88. ('ib', 1, 0, 'text', initial_balance_text[_p.initial_balance_mode], None, cell_style_center),
  89. ('coa', 1, 0, 'text', _p.chart_account.name, None, cell_style_center),
  90. ]
  91. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  92. row_position = self.xls_write_row(ws, row_position, row_data, row_style=cell_style)
  93. return row_position
  94. def print_comparison_header(self, _xs, xlwt, row_position, _p, ws, initial_balance_text ):
  95. cell_format_ct = _xs['bold'] + _xs['fill_blue'] + _xs['borders_all']
  96. cell_style_ct = xlwt.easyxf(cell_format_ct)
  97. c_specs = [('ct', 7, 0, 'text', _('Comparisons'))]
  98. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  99. row_position = self.xls_write_row(ws, row_position, row_data, row_style=cell_style_ct)
  100. cell_format = _xs['borders_all'] + _xs['wrap'] + _xs['top']
  101. cell_style_center = xlwt.easyxf(cell_format)
  102. for index, params in enumerate(_p.comp_params):
  103. c_specs = [('c', 2, 0, 'text', _('Comparison') + str(index + 1) + ' (C' + str(index + 1) + ')')]
  104. if params['comparison_filter'] == 'filter_date':
  105. c_specs += [('f', 2, 0, 'text', _('Dates Filter') + ': ' + _p.formatLang(params['start'], date=True) + ' - ' + _p.formatLang(params['stop'], date=True))]
  106. elif params['comparison_filter'] == 'filter_period':
  107. c_specs += [('f', 2, 0, 'text', _('Periods Filter') + ': ' + params['start'].name + ' - ' + params['stop'].name)]
  108. else:
  109. c_specs += [('f', 2, 0, 'text', _('Fiscal Year') + ': ' + params['fiscalyear'].name)]
  110. c_specs += [('ib', 2, 0, 'text', _('Initial Balance') + ': ' + initial_balance_text[params['initial_balance_mode']])]
  111. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  112. row_position = self.xls_write_row(ws, row_position, row_data, row_style=cell_style_center)
  113. return row_position
  114. def print_account_header(self, ws, _p, _xs, xlwt, row_position):
  115. cell_format = _xs['bold'] + _xs['fill'] + _xs['borders_all'] + _xs['wrap'] + _xs['top']
  116. cell_style = xlwt.easyxf(cell_format)
  117. cell_style_right = xlwt.easyxf(cell_format + _xs['right'])
  118. cell_style_center = xlwt.easyxf(cell_format + _xs['center'])
  119. if len(_p.comp_params) == 2:
  120. account_span = 3
  121. else:
  122. account_span = _p.initial_balance_mode and 2 or 3
  123. c_specs = [
  124. ('account', account_span, 0, 'text', _('Account / Partner Name')),
  125. ('code', 1, 0, 'text', _('Code / Ref')),
  126. ]
  127. if _p.comparison_mode == 'no_comparison':
  128. if _p.initial_balance_mode:
  129. c_specs += [('init_bal', 1, 0, 'text', _('Initial Balance'), None, cell_style_right)]
  130. c_specs += [
  131. ('debit', 1, 0, 'text', _('Debit'), None, cell_style_right),
  132. ('credit', 1, 0, 'text', _('Credit'), None, cell_style_right),
  133. ]
  134. if _p.comparison_mode == 'no_comparison' or not _p.fiscalyear:
  135. c_specs += [('balance', 1, 0, 'text', _('Balance'), None, cell_style_right)]
  136. else:
  137. c_specs += [('balance_fy', 1, 0, 'text', _('Balance %s') % _p.fiscalyear.name, None, cell_style_right)]
  138. if _p.comparison_mode in ('single', 'multiple'):
  139. for index in range(_p.nb_comparison):
  140. if _p.comp_params[index]['comparison_filter'] == 'filter_year' and _p.comp_params[index].get('fiscalyear', False):
  141. c_specs += [('balance_%s' %index, 1, 0, 'text', _('Balance %s') % _p.comp_params[index]['fiscalyear'].name, None, cell_style_right)]
  142. else:
  143. c_specs += [('balance_%s' %index, 1, 0, 'text', _('Balance C%s') % (index + 1), None, cell_style_right)]
  144. if _p.comparison_mode == 'single':
  145. c_specs += [
  146. ('diff', 1, 0, 'text', _('Difference'), None, cell_style_right),
  147. ('diff_percent', 1, 0, 'text', _('% Difference'), None, cell_style_center),
  148. ]
  149. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  150. row_position = self.xls_write_row(ws, row_position, row_data, row_style=cell_style)
  151. return row_position
  152. def print_row_code_account(self, ws, current_account, row_position, _xs, xlwt):
  153. cell_format = _xs['xls_title'] + _xs['bold'] + _xs['fill'] + _xs['borders_all']
  154. cell_style = xlwt.easyxf(cell_format)
  155. c_specs = [ ('acc_title', 7, 0, 'text', ' - '.join([current_account.code, current_account.name])), ]
  156. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  157. row_position = self.xls_write_row(ws, row_position, row_data, cell_style)
  158. return row_position
  159. def print_account_totals(self, _xs, xlwt, ws, row_start_account, row_position, current_account,_p):
  160. cell_format = _xs['bold'] + _xs['fill'] + _xs['borders_all'] + _xs['wrap'] + _xs['top']
  161. cell_style = xlwt.easyxf(cell_format)
  162. cell_style_decimal = xlwt.easyxf(cell_format + _xs['right'], num_format_str = report_xls.decimal_format)
  163. c_specs = [
  164. ('acc_title', 2, 0, 'text', current_account.name),
  165. ('code', 1, 0, 'text', current_account.code),
  166. ]
  167. for column in range(3,7):
  168. if (_p.comparison_mode == 'single' and column == 6): #in case of one single comparison, the column 6 will contain percentages
  169. total_diff = rowcol_to_cell(row_position, column-1)
  170. total_balance = rowcol_to_cell(row_position, column-2)
  171. account_formula = 'Round('+ total_diff + '/' + total_balance + '*100;0)'
  172. else:
  173. account_start = rowcol_to_cell(row_start_account, column)
  174. account_end = rowcol_to_cell(row_position -1, column)
  175. account_formula = 'Round(SUM(' + account_start + ':' + account_end + ');2)'
  176. c_specs += [('total%s' %column, 1, 0, 'text', None, account_formula, None, cell_style_decimal)]
  177. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  178. row_position = self.xls_write_row(ws, row_position, row_data, cell_style)
  179. return row_position + 1
  180. def generate_xls_report(self, _p, _xs, data, objects, wb):
  181. # Initialisations
  182. ws = wb.add_sheet(_p.report_name[:31])
  183. ws.panes_frozen = True
  184. ws.remove_splits = True
  185. ws.portrait = 0 # Landscape
  186. ws.fit_width_to_pages = 1
  187. row_pos = 0
  188. ws.header_str = self.xls_headers['standard']
  189. ws.footer_str = self.xls_footers['standard']
  190. # Print Title
  191. row_pos = self.print_title(ws, _p, row_pos, xlwt, _xs)
  192. # Print empty row to define column sizes
  193. row_pos = self.print_empty_row(ws, row_pos)
  194. # Print Header Table titles (Fiscal Year - Accounts Filter - Periods Filter...)
  195. row_pos = self.print_header_titles(ws, _p, data, row_pos, xlwt, _xs)
  196. initial_balance_text = {'initial_balance': _('Computed'), 'opening_balance': _('Opening Entries'), False: _('No')} # cf. account_report_partner_balance.mako
  197. # Print Header Table data
  198. row_pos = self.print_header_data(ws, _p, data, row_pos, xlwt, _xs, initial_balance_text)
  199. # Print comparison header table
  200. if _p.comparison_mode in ('single', 'multiple'):
  201. row_pos += 1
  202. row_pos = self.print_comparison_header(_xs, xlwt, row_pos, _p, ws, initial_balance_text)
  203. # Freeze the line
  204. ws.set_horz_split_pos(row_pos)
  205. # cell styles for account data
  206. regular_cell_format = _xs['borders_all']
  207. regular_cell_style = xlwt.easyxf(regular_cell_format)
  208. regular_cell_style_center = xlwt.easyxf(regular_cell_format + _xs['center'])
  209. regular_cell_style_decimal = xlwt.easyxf(regular_cell_format + _xs['right'], num_format_str = report_xls.decimal_format)
  210. regular_cell_style_pct = xlwt.easyxf(regular_cell_format + _xs['center'], num_format_str = '0')
  211. row_pos += 1
  212. for current_account in objects:
  213. partners_order = current_account.partners_order
  214. # do not display accounts without partners
  215. if not partners_order:
  216. continue
  217. comparisons = current_account.comparisons
  218. # in multiple columns mode, we do not want to print accounts without any rows
  219. if _p.comparison_mode in ('single', 'multiple'):
  220. all_comparison_lines = [comp['partners_amounts'][partner_id[1]]
  221. for partner_id in partners_order
  222. for comp in comparisons]
  223. if not display_line(all_comparison_lines):
  224. continue
  225. current_partner_amounts = current_account.partners_amounts
  226. if _p.comparison_mode in ('single', 'multiple'):
  227. comparison_total = {}
  228. for i, comp in enumerate(comparisons):
  229. comparison_total[i] = {'balance': 0.0}
  230. # print row: Code - Account name
  231. row_pos = self.print_row_code_account(ws, current_account, row_pos, _xs, xlwt)
  232. row_account_start = row_pos
  233. # Print row: Titles "Account/Partner Name-Code/ref-Initial Balance-Debit-Credit-Balance" or "Account/Partner Name-Code/ref-Balance Year-Balance Year2-Balance C2-Balance C3"
  234. row_pos = self.print_account_header(ws, _p, _xs, xlwt, row_pos)
  235. for (partner_code_name, partner_id, partner_ref, partner_name) in partners_order:
  236. partner = current_partner_amounts.get(partner_id, {})
  237. # in single mode, we have to display all the partners even if their balance is 0.0 because the initial balance should match with the previous year closings
  238. # in multiple columns mode, we do not want to print partners which have a balance at 0.0 in each comparison column
  239. if _p.comparison_mode in ('single', 'multiple'):
  240. all_comparison_lines = [comp['partners_amounts'][partner_id]
  241. for comp in comparisons
  242. if comp['partners_amounts'].get(partner_id)]
  243. if not display_line(all_comparison_lines):
  244. continue
  245. # display data row
  246. if len(_p.comp_params) == 2:
  247. account_span = 3
  248. else:
  249. account_span = _p.initial_balance_mode and 2 or 3
  250. c_specs = [('acc_title', account_span, 0, 'text', partner_name if partner_name else _('Unallocated'))]
  251. c_specs += [('partner_ref', 1, 0, 'text', partner_ref if partner_ref else '')]
  252. if _p.comparison_mode == 'no_comparison':
  253. bal_formula = ''
  254. if _p.initial_balance_mode:
  255. init_bal_cell = rowcol_to_cell(row_pos, 3)
  256. bal_formula = init_bal_cell + '+'
  257. debit_col = 4
  258. c_specs += [
  259. ('init_bal', 1, 0, 'number', partner.get('init_balance', 0.0), None, regular_cell_style_decimal),
  260. ]
  261. else:
  262. debit_col = 3
  263. c_specs += [
  264. ('debit', 1, 0, 'number', partner.get('debit', 0.0), None, regular_cell_style_decimal),
  265. ('credit', 1, 0, 'number', partner.get('credit', 0.0), None, regular_cell_style_decimal),
  266. ]
  267. debit_cell = rowcol_to_cell(row_pos, debit_col)
  268. credit_cell = rowcol_to_cell(row_pos, debit_col+1)
  269. bal_formula += debit_cell + '-' + credit_cell
  270. c_specs += [('bal', 1, 0, 'number', None, bal_formula, regular_cell_style_decimal),]
  271. else:
  272. c_specs += [('bal', 1, 0, 'number', partner.get('balance', 0.0), None, regular_cell_style_decimal),]
  273. if _p.comparison_mode in ('single', 'multiple'):
  274. for i, comp in enumerate(comparisons):
  275. comp_partners = comp['partners_amounts']
  276. balance = diff = percent_diff = 0
  277. if comp_partners.get(partner_id):
  278. balance = comp_partners[partner_id]['balance']
  279. diff = comp_partners[partner_id]['diff']
  280. percent_diff = comp_partners[partner_id]['percent_diff']
  281. comparison_total[i]['balance'] += balance
  282. c_specs += [('balance_%s' %i, 1, 0, 'number', balance, None, regular_cell_style_decimal), ]
  283. if _p.comparison_mode == 'single': ## no diff in multiple comparisons because it shows too much data
  284. c_specs += [('balance_diff', 1, 0, 'number', diff, None, regular_cell_style_decimal), ]
  285. if percent_diff is False:
  286. c_specs += [('balance', 1, 0, 'number', diff, None, regular_cell_style_decimal), ]
  287. else:
  288. c_specs += [('perc_diff', 1, 0, 'number', int(round(percent_diff))), ]
  289. row_data = self.xls_row_template(c_specs, [x[0] for x in c_specs])
  290. row_pos = self.xls_write_row(ws, row_pos, row_data, regular_cell_style)
  291. row_pos = self.print_account_totals(_xs, xlwt, ws, row_account_start, row_pos, current_account,_p)
  292. partners_balance_xls('report.account.account_report_partner_balance_xls', 'account.account',
  293. parser=PartnerBalanceWebkit)
  294. # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: