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.

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