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.

258 lines
9.8 KiB

5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. # Copyright 2016 Lorenzo Battistini - Agile Business Group
  2. # Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. from odoo import api, fields, models
  5. class AccountTax(models.Model):
  6. _inherit = "account.tax"
  7. balance = fields.Float(string="Total Balance", compute="_compute_balance")
  8. base_balance = fields.Float(string="Total Base Balance", compute="_compute_balance")
  9. balance_regular = fields.Float(string="Balance", compute="_compute_balance")
  10. base_balance_regular = fields.Float(
  11. string="Base Balance", compute="_compute_balance"
  12. )
  13. balance_refund = fields.Float(string="Balance Refund", compute="_compute_balance")
  14. base_balance_refund = fields.Float(
  15. string="Base Balance Refund", compute="_compute_balance"
  16. )
  17. has_moves = fields.Boolean(
  18. compute="_compute_balance",
  19. search="_search_has_moves",
  20. string="Has balance in period",
  21. )
  22. def get_context_values(self):
  23. context = self.env.context
  24. actual_company_id = context.get("company_id", self.env.company.id)
  25. return (
  26. context.get("from_date", fields.Date.context_today(self)),
  27. context.get("to_date", fields.Date.context_today(self)),
  28. context.get("company_ids", [actual_company_id]),
  29. context.get("target_move", "posted"),
  30. )
  31. @api.model
  32. def _search_has_moves(self, operator, value):
  33. assert isinstance(value, bool), "Not implemented"
  34. assert operator == "=", "Not implemented"
  35. ids_with_moves = self.search([]).filtered(lambda t: t.has_moves == value)
  36. return [("id", "in", ids_with_moves.ids)]
  37. def _compute_regular_and_refund(self, total):
  38. tax_ids = total.keys()
  39. total_refund = {}
  40. total_regular = {}
  41. for tax_id in tax_ids:
  42. total_refund[tax_id] = 0.0
  43. total_regular[tax_id] = 0.0
  44. move_types = total[tax_id].keys()
  45. for move_type in move_types:
  46. if move_type in ["receivable_refund", "payable_refund"]:
  47. total_refund[tax_id] += (-1) * total[tax_id][move_type]
  48. else:
  49. total_regular[tax_id] += (-1) * total[tax_id][move_type]
  50. return total_regular, total_refund
  51. def _compute_balance(self):
  52. total_balance_tax = self.compute_balance(tax_or_base="tax")
  53. total_balance_base = self.compute_balance(tax_or_base="base")
  54. total_balance_regular, total_balance_refund = self._compute_regular_and_refund(
  55. total_balance_tax
  56. )
  57. (
  58. total_base_balance_regular,
  59. total_base_balance_refund,
  60. ) = self._compute_regular_and_refund(total_balance_base)
  61. founded_taxes_ids = set(list(total_balance_tax.keys())).union(
  62. set(list(total_balance_base.keys()))
  63. )
  64. for tax in self:
  65. tax.has_moves = tax.id in list(founded_taxes_ids)
  66. tax.balance_regular = (
  67. total_balance_regular[tax.id]
  68. if tax.id in total_balance_regular.keys()
  69. else 0.0
  70. )
  71. tax.base_balance_regular = (
  72. total_base_balance_regular[tax.id]
  73. if tax.id in total_base_balance_regular.keys()
  74. else 0.0
  75. )
  76. tax.balance_refund = (
  77. total_balance_refund[tax.id]
  78. if tax.id in total_balance_refund.keys()
  79. else 0.0
  80. )
  81. tax.base_balance_refund = (
  82. total_base_balance_refund[tax.id]
  83. if tax.id in total_base_balance_refund.keys()
  84. else 0.0
  85. )
  86. tax.balance = tax.balance_regular + tax.balance_refund
  87. tax.base_balance = tax.base_balance_regular + tax.base_balance_refund
  88. def get_target_type_list(self, move_type=None):
  89. if move_type == "refund":
  90. return ["receivable_refund", "payable_refund"]
  91. elif move_type == "regular":
  92. return ["receivable", "payable", "liquidity", "other"]
  93. return []
  94. def get_target_state_list(self, target_move="posted"):
  95. if target_move == "posted":
  96. state = ["posted"]
  97. elif target_move == "all":
  98. state = ["posted", "draft"]
  99. else:
  100. state = []
  101. return state
  102. def get_move_line_partial_where(self, from_date, to_date, company_ids):
  103. query = "aml.date <= %s AND aml.date >= %s AND aml.company_id IN %s"
  104. params = [to_date, from_date, tuple(company_ids)]
  105. return query, params
  106. def compute_balance(self, tax_or_base="tax"):
  107. # There's really bad performace in m2m fields.
  108. # So we better do a direct query.
  109. # See https://github.com/odoo/odoo/issues/30350
  110. _select, _group_by, query, params = self.get_move_lines_query(
  111. tax_or_base=tax_or_base
  112. )
  113. query = query.format(select_clause=_select, group_by_clause=_group_by)
  114. self.env.cr.execute(query, params) # pylint: disable=E8103
  115. results = self.env.cr.fetchall()
  116. total_balance = {}
  117. for balance, tax_id, move_type in results:
  118. if tax_id not in total_balance.keys():
  119. total_balance[tax_id] = {}
  120. total_balance[tax_id][move_type] = balance
  121. return total_balance
  122. def get_move_lines_query(self, tax_or_base="tax"):
  123. from_date, to_date, company_ids, target_move = self.get_context_values()
  124. state_list = self.get_target_state_list(target_move)
  125. base_query = self.get_move_lines_base_query()
  126. _where = ""
  127. _joins = ""
  128. _group_by = ""
  129. _params = []
  130. _select = "SELECT SUM(balance)"
  131. _group_by = " GROUP BY am.move_type, "
  132. where, params = self.get_move_line_partial_where(
  133. from_date, to_date, company_ids
  134. )
  135. _where += where
  136. _params += params
  137. if tax_or_base == "tax":
  138. select, where, group_by, params = self.get_balance_where(state_list)
  139. _where += where
  140. _params += params
  141. _select += select
  142. _group_by += group_by
  143. elif tax_or_base == "base":
  144. select, joins, where, group_by, params = self.get_base_balance_where(
  145. state_list
  146. )
  147. _where += where
  148. _joins += joins
  149. _params += params
  150. _select += select
  151. _group_by += group_by
  152. query = base_query.format(
  153. select_clause="{select_clause}",
  154. where_clause=_where,
  155. additional_joins=_joins,
  156. group_by_clause="{group_by_clause}",
  157. )
  158. return _select, _group_by, query, _params
  159. def get_move_lines_base_query(self):
  160. return (
  161. "{select_clause} FROM account_move_line AS aml "
  162. "INNER JOIN account_move AS am ON aml.move_id = am.id "
  163. "{additional_joins}"
  164. " WHERE {where_clause}"
  165. "{group_by_clause}"
  166. )
  167. def get_balance_where(self, state_list):
  168. select = ", aml.tax_line_id as tax_id, am.move_type"
  169. where = (
  170. " AND am.state IN %s"
  171. " AND aml.tax_line_id IS NOT NULL"
  172. " AND aml.tax_exigible = True"
  173. )
  174. group_by = "aml.tax_line_id"
  175. params = [tuple(state_list)]
  176. return select, where, group_by, params
  177. def get_base_balance_where(self, state_list):
  178. select = ", rel.account_tax_id as tax_id, am.move_type"
  179. joins = (
  180. " INNER JOIN account_move_line_account_tax_rel AS rel "
  181. "ON aml.id = rel.account_move_line_id"
  182. )
  183. group_by = "rel.account_tax_id"
  184. where = " AND am.state IN %s" " AND aml.tax_exigible = True "
  185. params = [tuple(state_list)]
  186. return select, joins, where, group_by, params
  187. def get_move_lines_domain(self, tax_or_base="tax", move_type=None):
  188. _select, _group_by, query, params = self.get_move_lines_query(
  189. tax_or_base=tax_or_base
  190. )
  191. _select = "SELECT aml.id"
  192. _group_by = ""
  193. if tax_or_base == "tax":
  194. query += " AND aml.tax_line_id = " + str(self.id)
  195. elif tax_or_base == "base":
  196. query += " AND rel.account_tax_id = " + str(self.id)
  197. type_list = self.get_target_type_list(move_type)
  198. if type_list:
  199. query += " AND am.move_type IN %s"
  200. params += [tuple(type_list)]
  201. query = query.format(select_clause=_select, group_by_clause=_group_by)
  202. self.env.cr.execute(query, params) # pylint: disable=E8103
  203. amls = []
  204. for (aml_id,) in self.env.cr.fetchall():
  205. amls.append(aml_id)
  206. domain = [("id", "in", amls)]
  207. return domain
  208. def get_lines_action(self, tax_or_base="tax", move_type=None):
  209. domain = self.get_move_lines_domain(
  210. tax_or_base=tax_or_base, move_type=move_type
  211. )
  212. action = self.env.ref("account.action_account_moves_all_tree")
  213. vals = action.read()[0]
  214. vals["context"] = {}
  215. vals["domain"] = domain
  216. return vals
  217. def view_tax_lines(self):
  218. self.ensure_one()
  219. return self.get_lines_action(tax_or_base="tax")
  220. def view_base_lines(self):
  221. self.ensure_one()
  222. return self.get_lines_action(tax_or_base="base")
  223. def view_tax_regular_lines(self):
  224. self.ensure_one()
  225. return self.get_lines_action(tax_or_base="tax", move_type="regular")
  226. def view_base_regular_lines(self):
  227. self.ensure_one()
  228. return self.get_lines_action(tax_or_base="base", move_type="regular")
  229. def view_tax_refund_lines(self):
  230. self.ensure_one()
  231. return self.get_lines_action(tax_or_base="tax", move_type="refund")
  232. def view_base_refund_lines(self):
  233. self.ensure_one()
  234. return self.get_lines_action(tax_or_base="base", move_type="refund")