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.

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