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.

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