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.

249 lines
10 KiB

  1. # Copyright 2017-2019 Akretion France (http://www.akretion.com/)
  2. # @author: Alexis de Lattre <alexis.delattre@akretion.com>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. from odoo import _, fields, models
  5. class BankReconciliationXlsx(models.AbstractModel):
  6. _name = 'report.bank.reconciliation.xlsx'
  7. _inherit = 'report.report_xlsx.abstract'
  8. def _compute_account_balance(self, journal, date):
  9. bank_account = journal.default_debit_account_id
  10. amount_field = 'balance'
  11. # TODO: add support for bank accounts in foreign currency
  12. # if not o.currency_id else 'amount_currency'
  13. query = """
  14. SELECT sum(%s) FROM account_move_line
  15. WHERE account_id=%%s AND date <= %%s""" % (amount_field, )
  16. self.env.cr.execute(query, (bank_account.id, date))
  17. query_results = self.env.cr.dictfetchall()
  18. if query_results:
  19. account_bal = query_results[0].get('sum') or 0.0
  20. else:
  21. account_bal = 0.0
  22. return account_bal
  23. def _prepare_move_lines(self, journal, date):
  24. bank_account = journal.default_debit_account_id
  25. mlines = self.env['account.move.line'].search([
  26. ('account_id', '=', bank_account.id),
  27. ('journal_id', '=', journal.id), # to avoid initial line
  28. ('date', '<=', date),
  29. '|', ('statement_line_date', '=', False),
  30. ('statement_line_date', '>', date)])
  31. res = []
  32. for mline in mlines:
  33. move = mline.move_id
  34. cpart = []
  35. for line in move.line_ids:
  36. if (
  37. line.account_id != bank_account and
  38. line.account_id.code not in cpart):
  39. cpart.append(line.account_id.code)
  40. counterpart = ' ,'.join(cpart)
  41. res.append({
  42. 'date': mline.date,
  43. 'label': mline.name,
  44. 'ref': mline.ref or '',
  45. 'partner': mline.partner_id.display_name or '',
  46. 'amount': mline.balance,
  47. 'statement_line_date': mline.statement_line_date or '',
  48. 'move_number': move.name,
  49. 'counterpart': counterpart,
  50. })
  51. return res
  52. def _prepare_draft_statement_lines(self, journal, date):
  53. blines = self.env['account.bank.statement.line'].search([
  54. ('journal_entry_ids', '=', False),
  55. ('journal_id', '=', journal.id),
  56. ('date', '<=', date)])
  57. res = []
  58. for bline in blines:
  59. res.append({
  60. 'date': bline.date,
  61. 'label': bline.name,
  62. 'ref': bline.ref or '',
  63. 'partner': bline.partner_id.display_name or '',
  64. 'amount': bline.amount,
  65. 'statement_ref': bline.statement_id.display_name,
  66. })
  67. return res
  68. def generate_xlsx_report(self, workbook, data, wizard):
  69. date = wizard.date
  70. date_dt = fields.Date.from_string(date)
  71. no_bank_journal = True
  72. for o in wizard.journal_ids:
  73. no_bank_journal = False
  74. # Start styles
  75. lang_code = self.env.user.lang
  76. lang = False
  77. if lang_code:
  78. lang = self.env['res.lang'].search([('code', '=', lang_code)])
  79. if not lang:
  80. lang = self.env['res.lang'].search([], limit=1)
  81. xls_date_format = lang.date_format.replace('%Y', 'yyyy').\
  82. replace('%m', 'mm').replace('%d', 'dd').replace('%y', 'yy')
  83. doc_title = workbook.add_format({'bold': True, 'font_size': 16})
  84. col_title = workbook.add_format({
  85. 'bold': True, 'bg_color': '#e2e2fa',
  86. 'text_wrap': True, 'font_size': 10,
  87. })
  88. title_right = workbook.add_format({
  89. 'bold': True, 'bg_color': '#e6e6fa',
  90. 'font_size': 10, 'align': 'right',
  91. })
  92. title_date = workbook.add_format({
  93. 'bg_color': '#f6f6ff', 'bold': True,
  94. 'num_format': xls_date_format,
  95. 'font_size': 10,
  96. 'align': 'left'})
  97. label_bold = workbook.add_format({
  98. 'bold': True, 'text_wrap': False, 'font_size': 10})
  99. none = workbook.add_format({
  100. 'bold': True, 'font_size': 10, 'align': 'right'})
  101. regular = workbook.add_format({'font_size': 10})
  102. if '%' in xls_date_format:
  103. # fallback
  104. xls_date_format = 'yyyy-mm-dd'
  105. regular_date = workbook.add_format({
  106. 'num_format': xls_date_format,
  107. 'font_size': 10,
  108. 'align': 'left'})
  109. cur_format = u'#,##0.00 %s' % (
  110. o.company_id.currency_id.symbol or
  111. o.company_id.currency_id.name)
  112. # It seems that Excel replaces automatically the decimal
  113. # and thousand separator by those of the language under which
  114. # Excel runs
  115. regular_currency = workbook.add_format(
  116. {'num_format': cur_format, 'font_size': 10})
  117. regular_currency_bg = workbook.add_format({
  118. 'num_format': cur_format, 'font_size': 10,
  119. 'bg_color': '#f6f6ff'})
  120. # End styles
  121. sheet = workbook.add_worksheet(o.code or o.name)
  122. sheet.write(
  123. 0, 0,
  124. _('%s - %s - Bank Reconciliation') % (
  125. o.company_id.name, o.display_name),
  126. doc_title)
  127. sheet.set_row(0, 26)
  128. sheet.set_row(1, 25)
  129. sheet.set_column(0, 0, 10)
  130. sheet.set_column(1, 1, 40)
  131. sheet.set_column(2, 2, 15)
  132. sheet.set_column(3, 3, 25)
  133. sheet.set_column(4, 4, 12)
  134. sheet.set_column(5, 5, 18)
  135. sheet.set_column(6, 6, 14)
  136. sheet.set_column(7, 7, 14)
  137. row = 2
  138. sheet.write(row, 0, _("Date:"), title_right)
  139. sheet.write(row, 1, date_dt, title_date)
  140. # 1) Show accounting balance of bank account
  141. row += 2
  142. bank_account = o.default_debit_account_id
  143. for col in range(3):
  144. sheet.write(row, col, '', title_right)
  145. sheet.write(
  146. row, 3,
  147. _('Balance %s:') % bank_account.code, title_right)
  148. account_bal = self._compute_account_balance(o, date)
  149. sheet.write(row, 4, account_bal, regular_currency_bg)
  150. bank_bal = account_bal
  151. formula = '=E%d' % (row + 1)
  152. # 2) Show account move line that are not linked to bank statement
  153. # line or linked to a statement line after the date
  154. row += 2
  155. sheet.write(
  156. row, 0, _(
  157. 'Journal items of account %s not linked to a bank '
  158. 'statement line:') % bank_account.code,
  159. label_bold)
  160. mlines = self._prepare_move_lines(o, date)
  161. if not mlines:
  162. sheet.write(row, 4, _('NONE'), none)
  163. else:
  164. row += 1
  165. col_labels = [
  166. _('Date'), _('Label'), _('Ref.'), _('Partner'),
  167. _('Amount'), _('Statement Line Date'), _('Move Number'),
  168. _('Counter-part')]
  169. col = 0
  170. for col_label in col_labels:
  171. sheet.write(row, col, col_label, col_title)
  172. col += 1
  173. m_start_row = m_end_row = row + 1
  174. for mline in mlines:
  175. row += 1
  176. m_end_row = row
  177. bank_bal -= mline['amount']
  178. sheet.write(row, 0, mline['date'], regular_date)
  179. sheet.write(row, 1, mline['label'], regular)
  180. sheet.write(row, 2, mline['ref'], regular)
  181. sheet.write(row, 3, mline['partner'], regular)
  182. sheet.write(row, 4, mline['amount'], regular_currency)
  183. sheet.write(
  184. row, 5, mline['statement_line_date'], regular_date)
  185. sheet.write(row, 6, mline['move_number'], regular)
  186. sheet.write(row, 7, mline['counterpart'], regular)
  187. formula += '-SUM(E%d:E%d)' % (m_start_row + 1, m_end_row + 1)
  188. # 3) Add draft bank statement lines
  189. row += 2 # skip 1 line
  190. sheet.write(
  191. row, 0, _(
  192. 'Draft bank statement lines:'),
  193. label_bold)
  194. blines = self._prepare_draft_statement_lines(o, date)
  195. if not blines:
  196. sheet.write(row, 4, _('NONE'), none)
  197. else:
  198. row += 1
  199. col_labels = [
  200. _('Date'), _('Label'), _('Ref.'),
  201. _('Partner'), _('Amount'), _('Statement Ref.'), '', '']
  202. col = 0
  203. for col_label in col_labels:
  204. sheet.write(row, col, col_label, col_title)
  205. col += 1
  206. b_start_row = b_end_row = row + 1
  207. for bline in blines:
  208. row += 1
  209. b_end_row = row
  210. bank_bal += bline['amount']
  211. sheet.write(row, 0, bline['date'], regular_date)
  212. sheet.write(row, 1, bline['label'], regular)
  213. sheet.write(row, 2, bline['ref'], regular)
  214. sheet.write(row, 3, bline['partner'], regular)
  215. sheet.write(row, 4, bline['amount'], regular_currency)
  216. sheet.write(
  217. row, 5, bline['statement_ref'], regular_currency)
  218. formula += '+SUM(E%d:E%d)' % (b_start_row + 1, b_end_row + 1)
  219. # 4) Theoric bank account balance at the bank
  220. row += 2
  221. for col in range(3):
  222. sheet.write(row, col, '', title_right)
  223. sheet.write(
  224. row, 3, _('Computed Bank Account Balance at the Bank:'),
  225. title_right)
  226. sheet.write_formula(
  227. row, 4, formula, regular_currency_bg, bank_bal)
  228. if no_bank_journal:
  229. sheet = workbook.add_worksheet(_('No Bank Journal'))
  230. sheet.set_row(0, 30)
  231. warn_msg = workbook.add_format(
  232. {'bold': True, 'font_size': 16, 'font_color': '#003b6f'})
  233. sheet.write(
  234. 0, 0, _(
  235. "No bank journal selected. "
  236. "This report is only for bank journals."), warn_msg)