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.

294 lines
11 KiB

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