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.

553 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. # 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. if self.hierarchy_on != 'none':
  200. self._inject_account_group_values()
  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. def _inject_partner_values(self):
  277. """Inject report values for report_trial_balance_partner"""
  278. query_inject_partner = """
  279. INSERT INTO
  280. report_trial_balance_partner
  281. (
  282. report_account_id,
  283. create_uid,
  284. create_date,
  285. partner_id,
  286. name,
  287. initial_balance,
  288. initial_balance_foreign_currency,
  289. debit,
  290. credit,
  291. period_balance,
  292. final_balance,
  293. final_balance_foreign_currency
  294. )
  295. SELECT
  296. ra.id AS report_account_id,
  297. %s AS create_uid,
  298. NOW() AS create_date,
  299. rpg.partner_id,
  300. rpg.name,
  301. rpg.initial_balance AS initial_balance,
  302. rpg.initial_balance_foreign_currency AS initial_balance_foreign_currency,
  303. rpg.final_debit - rpg.initial_debit AS debit,
  304. rpg.final_credit - rpg.initial_credit AS credit,
  305. rpg.final_balance - rpg.initial_balance AS period_balance,
  306. rpg.final_balance AS final_balance,
  307. rpg.final_balance_foreign_currency AS final_balance_foreign_currency
  308. FROM
  309. report_general_ledger_partner rpg
  310. INNER JOIN
  311. report_general_ledger_account rag ON rpg.report_account_id = rag.id
  312. INNER JOIN
  313. report_trial_balance_account ra ON rag.code = ra.code
  314. WHERE
  315. rag.report_id = %s
  316. AND ra.report_id = %s
  317. """
  318. query_inject_partner_params = (
  319. self.env.uid,
  320. self.general_ledger_id.id,
  321. self.id,
  322. )
  323. self.env.cr.execute(query_inject_partner, query_inject_partner_params)
  324. def _inject_account_group_values(self):
  325. """Inject report values for report_trial_balance_account"""
  326. query_inject_account_group = """
  327. INSERT INTO
  328. report_trial_balance_account
  329. (
  330. report_id,
  331. create_uid,
  332. create_date,
  333. account_group_id,
  334. parent_id,
  335. code,
  336. name,
  337. sequence,
  338. level
  339. )
  340. SELECT
  341. %s AS report_id,
  342. %s AS create_uid,
  343. NOW() AS create_date,
  344. accgroup.id,
  345. accgroup.parent_id,
  346. coalesce(accgroup.code_prefix, accgroup.name),
  347. accgroup.name,
  348. accgroup.parent_left * 100000,
  349. accgroup.level
  350. FROM
  351. account_group accgroup"""
  352. query_inject_account_params = (
  353. self.id,
  354. self.env.uid,
  355. )
  356. self.env.cr.execute(query_inject_account_group,
  357. query_inject_account_params)
  358. def _update_account_group_child_values(self):
  359. """Compute values for report_trial_balance_account group in child."""
  360. query_update_account_group = """
  361. WITH computed AS (WITH RECURSIVE cte AS (
  362. SELECT account_group_id, code, account_group_id AS parent_id,
  363. initial_balance, initial_balance_foreign_currency, debit, credit,
  364. final_balance, final_balance_foreign_currency
  365. FROM report_trial_balance_account
  366. WHERE report_id = %s
  367. GROUP BY report_trial_balance_account.id
  368. UNION ALL
  369. SELECT c.account_group_id, c.code, p.account_group_id,
  370. p.initial_balance, p.initial_balance_foreign_currency, p.debit, p.credit,
  371. p.final_balance, p.final_balance_foreign_currency
  372. FROM cte c
  373. JOIN report_trial_balance_account p USING (parent_id)
  374. WHERE p.report_id = %s
  375. )
  376. SELECT account_group_id, code,
  377. sum(initial_balance) AS initial_balance,
  378. sum(initial_balance_foreign_currency) AS initial_balance_foreign_currency,
  379. sum(debit) AS debit,
  380. sum(credit) AS credit,
  381. sum(final_balance) AS final_balance,
  382. sum(final_balance_foreign_currency) AS final_balance_foreign_currency
  383. FROM cte
  384. GROUP BY cte.account_group_id, cte.code
  385. ORDER BY account_group_id
  386. )
  387. UPDATE report_trial_balance_account
  388. SET initial_balance = computed.initial_balance,
  389. initial_balance_foreign_currency =
  390. computed.initial_balance_foreign_currency,
  391. debit = computed.debit,
  392. credit = computed.credit,
  393. final_balance = computed.final_balance,
  394. final_balance_foreign_currency =
  395. computed.final_balance_foreign_currency
  396. FROM computed
  397. WHERE report_trial_balance_account.account_group_id = computed.account_group_id
  398. AND report_trial_balance_account.report_id = %s
  399. """
  400. query_update_account_params = (self.id, self.id, self.id,)
  401. self.env.cr.execute(query_update_account_group,
  402. query_update_account_params)
  403. def _add_account_group_account_values(self):
  404. """Compute values for report_trial_balance_account group in child."""
  405. query_update_account_group = """
  406. DROP AGGREGATE IF EXISTS array_concat_agg(anyarray);
  407. CREATE AGGREGATE array_concat_agg(anyarray) (
  408. SFUNC = array_cat,
  409. STYPE = anyarray
  410. );
  411. WITH aggr AS(WITH computed AS (WITH RECURSIVE cte AS (
  412. SELECT account_group_id, account_group_id AS parent_id,
  413. ARRAY[account_id]::int[] as child_account_ids
  414. FROM report_trial_balance_account
  415. WHERE report_id = %s
  416. GROUP BY report_trial_balance_account.id
  417. UNION ALL
  418. SELECT c.account_group_id, p.account_group_id, ARRAY[p.account_id]::int[]
  419. FROM cte c
  420. JOIN report_trial_balance_account p USING (parent_id)
  421. WHERE p.report_id = %s
  422. )
  423. SELECT account_group_id,
  424. array_concat_agg(DISTINCT child_account_ids)::int[] as child_account_ids
  425. FROM cte
  426. GROUP BY cte.account_group_id, cte.child_account_ids
  427. ORDER BY account_group_id
  428. )
  429. SELECT account_group_id,
  430. array_concat_agg(DISTINCT child_account_ids)::int[]
  431. AS child_account_ids from computed
  432. GROUP BY account_group_id)
  433. UPDATE report_trial_balance_account
  434. SET child_account_ids = aggr.child_account_ids
  435. FROM aggr
  436. WHERE report_trial_balance_account.account_group_id = aggr.account_group_id
  437. AND report_trial_balance_account.report_id = %s
  438. """
  439. query_update_account_params = (self.id, self.id, self.id,)
  440. self.env.cr.execute(query_update_account_group,
  441. query_update_account_params)
  442. def _update_account_group_computed_values(self):
  443. """Compute values for report_trial_balance_account group in compute."""
  444. query_update_account_group = """
  445. WITH RECURSIVE accgroup AS
  446. (SELECT
  447. accgroup.id,
  448. sum(coalesce(ra.initial_balance, 0)) as initial_balance,
  449. sum(coalesce(ra.initial_balance_foreign_currency, 0))
  450. as initial_balance_foreign_currency,
  451. sum(coalesce(ra.debit, 0)) as debit,
  452. sum(coalesce(ra.credit, 0)) as credit,
  453. sum(coalesce(ra.final_balance, 0)) as final_balance,
  454. sum(coalesce(ra.final_balance_foreign_currency, 0))
  455. as final_balance_foreign_currency
  456. FROM
  457. account_group accgroup
  458. LEFT OUTER JOIN account_account AS acc
  459. ON strpos(acc.code, accgroup.code_prefix) = 1
  460. LEFT OUTER JOIN report_trial_balance_account AS ra
  461. ON ra.account_id = acc.id
  462. WHERE ra.report_id = %s
  463. GROUP BY accgroup.id
  464. )
  465. UPDATE report_trial_balance_account
  466. SET initial_balance = accgroup.initial_balance,
  467. initial_balance_foreign_currency =
  468. accgroup.initial_balance_foreign_currency,
  469. debit = accgroup.debit,
  470. credit = accgroup.credit,
  471. final_balance = accgroup.final_balance,
  472. final_balance_foreign_currency =
  473. accgroup.final_balance_foreign_currency
  474. FROM accgroup
  475. WHERE report_trial_balance_account.account_group_id = accgroup.id
  476. """
  477. query_update_account_params = (self.id,)
  478. self.env.cr.execute(query_update_account_group,
  479. query_update_account_params)
  480. def _update_account_sequence(self):
  481. """Compute sequence, level for report_trial_balance_account account."""
  482. query_update_account_group = """
  483. UPDATE report_trial_balance_account
  484. SET sequence = newline.sequence + 1,
  485. level = newline.level + 1
  486. FROM report_trial_balance_account as newline
  487. WHERE newline.account_group_id = report_trial_balance_account.parent_id
  488. AND report_trial_balance_account.report_id = newline.report_id
  489. AND report_trial_balance_account.account_id is not null
  490. AND report_trial_balance_account.report_id = %s"""
  491. query_update_account_params = (self.id,)
  492. self.env.cr.execute(query_update_account_group,
  493. query_update_account_params)
  494. def _compute_group_accounts(self):
  495. groups = self.account_ids.filtered(
  496. lambda a: a.account_group_id is not False)
  497. for group in groups:
  498. if self.hierarchy_on == 'compute':
  499. group.compute_account_ids = \
  500. group.account_group_id.compute_account_ids
  501. else:
  502. if group.child_account_ids:
  503. chacc = group.child_account_ids.replace(
  504. '}', '').replace('{', '').split(',')
  505. if 'NULL' in chacc:
  506. chacc.remove('NULL')
  507. if chacc:
  508. group.compute_account_ids = [
  509. (6, 0, [int(g) for g in chacc])]