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.

565 lines
19 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. 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'
  48. )
  49. # Data fields, used to browse report data
  50. account_ids = fields.One2many(
  51. comodel_name='report_trial_balance_account',
  52. inverse_name='report_id'
  53. )
  54. class TrialBalanceReportAccount(models.TransientModel):
  55. _name = 'report_trial_balance_account'
  56. _inherit = 'account_financial_report_abstract'
  57. _order = 'sequence, code ASC, name'
  58. report_id = fields.Many2one(
  59. comodel_name='report_trial_balance',
  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. currency_id = fields.Many2one('res.currency')
  89. initial_balance = fields.Float(digits=(16, 2))
  90. initial_balance_foreign_currency = fields.Float(digits=(16, 2))
  91. debit = fields.Float(digits=(16, 2))
  92. credit = fields.Float(digits=(16, 2))
  93. period_balance = fields.Float(digits=(16, 2))
  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_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_partner'
  117. _inherit = 'account_financial_report_abstract'
  118. report_account_id = fields.Many2one(
  119. comodel_name='report_trial_balance_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. currency_id = fields.Many2one('res.currency')
  131. initial_balance = fields.Float(digits=(16, 2))
  132. initial_balance_foreign_currency = fields.Float(digits=(16, 2))
  133. debit = fields.Float(digits=(16, 2))
  134. credit = fields.Float(digits=(16, 2))
  135. period_balance = fields.Float(digits=(16, 2))
  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_partner"."partner_id" IS NOT NULL
  145. THEN 0
  146. ELSE 1
  147. END,
  148. "report_trial_balance_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'
  155. @api.multi
  156. def print_report(self, report_type):
  157. self.ensure_one()
  158. if report_type == 'xlsx':
  159. report_name = 'a_f_r.report_trial_balance_xlsx'
  160. else:
  161. report_name = 'account_financial_report.' \
  162. 'report_trial_balance_qweb'
  163. return self.env['ir.actions.report'].search(
  164. [('report_name', '=', report_name),
  165. ('report_type', '=', report_type)], limit=1).report_action(self)
  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.report_trial_balance').render(
  175. 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. 'hide_account_at_0': self.hide_account_at_0,
  187. 'foreign_currency': self.foreign_currency,
  188. 'company_id': self.company_id.id,
  189. 'filter_account_ids': [(6, 0, account_ids.ids)],
  190. 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
  191. 'filter_journal_ids': [(6, 0, self.filter_journal_ids.ids)],
  192. 'fy_start_date': self.fy_start_date,
  193. }
  194. @api.multi
  195. def compute_data_for_report(self):
  196. self.ensure_one()
  197. # Compute General Ledger Report Data.
  198. # The data of Trial Balance Report
  199. # are based on General Ledger Report data.
  200. model = self.env['report_general_ledger']
  201. if self.filter_account_ids:
  202. account_ids = self.filter_account_ids
  203. else:
  204. account_ids = self.env['account.account'].search(
  205. [('company_id', '=', self.company_id.id)])
  206. self.general_ledger_id = model.create(
  207. self._prepare_report_general_ledger(account_ids)
  208. )
  209. self.general_ledger_id.compute_data_for_report(
  210. with_line_details=False, with_partners=self.show_partner_details
  211. )
  212. # Compute report data
  213. self._inject_account_values(account_ids)
  214. if self.show_partner_details:
  215. self._inject_partner_values()
  216. if not self.filter_account_ids:
  217. if self.hierarchy_on != 'none':
  218. self._inject_account_group_values()
  219. if self.hierarchy_on == 'computed':
  220. self._update_account_group_computed_values()
  221. else:
  222. self._update_account_group_child_values()
  223. self._update_account_sequence()
  224. self._add_account_group_account_values()
  225. self.refresh()
  226. if not self.filter_account_ids and self.hierarchy_on != 'none':
  227. self._compute_group_accounts()
  228. else:
  229. for line in self.account_ids:
  230. line.write({'level': 0})
  231. def _inject_account_values(self, account_ids):
  232. """Inject report values for report_trial_balance_account"""
  233. query_inject_account = """
  234. INSERT INTO
  235. report_trial_balance_account
  236. (
  237. report_id,
  238. create_uid,
  239. create_date,
  240. account_id,
  241. parent_id,
  242. code,
  243. name,
  244. initial_balance,
  245. debit,
  246. credit,
  247. period_balance,
  248. final_balance,
  249. currency_id,
  250. initial_balance_foreign_currency,
  251. final_balance_foreign_currency
  252. )
  253. SELECT
  254. %s AS report_id,
  255. %s AS create_uid,
  256. NOW() AS create_date,
  257. acc.id,
  258. acc.group_id,
  259. acc.code,
  260. acc.name,
  261. coalesce(rag.initial_balance, 0) AS initial_balance,
  262. coalesce(rag.final_debit - rag.initial_debit, 0) AS debit,
  263. coalesce(rag.final_credit - rag.initial_credit, 0) AS credit,
  264. coalesce(rag.final_balance - rag.initial_balance, 0) AS period_balance,
  265. coalesce(rag.final_balance, 0) AS final_balance,
  266. rag.currency_id AS currency_id,
  267. coalesce(rag.initial_balance_foreign_currency, 0)
  268. AS initial_balance_foreign_currency,
  269. coalesce(rag.final_balance_foreign_currency, 0)
  270. AS final_balance_foreign_currency
  271. FROM
  272. account_account acc
  273. LEFT OUTER JOIN report_general_ledger_account AS rag
  274. ON rag.account_id = acc.id AND rag.report_id = %s
  275. WHERE
  276. acc.id in %s
  277. """
  278. query_inject_account_params = (
  279. self.id,
  280. self.env.uid,
  281. self.general_ledger_id.id,
  282. account_ids._ids,
  283. )
  284. self.env.cr.execute(query_inject_account, query_inject_account_params)
  285. def _inject_partner_values(self):
  286. """Inject report values for report_trial_balance_partner"""
  287. query_inject_partner = """
  288. INSERT INTO
  289. report_trial_balance_partner
  290. (
  291. report_account_id,
  292. create_uid,
  293. create_date,
  294. partner_id,
  295. name,
  296. initial_balance,
  297. initial_balance_foreign_currency,
  298. debit,
  299. credit,
  300. period_balance,
  301. final_balance,
  302. final_balance_foreign_currency
  303. )
  304. SELECT
  305. ra.id AS report_account_id,
  306. %s AS create_uid,
  307. NOW() AS create_date,
  308. rpg.partner_id,
  309. rpg.name,
  310. rpg.initial_balance AS initial_balance,
  311. rpg.initial_balance_foreign_currency AS initial_balance_foreign_currency,
  312. rpg.final_debit - rpg.initial_debit AS debit,
  313. rpg.final_credit - rpg.initial_credit AS credit,
  314. rpg.final_balance - rpg.initial_balance AS period_balance,
  315. rpg.final_balance AS final_balance,
  316. rpg.final_balance_foreign_currency AS final_balance_foreign_currency
  317. FROM
  318. report_general_ledger_partner rpg
  319. INNER JOIN
  320. report_general_ledger_account rag ON rpg.report_account_id = rag.id
  321. INNER JOIN
  322. report_trial_balance_account ra ON rag.code = ra.code
  323. WHERE
  324. rag.report_id = %s
  325. AND ra.report_id = %s
  326. """
  327. query_inject_partner_params = (
  328. self.env.uid,
  329. self.general_ledger_id.id,
  330. self.id,
  331. )
  332. self.env.cr.execute(query_inject_partner, query_inject_partner_params)
  333. def _inject_account_group_values(self):
  334. """Inject report values for report_trial_balance_account"""
  335. query_inject_account_group = """
  336. INSERT INTO
  337. report_trial_balance_account
  338. (
  339. report_id,
  340. create_uid,
  341. create_date,
  342. account_group_id,
  343. parent_id,
  344. code,
  345. name,
  346. sequence,
  347. level
  348. )
  349. SELECT
  350. %s AS report_id,
  351. %s AS create_uid,
  352. NOW() AS create_date,
  353. accgroup.id,
  354. accgroup.parent_id,
  355. coalesce(accgroup.code_prefix, accgroup.name),
  356. accgroup.name,
  357. accgroup.parent_left * 100000,
  358. accgroup.level
  359. FROM
  360. account_group accgroup"""
  361. query_inject_account_params = (
  362. self.id,
  363. self.env.uid,
  364. )
  365. self.env.cr.execute(query_inject_account_group,
  366. query_inject_account_params)
  367. def _update_account_group_child_values(self):
  368. """Compute values for report_trial_balance_account group in child."""
  369. query_update_account_group = """
  370. WITH computed AS (WITH RECURSIVE cte AS (
  371. SELECT account_group_id, code, account_group_id AS parent_id,
  372. initial_balance, initial_balance_foreign_currency, debit, credit,
  373. period_balance, final_balance, final_balance_foreign_currency
  374. FROM report_trial_balance_account
  375. WHERE report_id = %s
  376. GROUP BY report_trial_balance_account.id
  377. UNION ALL
  378. SELECT c.account_group_id, c.code, p.account_group_id,
  379. p.initial_balance, p.initial_balance_foreign_currency, p.debit, p.credit,
  380. p.period_balance, p.final_balance, p.final_balance_foreign_currency
  381. FROM cte c
  382. JOIN report_trial_balance_account p USING (parent_id)
  383. WHERE p.report_id = %s
  384. )
  385. SELECT account_group_id, code,
  386. sum(initial_balance) AS initial_balance,
  387. sum(initial_balance_foreign_currency) AS initial_balance_foreign_currency,
  388. sum(debit) AS debit,
  389. sum(credit) AS credit,
  390. sum(debit) - sum(credit) AS period_balance,
  391. sum(final_balance) AS final_balance,
  392. sum(final_balance_foreign_currency) AS final_balance_foreign_currency
  393. FROM cte
  394. GROUP BY cte.account_group_id, cte.code
  395. ORDER BY account_group_id
  396. )
  397. UPDATE report_trial_balance_account
  398. SET initial_balance = computed.initial_balance,
  399. initial_balance_foreign_currency =
  400. computed.initial_balance_foreign_currency,
  401. debit = computed.debit,
  402. credit = computed.credit,
  403. period_balance = computed.period_balance,
  404. final_balance = computed.final_balance,
  405. final_balance_foreign_currency =
  406. computed.final_balance_foreign_currency
  407. FROM computed
  408. WHERE report_trial_balance_account.account_group_id = computed.account_group_id
  409. AND report_trial_balance_account.report_id = %s
  410. """
  411. query_update_account_params = (self.id, self.id, self.id,)
  412. self.env.cr.execute(query_update_account_group,
  413. query_update_account_params)
  414. def _add_account_group_account_values(self):
  415. """Compute values for report_trial_balance_account group in child."""
  416. query_update_account_group = """
  417. DROP AGGREGATE IF EXISTS array_concat_agg(anyarray);
  418. CREATE AGGREGATE array_concat_agg(anyarray) (
  419. SFUNC = array_cat,
  420. STYPE = anyarray
  421. );
  422. WITH aggr AS(WITH computed AS (WITH RECURSIVE cte AS (
  423. SELECT account_group_id, account_group_id AS parent_id,
  424. ARRAY[account_id]::int[] as child_account_ids
  425. FROM report_trial_balance_account
  426. WHERE report_id = %s
  427. GROUP BY report_trial_balance_account.id
  428. UNION ALL
  429. SELECT c.account_group_id, p.account_group_id, ARRAY[p.account_id]::int[]
  430. FROM cte c
  431. JOIN report_trial_balance_account p USING (parent_id)
  432. WHERE p.report_id = %s
  433. )
  434. SELECT account_group_id,
  435. array_concat_agg(DISTINCT child_account_ids)::int[] as child_account_ids
  436. FROM cte
  437. GROUP BY cte.account_group_id, cte.child_account_ids
  438. ORDER BY account_group_id
  439. )
  440. SELECT account_group_id,
  441. array_concat_agg(DISTINCT child_account_ids)::int[]
  442. AS child_account_ids from computed
  443. GROUP BY account_group_id)
  444. UPDATE report_trial_balance_account
  445. SET child_account_ids = aggr.child_account_ids
  446. FROM aggr
  447. WHERE report_trial_balance_account.account_group_id = aggr.account_group_id
  448. AND report_trial_balance_account.report_id = %s
  449. """
  450. query_update_account_params = (self.id, self.id, self.id,)
  451. self.env.cr.execute(query_update_account_group,
  452. query_update_account_params)
  453. def _update_account_group_computed_values(self):
  454. """Compute values for report_trial_balance_account group in compute."""
  455. query_update_account_group = """
  456. WITH RECURSIVE accgroup AS
  457. (SELECT
  458. accgroup.id,
  459. sum(coalesce(ra.initial_balance, 0)) as initial_balance,
  460. sum(coalesce(ra.initial_balance_foreign_currency, 0))
  461. as initial_balance_foreign_currency,
  462. sum(coalesce(ra.debit, 0)) as debit,
  463. sum(coalesce(ra.credit, 0)) as credit,
  464. sum(coalesce(ra.debit, 0)) - sum(coalesce(ra.credit, 0)) as period_balance,
  465. sum(coalesce(ra.final_balance, 0)) as final_balance,
  466. sum(coalesce(ra.final_balance_foreign_currency, 0))
  467. as final_balance_foreign_currency
  468. FROM
  469. account_group accgroup
  470. LEFT OUTER JOIN account_account AS acc
  471. ON strpos(acc.code, accgroup.code_prefix) = 1
  472. LEFT OUTER JOIN report_trial_balance_account AS ra
  473. ON ra.account_id = acc.id
  474. WHERE ra.report_id = %s
  475. GROUP BY accgroup.id
  476. )
  477. UPDATE report_trial_balance_account
  478. SET initial_balance = accgroup.initial_balance,
  479. initial_balance_foreign_currency =
  480. accgroup.initial_balance_foreign_currency,
  481. debit = accgroup.debit,
  482. credit = accgroup.credit,
  483. period_balance = accgroup.period_balance,
  484. final_balance = accgroup.final_balance,
  485. final_balance_foreign_currency =
  486. accgroup.final_balance_foreign_currency
  487. FROM accgroup
  488. WHERE report_trial_balance_account.account_group_id = accgroup.id
  489. """
  490. query_update_account_params = (self.id,)
  491. self.env.cr.execute(query_update_account_group,
  492. query_update_account_params)
  493. def _update_account_sequence(self):
  494. """Compute sequence, level for report_trial_balance_account account."""
  495. query_update_account_group = """
  496. UPDATE report_trial_balance_account
  497. SET sequence = newline.sequence + 1,
  498. level = newline.level + 1
  499. FROM report_trial_balance_account as newline
  500. WHERE newline.account_group_id = report_trial_balance_account.parent_id
  501. AND report_trial_balance_account.report_id = newline.report_id
  502. AND report_trial_balance_account.account_id is not null
  503. AND report_trial_balance_account.report_id = %s"""
  504. query_update_account_params = (self.id,)
  505. self.env.cr.execute(query_update_account_group,
  506. query_update_account_params)
  507. def _compute_group_accounts(self):
  508. groups = self.account_ids.filtered(
  509. lambda a: a.account_group_id is not False)
  510. for group in groups:
  511. if self.hierarchy_on == 'compute':
  512. group.compute_account_ids = \
  513. group.account_group_id.compute_account_ids
  514. else:
  515. if group.child_account_ids:
  516. chacc = group.child_account_ids.replace(
  517. '}', '').replace('{', '').split(',')
  518. if 'NULL' in chacc:
  519. chacc.remove('NULL')
  520. if chacc:
  521. group.compute_account_ids = [
  522. (6, 0, [int(g) for g in chacc])]