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.

586 lines
20 KiB

  1. # © 2016 Julien Coux (Camptocamp)
  2. # © 2018 Forest and Biomass Romania SA
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. from odoo import models, fields, api
  5. from odoo.tools import float_is_zero
  6. class TrialBalanceReport(models.TransientModel):
  7. """ Here, we just define class fields.
  8. For methods, go more bottom at this file.
  9. The class hierarchy is :
  10. * TrialBalanceReport
  11. *** TrialBalanceReportAccount
  12. **** TrialBalanceReportPartner
  13. If "show_partner_details" is selected
  14. """
  15. _name = 'report_trial_balance'
  16. _inherit = 'account_financial_report_abstract'
  17. # Filters fields, used for data computation
  18. date_from = fields.Date()
  19. date_to = fields.Date()
  20. fy_start_date = fields.Date()
  21. only_posted_moves = fields.Boolean()
  22. hide_account_at_0 = fields.Boolean()
  23. foreign_currency = fields.Boolean()
  24. company_id = fields.Many2one(comodel_name='res.company')
  25. filter_account_ids = fields.Many2many(comodel_name='account.account')
  26. filter_partner_ids = fields.Many2many(comodel_name='res.partner')
  27. filter_journal_ids = fields.Many2many(comodel_name='account.journal')
  28. show_partner_details = fields.Boolean()
  29. hierarchy_on = fields.Selection(
  30. [('computed', 'Computed Accounts'),
  31. ('relation', 'Child Accounts'),
  32. ('none', 'No hierarchy')],
  33. string='Hierarchy On',
  34. required=True,
  35. default='computed',
  36. help="""Computed Accounts: Use when the account group have codes
  37. that represent prefixes of the actual accounts.\n
  38. Child Accounts: Use when your account groups are hierarchical.\n
  39. No hierarchy: Use to display just the accounts, without any grouping.
  40. """,
  41. )
  42. limit_hierarchy_level = fields.Boolean('Limit hierarchy levels')
  43. show_hierarchy_level = fields.Integer('Hierarchy Levels to display',
  44. default=1)
  45. hide_parent_hierarchy_level = fields.Boolean(
  46. 'Do not display parent levels', default=False)
  47. # General Ledger Report Data fields,
  48. # used as base for compute the data reports
  49. general_ledger_id = fields.Many2one(
  50. comodel_name='report_general_ledger'
  51. )
  52. # Data fields, used to browse report data
  53. account_ids = fields.One2many(
  54. comodel_name='report_trial_balance_account',
  55. inverse_name='report_id'
  56. )
  57. class TrialBalanceReportAccount(models.TransientModel):
  58. _name = 'report_trial_balance_account'
  59. _inherit = 'account_financial_report_abstract'
  60. _order = 'sequence, code ASC, name'
  61. report_id = fields.Many2one(
  62. comodel_name='report_trial_balance',
  63. ondelete='cascade',
  64. index=True
  65. )
  66. hide_line = fields.Boolean(compute='_compute_hide_line')
  67. # Data fields, used to keep link with real object
  68. sequence = fields.Integer(index=True, default=1)
  69. level = fields.Integer(index=True, default=1)
  70. # Data fields, used to keep link with real object
  71. account_id = fields.Many2one(
  72. 'account.account',
  73. index=True
  74. )
  75. account_group_id = fields.Many2one(
  76. 'account.group',
  77. index=True
  78. )
  79. parent_id = fields.Many2one(
  80. 'account.group',
  81. index=True
  82. )
  83. child_account_ids = fields.Char(
  84. string="Accounts")
  85. compute_account_ids = fields.Many2many(
  86. 'account.account',
  87. string="Accounts", store=True)
  88. # Data fields, used for report display
  89. code = fields.Char()
  90. name = fields.Char()
  91. currency_id = fields.Many2one('res.currency')
  92. initial_balance = fields.Float(digits=(16, 2))
  93. initial_balance_foreign_currency = fields.Float(digits=(16, 2))
  94. debit = fields.Float(digits=(16, 2))
  95. credit = fields.Float(digits=(16, 2))
  96. period_balance = fields.Float(digits=(16, 2))
  97. final_balance = fields.Float(digits=(16, 2))
  98. final_balance_foreign_currency = fields.Float(digits=(16, 2))
  99. # Data fields, used to browse report data
  100. partner_ids = fields.One2many(
  101. comodel_name='report_trial_balance_partner',
  102. inverse_name='report_account_id'
  103. )
  104. @api.depends(
  105. 'currency_id',
  106. 'report_id',
  107. 'report_id.hide_account_at_0',
  108. 'report_id.limit_hierarchy_level',
  109. 'report_id.show_hierarchy_level',
  110. 'initial_balance',
  111. 'final_balance',
  112. 'debit',
  113. 'credit',
  114. )
  115. def _compute_hide_line(self):
  116. for rec in self:
  117. report = rec.report_id
  118. r = (rec.currency_id or report.company_id.currency_id).rounding
  119. if report.hide_account_at_0 and (
  120. float_is_zero(rec.initial_balance, precision_rounding=r)
  121. and float_is_zero(rec.final_balance, precision_rounding=r)
  122. and float_is_zero(rec.debit, precision_rounding=r)
  123. and float_is_zero(rec.credit, precision_rounding=r)):
  124. rec.hide_line = True
  125. elif report.limit_hierarchy_level and report.show_hierarchy_level:
  126. if report.hide_parent_hierarchy_level:
  127. distinct_level = rec.level != report.show_hierarchy_level
  128. if rec.account_group_id and distinct_level:
  129. rec.hide_line = True
  130. elif rec.level and distinct_level:
  131. rec.hide_line = True
  132. elif not report.hide_parent_hierarchy_level and \
  133. rec.level > report.show_hierarchy_level:
  134. rec.hide_line = True
  135. class TrialBalanceReportPartner(models.TransientModel):
  136. _name = 'report_trial_balance_partner'
  137. _inherit = 'account_financial_report_abstract'
  138. report_account_id = fields.Many2one(
  139. comodel_name='report_trial_balance_account',
  140. ondelete='cascade',
  141. index=True
  142. )
  143. # Data fields, used to keep link with real object
  144. partner_id = fields.Many2one(
  145. 'res.partner',
  146. index=True
  147. )
  148. # Data fields, used for report display
  149. name = fields.Char()
  150. currency_id = fields.Many2one('res.currency')
  151. initial_balance = fields.Float(digits=(16, 2))
  152. initial_balance_foreign_currency = fields.Float(digits=(16, 2))
  153. debit = fields.Float(digits=(16, 2))
  154. credit = fields.Float(digits=(16, 2))
  155. period_balance = fields.Float(digits=(16, 2))
  156. final_balance = fields.Float(digits=(16, 2))
  157. final_balance_foreign_currency = fields.Float(digits=(16, 2))
  158. @api.model
  159. def _generate_order_by(self, order_spec, query):
  160. """Custom order to display "No partner allocated" at last position."""
  161. return """
  162. ORDER BY
  163. CASE
  164. WHEN "report_trial_balance_partner"."partner_id" IS NOT NULL
  165. THEN 0
  166. ELSE 1
  167. END,
  168. "report_trial_balance_partner"."name"
  169. """
  170. class TrialBalanceReportCompute(models.TransientModel):
  171. """ Here, we just define methods.
  172. For class fields, go more top at this file.
  173. """
  174. _inherit = 'report_trial_balance'
  175. @api.multi
  176. def print_report(self, report_type):
  177. self.ensure_one()
  178. if report_type == 'xlsx':
  179. report_name = 'a_f_r.report_trial_balance_xlsx'
  180. else:
  181. report_name = 'account_financial_report.' \
  182. 'report_trial_balance_qweb'
  183. return self.env['ir.actions.report'].search(
  184. [('report_name', '=', report_name),
  185. ('report_type', '=', report_type)], limit=1).report_action(self)
  186. def _get_html(self):
  187. result = {}
  188. rcontext = {}
  189. context = dict(self.env.context)
  190. report = self.browse(context.get('active_id'))
  191. if report:
  192. rcontext['o'] = report
  193. result['html'] = self.env.ref(
  194. 'account_financial_report.report_trial_balance').render(
  195. rcontext)
  196. return result
  197. @api.model
  198. def get_html(self, given_context=None):
  199. return self._get_html()
  200. def _prepare_report_general_ledger(self, account_ids):
  201. self.ensure_one()
  202. return {
  203. 'date_from': self.date_from,
  204. 'date_to': self.date_to,
  205. 'only_posted_moves': self.only_posted_moves,
  206. # This is postprocessed later with a computed field
  207. 'hide_account_at_0': False,
  208. 'foreign_currency': self.foreign_currency,
  209. 'company_id': self.company_id.id,
  210. 'filter_account_ids': [(6, 0, account_ids.ids)],
  211. 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
  212. 'filter_journal_ids': [(6, 0, self.filter_journal_ids.ids)],
  213. 'fy_start_date': self.fy_start_date,
  214. }
  215. @api.multi
  216. def compute_data_for_report(self):
  217. self.ensure_one()
  218. # Compute General Ledger Report Data.
  219. # The data of Trial Balance Report
  220. # are based on General Ledger Report data.
  221. model = self.env['report_general_ledger']
  222. if self.filter_account_ids:
  223. account_ids = self.filter_account_ids
  224. else:
  225. account_ids = self.env['account.account'].search(
  226. [('company_id', '=', self.company_id.id)])
  227. self.general_ledger_id = model.create(
  228. self._prepare_report_general_ledger(account_ids)
  229. )
  230. self.general_ledger_id.compute_data_for_report(
  231. with_line_details=False, with_partners=self.show_partner_details
  232. )
  233. # Compute report data
  234. self._inject_account_values(account_ids)
  235. if self.show_partner_details:
  236. self._inject_partner_values()
  237. if not self.filter_account_ids:
  238. if self.hierarchy_on != 'none':
  239. self._inject_account_group_values()
  240. if self.hierarchy_on == 'computed':
  241. self._update_account_group_computed_values()
  242. else:
  243. self._update_account_group_child_values()
  244. self._update_account_sequence()
  245. self._add_account_group_account_values()
  246. self.refresh()
  247. if not self.filter_account_ids and self.hierarchy_on != 'none':
  248. self._compute_group_accounts()
  249. else:
  250. for line in self.account_ids:
  251. line.write({'level': 0})
  252. def _inject_account_values(self, account_ids):
  253. """Inject report values for report_trial_balance_account"""
  254. query_inject_account = """
  255. INSERT INTO
  256. report_trial_balance_account
  257. (
  258. report_id,
  259. create_uid,
  260. create_date,
  261. account_id,
  262. parent_id,
  263. code,
  264. name,
  265. initial_balance,
  266. debit,
  267. credit,
  268. period_balance,
  269. final_balance,
  270. currency_id,
  271. initial_balance_foreign_currency,
  272. final_balance_foreign_currency
  273. )
  274. SELECT
  275. %s AS report_id,
  276. %s AS create_uid,
  277. NOW() AS create_date,
  278. acc.id,
  279. acc.group_id,
  280. acc.code,
  281. acc.name,
  282. coalesce(rag.initial_balance, 0) AS initial_balance,
  283. coalesce(rag.final_debit - rag.initial_debit, 0) AS debit,
  284. coalesce(rag.final_credit - rag.initial_credit, 0) AS credit,
  285. coalesce(rag.final_balance - rag.initial_balance, 0) AS period_balance,
  286. coalesce(rag.final_balance, 0) AS final_balance,
  287. rag.currency_id AS currency_id,
  288. coalesce(rag.initial_balance_foreign_currency, 0)
  289. AS initial_balance_foreign_currency,
  290. coalesce(rag.final_balance_foreign_currency, 0)
  291. AS final_balance_foreign_currency
  292. FROM
  293. account_account acc
  294. LEFT OUTER JOIN report_general_ledger_account AS rag
  295. ON rag.account_id = acc.id AND rag.report_id = %s
  296. WHERE
  297. acc.id in %s
  298. """
  299. query_inject_account_params = (
  300. self.id,
  301. self.env.uid,
  302. self.general_ledger_id.id,
  303. account_ids._ids,
  304. )
  305. self.env.cr.execute(query_inject_account, query_inject_account_params)
  306. def _inject_partner_values(self):
  307. """Inject report values for report_trial_balance_partner"""
  308. query_inject_partner = """
  309. INSERT INTO
  310. report_trial_balance_partner
  311. (
  312. report_account_id,
  313. create_uid,
  314. create_date,
  315. partner_id,
  316. name,
  317. initial_balance,
  318. initial_balance_foreign_currency,
  319. debit,
  320. credit,
  321. period_balance,
  322. final_balance,
  323. final_balance_foreign_currency
  324. )
  325. SELECT
  326. ra.id AS report_account_id,
  327. %s AS create_uid,
  328. NOW() AS create_date,
  329. rpg.partner_id,
  330. rpg.name,
  331. rpg.initial_balance AS initial_balance,
  332. rpg.initial_balance_foreign_currency AS initial_balance_foreign_currency,
  333. rpg.final_debit - rpg.initial_debit AS debit,
  334. rpg.final_credit - rpg.initial_credit AS credit,
  335. rpg.final_balance - rpg.initial_balance AS period_balance,
  336. rpg.final_balance AS final_balance,
  337. rpg.final_balance_foreign_currency AS final_balance_foreign_currency
  338. FROM
  339. report_general_ledger_partner rpg
  340. INNER JOIN
  341. report_general_ledger_account rag ON rpg.report_account_id = rag.id
  342. INNER JOIN
  343. report_trial_balance_account ra ON rag.code = ra.code
  344. WHERE
  345. rag.report_id = %s
  346. AND ra.report_id = %s
  347. """
  348. query_inject_partner_params = (
  349. self.env.uid,
  350. self.general_ledger_id.id,
  351. self.id,
  352. )
  353. self.env.cr.execute(query_inject_partner, query_inject_partner_params)
  354. def _inject_account_group_values(self):
  355. """Inject report values for report_trial_balance_account"""
  356. query_inject_account_group = """
  357. INSERT INTO
  358. report_trial_balance_account
  359. (
  360. report_id,
  361. create_uid,
  362. create_date,
  363. account_group_id,
  364. parent_id,
  365. code,
  366. name,
  367. sequence,
  368. level
  369. )
  370. SELECT
  371. %s AS report_id,
  372. %s AS create_uid,
  373. NOW() AS create_date,
  374. accgroup.id,
  375. accgroup.parent_id,
  376. coalesce(accgroup.code_prefix, accgroup.name),
  377. accgroup.name,
  378. accgroup.parent_left * 100000,
  379. accgroup.level
  380. FROM
  381. account_group accgroup"""
  382. query_inject_account_params = (
  383. self.id,
  384. self.env.uid,
  385. )
  386. self.env.cr.execute(query_inject_account_group,
  387. query_inject_account_params)
  388. def _update_account_group_child_values(self):
  389. """Compute values for report_trial_balance_account group in child."""
  390. query_update_account_group = """
  391. WITH computed AS (WITH RECURSIVE cte AS (
  392. SELECT account_group_id, code, account_group_id AS parent_id,
  393. initial_balance, initial_balance_foreign_currency, debit, credit,
  394. period_balance, final_balance, final_balance_foreign_currency
  395. FROM report_trial_balance_account
  396. WHERE report_id = %s
  397. GROUP BY report_trial_balance_account.id
  398. UNION ALL
  399. SELECT c.account_group_id, c.code, p.account_group_id,
  400. p.initial_balance, p.initial_balance_foreign_currency, p.debit, p.credit,
  401. p.period_balance, p.final_balance, p.final_balance_foreign_currency
  402. FROM cte c
  403. JOIN report_trial_balance_account p USING (parent_id)
  404. WHERE p.report_id = %s
  405. )
  406. SELECT account_group_id, code,
  407. sum(initial_balance) AS initial_balance,
  408. sum(initial_balance_foreign_currency) AS initial_balance_foreign_currency,
  409. sum(debit) AS debit,
  410. sum(credit) AS credit,
  411. sum(debit) - sum(credit) AS period_balance,
  412. sum(final_balance) AS final_balance,
  413. sum(final_balance_foreign_currency) AS final_balance_foreign_currency
  414. FROM cte
  415. GROUP BY cte.account_group_id, cte.code
  416. ORDER BY account_group_id
  417. )
  418. UPDATE report_trial_balance_account
  419. SET initial_balance = computed.initial_balance,
  420. initial_balance_foreign_currency =
  421. computed.initial_balance_foreign_currency,
  422. debit = computed.debit,
  423. credit = computed.credit,
  424. period_balance = computed.period_balance,
  425. final_balance = computed.final_balance,
  426. final_balance_foreign_currency =
  427. computed.final_balance_foreign_currency
  428. FROM computed
  429. WHERE report_trial_balance_account.account_group_id = computed.account_group_id
  430. AND report_trial_balance_account.report_id = %s
  431. """
  432. query_update_account_params = (self.id, self.id, self.id,)
  433. self.env.cr.execute(query_update_account_group,
  434. query_update_account_params)
  435. def _add_account_group_account_values(self):
  436. """Compute values for report_trial_balance_account group in child."""
  437. query_update_account_group = """
  438. DROP AGGREGATE IF EXISTS array_concat_agg(anyarray);
  439. CREATE AGGREGATE array_concat_agg(anyarray) (
  440. SFUNC = array_cat,
  441. STYPE = anyarray
  442. );
  443. WITH aggr AS(WITH computed AS (WITH RECURSIVE cte AS (
  444. SELECT account_group_id, account_group_id AS parent_id,
  445. ARRAY[account_id]::int[] as child_account_ids
  446. FROM report_trial_balance_account
  447. WHERE report_id = %s
  448. GROUP BY report_trial_balance_account.id
  449. UNION ALL
  450. SELECT c.account_group_id, p.account_group_id, ARRAY[p.account_id]::int[]
  451. FROM cte c
  452. JOIN report_trial_balance_account p USING (parent_id)
  453. WHERE p.report_id = %s
  454. )
  455. SELECT account_group_id,
  456. array_concat_agg(DISTINCT child_account_ids)::int[] as child_account_ids
  457. FROM cte
  458. GROUP BY cte.account_group_id, cte.child_account_ids
  459. ORDER BY account_group_id
  460. )
  461. SELECT account_group_id,
  462. array_concat_agg(DISTINCT child_account_ids)::int[]
  463. AS child_account_ids from computed
  464. GROUP BY account_group_id)
  465. UPDATE report_trial_balance_account
  466. SET child_account_ids = aggr.child_account_ids
  467. FROM aggr
  468. WHERE report_trial_balance_account.account_group_id = aggr.account_group_id
  469. AND report_trial_balance_account.report_id = %s
  470. """
  471. query_update_account_params = (self.id, self.id, self.id,)
  472. self.env.cr.execute(query_update_account_group,
  473. query_update_account_params)
  474. def _update_account_group_computed_values(self):
  475. """Compute values for report_trial_balance_account group in compute."""
  476. query_update_account_group = """
  477. WITH RECURSIVE accgroup AS
  478. (SELECT
  479. accgroup.id,
  480. sum(coalesce(ra.initial_balance, 0)) as initial_balance,
  481. sum(coalesce(ra.initial_balance_foreign_currency, 0))
  482. as initial_balance_foreign_currency,
  483. sum(coalesce(ra.debit, 0)) as debit,
  484. sum(coalesce(ra.credit, 0)) as credit,
  485. sum(coalesce(ra.debit, 0)) - sum(coalesce(ra.credit, 0)) as period_balance,
  486. sum(coalesce(ra.final_balance, 0)) as final_balance,
  487. sum(coalesce(ra.final_balance_foreign_currency, 0))
  488. as final_balance_foreign_currency
  489. FROM
  490. account_group accgroup
  491. LEFT OUTER JOIN account_account AS acc
  492. ON strpos(acc.code, accgroup.code_prefix) = 1
  493. LEFT OUTER JOIN report_trial_balance_account AS ra
  494. ON ra.account_id = acc.id
  495. WHERE ra.report_id = %s
  496. GROUP BY accgroup.id
  497. )
  498. UPDATE report_trial_balance_account
  499. SET initial_balance = accgroup.initial_balance,
  500. initial_balance_foreign_currency =
  501. accgroup.initial_balance_foreign_currency,
  502. debit = accgroup.debit,
  503. credit = accgroup.credit,
  504. period_balance = accgroup.period_balance,
  505. final_balance = accgroup.final_balance,
  506. final_balance_foreign_currency =
  507. accgroup.final_balance_foreign_currency
  508. FROM accgroup
  509. WHERE report_trial_balance_account.account_group_id = accgroup.id
  510. """
  511. query_update_account_params = (self.id,)
  512. self.env.cr.execute(query_update_account_group,
  513. query_update_account_params)
  514. def _update_account_sequence(self):
  515. """Compute sequence, level for report_trial_balance_account account."""
  516. query_update_account_group = """
  517. UPDATE report_trial_balance_account
  518. SET sequence = newline.sequence + 1,
  519. level = newline.level + 1
  520. FROM report_trial_balance_account as newline
  521. WHERE newline.account_group_id = report_trial_balance_account.parent_id
  522. AND report_trial_balance_account.report_id = newline.report_id
  523. AND report_trial_balance_account.account_id is not null
  524. AND report_trial_balance_account.report_id = %s"""
  525. query_update_account_params = (self.id,)
  526. self.env.cr.execute(query_update_account_group,
  527. query_update_account_params)
  528. def _compute_group_accounts(self):
  529. groups = self.account_ids.filtered(
  530. lambda a: a.account_group_id is not False)
  531. for group in groups:
  532. if self.hierarchy_on == 'computed':
  533. group.compute_account_ids = \
  534. group.account_group_id.compute_account_ids
  535. else:
  536. if group.child_account_ids:
  537. chacc = group.child_account_ids.replace(
  538. '}', '').replace('{', '').split(',')
  539. if 'NULL' in chacc:
  540. chacc.remove('NULL')
  541. if chacc:
  542. group.compute_account_ids = [
  543. (6, 0, [int(g) for g in chacc])]