analytic_account.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from collections import defaultdict
  4. from odoo import api, fields, models, _
  5. from odoo.exceptions import UserError
  6. class AccountAnalyticAccount(models.Model):
  7. _name = 'account.analytic.account'
  8. _inherit = ['mail.thread']
  9. _description = 'Analytic Account'
  10. _order = 'plan_id, name asc'
  11. _check_company_auto = True
  12. _rec_names_search = ['name', 'code', 'partner_id']
  13. name = fields.Char(
  14. string='Analytic Account',
  15. index='trigram',
  16. required=True,
  17. tracking=True,
  18. )
  19. code = fields.Char(
  20. string='Reference',
  21. index='btree',
  22. tracking=True,
  23. )
  24. active = fields.Boolean(
  25. 'Active',
  26. help="Deactivate the account.",
  27. default=True,
  28. tracking=True,
  29. )
  30. plan_id = fields.Many2one(
  31. 'account.analytic.plan',
  32. string='Plan',
  33. check_company=True,
  34. required=True,
  35. )
  36. root_plan_id = fields.Many2one(
  37. 'account.analytic.plan',
  38. string='Root Plan',
  39. check_company=True,
  40. compute="_compute_root_plan",
  41. store=True,
  42. )
  43. color = fields.Integer(
  44. 'Color Index',
  45. related='plan_id.color',
  46. )
  47. line_ids = fields.One2many(
  48. 'account.analytic.line',
  49. 'account_id',
  50. string="Analytic Lines",
  51. )
  52. company_id = fields.Many2one(
  53. 'res.company',
  54. string='Company',
  55. default=lambda self: self.env.company,
  56. )
  57. # use auto_join to speed up name_search call
  58. partner_id = fields.Many2one(
  59. 'res.partner',
  60. string='Customer',
  61. auto_join=True,
  62. tracking=True,
  63. check_company=True,
  64. )
  65. balance = fields.Monetary(
  66. compute='_compute_debit_credit_balance',
  67. string='Balance',
  68. groups='account.group_account_readonly',
  69. )
  70. debit = fields.Monetary(
  71. compute='_compute_debit_credit_balance',
  72. string='Debit',
  73. groups='account.group_account_readonly',
  74. )
  75. credit = fields.Monetary(
  76. compute='_compute_debit_credit_balance',
  77. string='Credit',
  78. groups='account.group_account_readonly',
  79. )
  80. currency_id = fields.Many2one(
  81. related="company_id.currency_id",
  82. string="Currency",
  83. )
  84. @api.constrains('company_id')
  85. def _check_company_consistency(self):
  86. analytic_accounts = self.filtered('company_id')
  87. if not analytic_accounts:
  88. return
  89. self.flush_recordset(['company_id'])
  90. self.env['account.analytic.line'].flush_model(['account_id', 'company_id'])
  91. self._cr.execute('''
  92. SELECT line.account_id
  93. FROM account_analytic_line line
  94. JOIN account_analytic_account account ON line.account_id = account.id
  95. WHERE line.company_id != account.company_id and account.company_id IS NOT NULL
  96. AND account.id IN %s
  97. ''', [tuple(self.ids)])
  98. if self._cr.fetchone():
  99. raise UserError(_("You can't set a different company on your analytic account since there are some analytic items linked to it."))
  100. def name_get(self):
  101. res = []
  102. for analytic in self:
  103. name = analytic.name
  104. if analytic.code:
  105. name = f'[{analytic.code}] {name}'
  106. if analytic.partner_id.commercial_partner_id.name:
  107. name = f'{name} - {analytic.partner_id.commercial_partner_id.name}'
  108. res.append((analytic.id, name))
  109. return res
  110. def copy_data(self, default=None):
  111. default = dict(default or {})
  112. default.setdefault('name', _("%s (copy)", self.name))
  113. return super().copy_data(default)
  114. @api.model
  115. def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
  116. """
  117. Override read_group to calculate the sum of the non-stored fields that depend on the user context
  118. """
  119. res = super(AccountAnalyticAccount, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
  120. accounts = self.env['account.analytic.account']
  121. for line in res:
  122. if '__domain' in line:
  123. accounts = self.search(line['__domain'])
  124. if 'balance' in fields:
  125. line['balance'] = sum(accounts.mapped('balance'))
  126. if 'debit' in fields:
  127. line['debit'] = sum(accounts.mapped('debit'))
  128. if 'credit' in fields:
  129. line['credit'] = sum(accounts.mapped('credit'))
  130. return res
  131. @api.depends('line_ids.amount')
  132. def _compute_debit_credit_balance(self):
  133. Curr = self.env['res.currency']
  134. analytic_line_obj = self.env['account.analytic.line']
  135. domain = [
  136. ('account_id', 'in', self.ids),
  137. ('company_id', 'in', [False] + self.env.companies.ids)
  138. ]
  139. if self._context.get('from_date', False):
  140. domain.append(('date', '>=', self._context['from_date']))
  141. if self._context.get('to_date', False):
  142. domain.append(('date', '<=', self._context['to_date']))
  143. user_currency = self.env.company.currency_id
  144. credit_groups = analytic_line_obj.read_group(
  145. domain=domain + [('amount', '>=', 0.0)],
  146. fields=['account_id', 'currency_id', 'amount'],
  147. groupby=['account_id', 'currency_id'],
  148. lazy=False,
  149. )
  150. data_credit = defaultdict(float)
  151. for l in credit_groups:
  152. data_credit[l['account_id'][0]] += Curr.browse(l['currency_id'][0])._convert(
  153. l['amount'], user_currency, self.env.company, fields.Date.today())
  154. debit_groups = analytic_line_obj.read_group(
  155. domain=domain + [('amount', '<', 0.0)],
  156. fields=['account_id', 'currency_id', 'amount'],
  157. groupby=['account_id', 'currency_id'],
  158. lazy=False,
  159. )
  160. data_debit = defaultdict(float)
  161. for l in debit_groups:
  162. data_debit[l['account_id'][0]] += Curr.browse(l['currency_id'][0])._convert(
  163. l['amount'], user_currency, self.env.company, fields.Date.today())
  164. for account in self:
  165. account.debit = abs(data_debit.get(account.id, 0.0))
  166. account.credit = data_credit.get(account.id, 0.0)
  167. account.balance = account.credit - account.debit
  168. @api.depends('plan_id', 'plan_id.parent_path')
  169. def _compute_root_plan(self):
  170. for account in self:
  171. account.root_plan_id = int(account.plan_id.parent_path[:-1].split('/')[0]) if account.plan_id.parent_path else None