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.

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