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.

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