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.

290 lines
11 KiB

  1. # Author: Julien Coux
  2. # Copyright 2016 Camptocamp SA
  3. # Copyright 2017 Akretion - Alexis de Lattre
  4. # Copyright 2018 ForgeFlow, S.L.
  5. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  6. from odoo import _, api, fields, models
  7. from odoo.exceptions import UserError, ValidationError
  8. class TrialBalanceReportWizard(models.TransientModel):
  9. """Trial balance report wizard."""
  10. _name = "trial.balance.report.wizard"
  11. _description = "Trial Balance Report Wizard"
  12. _inherit = "account_financial_report_abstract_wizard"
  13. date_range_id = fields.Many2one(comodel_name="date.range", string="Date range")
  14. date_from = fields.Date(required=True)
  15. date_to = fields.Date(required=True)
  16. fy_start_date = fields.Date(compute="_compute_fy_start_date")
  17. target_move = fields.Selection(
  18. [("posted", "All Posted Entries"), ("all", "All Entries")],
  19. string="Target Moves",
  20. required=True,
  21. default="posted",
  22. )
  23. hierarchy_on = fields.Selection(
  24. [
  25. ("computed", "Computed Accounts"),
  26. ("relation", "Child Accounts"),
  27. ("none", "No hierarchy"),
  28. ],
  29. string="Hierarchy On",
  30. required=True,
  31. default="none",
  32. help="""Computed Accounts: Use when the account group have codes
  33. that represent prefixes of the actual accounts.\n
  34. Child Accounts: Use when your account groups are hierarchical.\n
  35. No hierarchy: Use to display just the accounts, without any grouping.
  36. """,
  37. )
  38. limit_hierarchy_level = fields.Boolean("Limit hierarchy levels")
  39. show_hierarchy_level = fields.Integer("Hierarchy Levels to display", default=1)
  40. hide_parent_hierarchy_level = fields.Boolean(
  41. "Do not display parent levels", default=False
  42. )
  43. account_ids = fields.Many2many(
  44. comodel_name="account.account", string="Filter accounts"
  45. )
  46. hide_account_at_0 = fields.Boolean(
  47. string="Hide accounts at 0",
  48. default=True,
  49. help="When this option is enabled, the trial balance will "
  50. "not display accounts that have initial balance = "
  51. "debit = credit = end balance = 0",
  52. )
  53. receivable_accounts_only = fields.Boolean()
  54. payable_accounts_only = fields.Boolean()
  55. show_partner_details = fields.Boolean()
  56. partner_ids = fields.Many2many(comodel_name="res.partner", string="Filter partners")
  57. journal_ids = fields.Many2many(comodel_name="account.journal")
  58. not_only_one_unaffected_earnings_account = fields.Boolean(
  59. readonly=True, string="Not only one unaffected earnings account"
  60. )
  61. foreign_currency = fields.Boolean(
  62. string="Show foreign currency",
  63. help="Display foreign currency for move lines, unless "
  64. "account currency is not setup through chart of accounts "
  65. "will display initial and final balance in that currency.",
  66. )
  67. account_code_from = fields.Many2one(
  68. comodel_name="account.account",
  69. string="Account Code From",
  70. help="Starting account in a range",
  71. )
  72. account_code_to = fields.Many2one(
  73. comodel_name="account.account",
  74. string="Account Code To",
  75. help="Ending account in a range",
  76. )
  77. @api.onchange("account_code_from", "account_code_to")
  78. def on_change_account_range(self):
  79. if (
  80. self.account_code_from
  81. and self.account_code_from.code.isdigit()
  82. and self.account_code_to
  83. and self.account_code_to.code.isdigit()
  84. ):
  85. start_range = int(self.account_code_from.code)
  86. end_range = int(self.account_code_to.code)
  87. self.account_ids = self.env["account.account"].search(
  88. [("code", "in", [x for x in range(start_range, end_range + 1)])]
  89. )
  90. if self.company_id:
  91. self.account_ids = self.account_ids.filtered(
  92. lambda a: a.company_id == self.company_id
  93. )
  94. @api.constrains("hierarchy_on", "show_hierarchy_level")
  95. def _check_show_hierarchy_level(self):
  96. for rec in self:
  97. if rec.hierarchy_on != "none" and rec.show_hierarchy_level <= 0:
  98. raise UserError(
  99. _("The hierarchy level to filter on must be " "greater than 0.")
  100. )
  101. @api.depends("date_from")
  102. def _compute_fy_start_date(self):
  103. for wiz in self:
  104. if wiz.date_from:
  105. res = self.company_id.compute_fiscalyear_dates(wiz.date_from)
  106. wiz.fy_start_date = res["date_from"]
  107. else:
  108. wiz.fy_start_date = False
  109. @api.onchange("company_id")
  110. def onchange_company_id(self):
  111. """Handle company change."""
  112. account_type = self.env.ref("account.data_unaffected_earnings")
  113. count = self.env["account.account"].search_count(
  114. [
  115. ("user_type_id", "=", account_type.id),
  116. ("company_id", "=", self.company_id.id),
  117. ]
  118. )
  119. self.not_only_one_unaffected_earnings_account = count != 1
  120. if (
  121. self.company_id
  122. and self.date_range_id.company_id
  123. and self.date_range_id.company_id != self.company_id
  124. ):
  125. self.date_range_id = False
  126. if self.company_id and self.partner_ids:
  127. self.partner_ids = self.partner_ids.filtered(
  128. lambda p: p.company_id == self.company_id or not p.company_id
  129. )
  130. if self.company_id and self.journal_ids:
  131. self.journal_ids = self.journal_ids.filtered(
  132. lambda a: a.company_id == self.company_id
  133. )
  134. if self.company_id and self.account_ids:
  135. if self.receivable_accounts_only or self.payable_accounts_only:
  136. self.onchange_type_accounts_only()
  137. else:
  138. self.account_ids = self.account_ids.filtered(
  139. lambda a: a.company_id == self.company_id
  140. )
  141. res = {
  142. "domain": {
  143. "account_ids": [],
  144. "partner_ids": [],
  145. "date_range_id": [],
  146. "journal_ids": [],
  147. }
  148. }
  149. if not self.company_id:
  150. return res
  151. else:
  152. res["domain"]["account_ids"] += [("company_id", "=", self.company_id.id)]
  153. res["domain"]["partner_ids"] += self._get_partner_ids_domain()
  154. res["domain"]["date_range_id"] += [
  155. "|",
  156. ("company_id", "=", self.company_id.id),
  157. ("company_id", "=", False),
  158. ]
  159. res["domain"]["journal_ids"] += [("company_id", "=", self.company_id.id)]
  160. return res
  161. @api.onchange("date_range_id")
  162. def onchange_date_range_id(self):
  163. """Handle date range change."""
  164. self.date_from = self.date_range_id.date_start
  165. self.date_to = self.date_range_id.date_end
  166. @api.constrains("company_id", "date_range_id")
  167. def _check_company_id_date_range_id(self):
  168. for rec in self.sudo():
  169. if (
  170. rec.company_id
  171. and rec.date_range_id.company_id
  172. and rec.company_id != rec.date_range_id.company_id
  173. ):
  174. raise ValidationError(
  175. _(
  176. "The Company in the Trial Balance Report Wizard and in "
  177. "Date Range must be the same."
  178. )
  179. )
  180. @api.onchange("receivable_accounts_only", "payable_accounts_only")
  181. def onchange_type_accounts_only(self):
  182. """Handle receivable/payable accounts only change."""
  183. if self.receivable_accounts_only or self.payable_accounts_only:
  184. domain = [("company_id", "=", self.company_id.id)]
  185. if self.receivable_accounts_only and self.payable_accounts_only:
  186. domain += [("internal_type", "in", ("receivable", "payable"))]
  187. elif self.receivable_accounts_only:
  188. domain += [("internal_type", "=", "receivable")]
  189. elif self.payable_accounts_only:
  190. domain += [("internal_type", "=", "payable")]
  191. self.account_ids = self.env["account.account"].search(domain)
  192. else:
  193. self.account_ids = None
  194. @api.onchange("show_partner_details")
  195. def onchange_show_partner_details(self):
  196. """Handle partners change."""
  197. if self.show_partner_details:
  198. self.receivable_accounts_only = self.payable_accounts_only = True
  199. else:
  200. self.receivable_accounts_only = self.payable_accounts_only = False
  201. @api.depends("company_id")
  202. def _compute_unaffected_earnings_account(self):
  203. account_type = self.env.ref("account.data_unaffected_earnings")
  204. for record in self:
  205. record.unaffected_earnings_account = self.env["account.account"].search(
  206. [
  207. ("user_type_id", "=", account_type.id),
  208. ("company_id", "=", record.company_id.id),
  209. ]
  210. )
  211. unaffected_earnings_account = fields.Many2one(
  212. comodel_name="account.account",
  213. compute="_compute_unaffected_earnings_account",
  214. store=True,
  215. )
  216. def _print_report(self, report_type):
  217. self.ensure_one()
  218. data = self._prepare_report_trial_balance()
  219. if report_type == "xlsx":
  220. report_name = "a_f_r.report_trial_balance_xlsx"
  221. else:
  222. report_name = "account_financial_report.trial_balance"
  223. return (
  224. self.env["ir.actions.report"]
  225. .search(
  226. [("report_name", "=", report_name), ("report_type", "=", report_type)],
  227. limit=1,
  228. )
  229. .report_action(self, data=data)
  230. )
  231. def button_export_html(self):
  232. self.ensure_one()
  233. report_type = "qweb-html"
  234. return self._export(report_type)
  235. def button_export_pdf(self):
  236. self.ensure_one()
  237. report_type = "qweb-pdf"
  238. return self._export(report_type)
  239. def button_export_xlsx(self):
  240. self.ensure_one()
  241. report_type = "xlsx"
  242. return self._export(report_type)
  243. def _prepare_report_trial_balance(self):
  244. self.ensure_one()
  245. return {
  246. "wizard_id": self.id,
  247. "date_from": self.date_from,
  248. "date_to": self.date_to,
  249. "only_posted_moves": self.target_move == "posted",
  250. "hide_account_at_0": self.hide_account_at_0,
  251. "foreign_currency": self.foreign_currency,
  252. "company_id": self.company_id.id,
  253. "account_ids": self.account_ids.ids or [],
  254. "partner_ids": self.partner_ids.ids or [],
  255. "journal_ids": self.journal_ids.ids or [],
  256. "fy_start_date": self.fy_start_date,
  257. "hierarchy_on": self.hierarchy_on,
  258. "limit_hierarchy_level": self.limit_hierarchy_level,
  259. "show_hierarchy_level": self.show_hierarchy_level,
  260. "hide_parent_hierarchy_level": self.hide_parent_hierarchy_level,
  261. "show_partner_details": self.show_partner_details,
  262. "unaffected_earnings_account": self.unaffected_earnings_account.id,
  263. "account_financial_report_lang": self.env.lang,
  264. }
  265. def _export(self, report_type):
  266. """Default export is PDF."""
  267. return self._print_report(report_type)