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.

578 lines
20 KiB

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