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.

278 lines
10 KiB

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