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.

628 lines
19 KiB

  1. # © 2016 Julien Coux (Camptocamp)
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  3. from odoo import models, fields, api
  4. class AgedPartnerBalanceReport(models.TransientModel):
  5. """ Here, we just define class fields.
  6. For methods, go more bottom at this file.
  7. The class hierarchy is :
  8. * AgedPartnerBalanceReport
  9. ** AgedPartnerBalanceReportAccount
  10. *** AgedPartnerBalanceReportPartner
  11. **** AgedPartnerBalanceReportLine
  12. **** AgedPartnerBalanceReportMoveLine
  13. If "show_move_line_details" is selected
  14. """
  15. _name = 'report_aged_partner_balance'
  16. # Filters fields, used for data computation
  17. date_at = fields.Date()
  18. only_posted_moves = fields.Boolean()
  19. company_id = fields.Many2one(comodel_name='res.company')
  20. filter_account_ids = fields.Many2many(comodel_name='account.account')
  21. filter_partner_ids = fields.Many2many(comodel_name='res.partner')
  22. show_move_line_details = fields.Boolean()
  23. # Open Items Report Data fields, used as base for compute the data reports
  24. open_items_id = fields.Many2one(comodel_name='report_open_items')
  25. # Data fields, used to browse report data
  26. account_ids = fields.One2many(
  27. comodel_name='report_aged_partner_balance_account',
  28. inverse_name='report_id'
  29. )
  30. class AgedPartnerBalanceReportAccount(models.TransientModel):
  31. _name = 'report_aged_partner_balance_account'
  32. _order = 'code ASC'
  33. report_id = fields.Many2one(
  34. comodel_name='report_aged_partner_balance',
  35. ondelete='cascade',
  36. index=True
  37. )
  38. # Data fields, used to keep link with real object
  39. account_id = fields.Many2one(
  40. 'account.account',
  41. index=True
  42. )
  43. # Data fields, used for report display
  44. code = fields.Char()
  45. name = fields.Char()
  46. cumul_amount_residual = fields.Float(digits=(16, 2))
  47. cumul_current = fields.Float(digits=(16, 2))
  48. cumul_age_30_days = fields.Float(digits=(16, 2))
  49. cumul_age_60_days = fields.Float(digits=(16, 2))
  50. cumul_age_90_days = fields.Float(digits=(16, 2))
  51. cumul_age_120_days = fields.Float(digits=(16, 2))
  52. cumul_older = fields.Float(digits=(16, 2))
  53. percent_current = fields.Float(digits=(16, 2))
  54. percent_age_30_days = fields.Float(digits=(16, 2))
  55. percent_age_60_days = fields.Float(digits=(16, 2))
  56. percent_age_90_days = fields.Float(digits=(16, 2))
  57. percent_age_120_days = fields.Float(digits=(16, 2))
  58. percent_older = fields.Float(digits=(16, 2))
  59. # Data fields, used to browse report data
  60. partner_ids = fields.One2many(
  61. comodel_name='report_aged_partner_balance_partner',
  62. inverse_name='report_account_id'
  63. )
  64. class AgedPartnerBalanceReportPartner(models.TransientModel):
  65. _name = 'report_aged_partner_balance_partner'
  66. report_account_id = fields.Many2one(
  67. comodel_name='report_aged_partner_balance_account',
  68. ondelete='cascade',
  69. index=True
  70. )
  71. # Data fields, used to keep link with real object
  72. partner_id = fields.Many2one(
  73. 'res.partner',
  74. index=True
  75. )
  76. # Data fields, used for report display
  77. name = fields.Char()
  78. # Data fields, used to browse report data
  79. move_line_ids = fields.One2many(
  80. comodel_name='report_aged_partner_balance_move_line',
  81. inverse_name='report_partner_id'
  82. )
  83. line_ids = fields.One2many(
  84. comodel_name='report_aged_partner_balance_line',
  85. inverse_name='report_partner_id'
  86. )
  87. @api.model
  88. def _generate_order_by(self, order_spec, query):
  89. """Custom order to display "No partner allocated" at last position."""
  90. return """
  91. ORDER BY
  92. CASE
  93. WHEN
  94. "report_aged_partner_balance_partner"."partner_id" IS NOT NULL
  95. THEN 0
  96. ELSE 1
  97. END,
  98. "report_aged_partner_balance_partner"."name"
  99. """
  100. class AgedPartnerBalanceReportLine(models.TransientModel):
  101. _name = 'report_aged_partner_balance_line'
  102. report_partner_id = fields.Many2one(
  103. comodel_name='report_aged_partner_balance_partner',
  104. ondelete='cascade',
  105. index=True
  106. )
  107. # Data fields, used for report display
  108. partner = fields.Char()
  109. amount_residual = fields.Float(digits=(16, 2))
  110. current = fields.Float(digits=(16, 2))
  111. age_30_days = fields.Float(digits=(16, 2))
  112. age_60_days = fields.Float(digits=(16, 2))
  113. age_90_days = fields.Float(digits=(16, 2))
  114. age_120_days = fields.Float(digits=(16, 2))
  115. older = fields.Float(digits=(16, 2))
  116. class AgedPartnerBalanceReportMoveLine(models.TransientModel):
  117. _name = 'report_aged_partner_balance_move_line'
  118. report_partner_id = fields.Many2one(
  119. comodel_name='report_aged_partner_balance_partner',
  120. ondelete='cascade',
  121. index=True
  122. )
  123. # Data fields, used to keep link with real object
  124. move_line_id = fields.Many2one('account.move.line')
  125. # Data fields, used for report display
  126. date = fields.Date()
  127. date_due = fields.Date()
  128. entry = fields.Char()
  129. journal = fields.Char()
  130. account = fields.Char()
  131. partner = fields.Char()
  132. label = fields.Char()
  133. amount_residual = fields.Float(digits=(16, 2))
  134. current = fields.Float(digits=(16, 2))
  135. age_30_days = fields.Float(digits=(16, 2))
  136. age_60_days = fields.Float(digits=(16, 2))
  137. age_90_days = fields.Float(digits=(16, 2))
  138. age_120_days = fields.Float(digits=(16, 2))
  139. older = fields.Float(digits=(16, 2))
  140. class AgedPartnerBalanceReportCompute(models.TransientModel):
  141. """ Here, we just define methods.
  142. For class fields, go more top at this file.
  143. """
  144. _inherit = 'report_aged_partner_balance'
  145. @api.multi
  146. def print_report(self, report_type):
  147. self.ensure_one()
  148. if report_type == 'xlsx':
  149. report_name = 'a_f_r.report_aged_partner_balance_xlsx'
  150. else:
  151. report_name = 'account_financial_report.' \
  152. 'report_aged_partner_balance_qweb'
  153. report = self.env['ir.actions.report'].search(
  154. [('report_name', '=', report_name),
  155. ('report_type', '=', report_type)], limit=1)
  156. return report.report_action(self)
  157. def _get_html(self):
  158. result = {}
  159. rcontext = {}
  160. context = dict(self.env.context)
  161. report = self.browse(context.get('active_id'))
  162. if report:
  163. rcontext['o'] = report
  164. result['html'] = self.env.ref(
  165. 'account_financial_report.report_aged_partner_balance').render(
  166. rcontext)
  167. return result
  168. @api.model
  169. def get_html(self, given_context=None):
  170. return self._get_html()
  171. def _prepare_report_open_items(self):
  172. self.ensure_one()
  173. return {
  174. 'date_at': self.date_at,
  175. 'only_posted_moves': self.only_posted_moves,
  176. 'company_id': self.company_id.id,
  177. 'filter_account_ids': [(6, 0, self.filter_account_ids.ids)],
  178. 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
  179. }
  180. @api.multi
  181. def compute_data_for_report(self):
  182. self.ensure_one()
  183. # Compute Open Items Report Data.
  184. # The data of Aged Partner Balance Report
  185. # are based on Open Items Report data.
  186. model = self.env['report_open_items']
  187. self.open_items_id = model.create(self._prepare_report_open_items())
  188. self.open_items_id.compute_data_for_report()
  189. # Compute report data
  190. self._inject_account_values()
  191. self._inject_partner_values()
  192. self._inject_line_values()
  193. self._inject_line_values(only_empty_partner_line=True)
  194. if self.show_move_line_details:
  195. self._inject_move_line_values()
  196. self._inject_move_line_values(only_empty_partner_line=True)
  197. self._compute_accounts_cumul()
  198. # Refresh cache because all data are computed with SQL requests
  199. self.refresh()
  200. def _inject_account_values(self):
  201. """Inject report values for report_aged_partner_balance_account"""
  202. query_inject_account = """
  203. INSERT INTO
  204. report_aged_partner_balance_account
  205. (
  206. report_id,
  207. create_uid,
  208. create_date,
  209. account_id,
  210. code,
  211. name
  212. )
  213. SELECT
  214. %s AS report_id,
  215. %s AS create_uid,
  216. NOW() AS create_date,
  217. rao.account_id,
  218. rao.code,
  219. rao.name
  220. FROM
  221. report_open_items_account rao
  222. WHERE
  223. rao.report_id = %s
  224. """
  225. query_inject_account_params = (
  226. self.id,
  227. self.env.uid,
  228. self.open_items_id.id,
  229. )
  230. self.env.cr.execute(query_inject_account, query_inject_account_params)
  231. def _inject_partner_values(self):
  232. """Inject report values for report_aged_partner_balance_partner"""
  233. query_inject_partner = """
  234. INSERT INTO
  235. report_aged_partner_balance_partner
  236. (
  237. report_account_id,
  238. create_uid,
  239. create_date,
  240. partner_id,
  241. name
  242. )
  243. SELECT
  244. ra.id AS report_account_id,
  245. %s AS create_uid,
  246. NOW() AS create_date,
  247. rpo.partner_id,
  248. rpo.name
  249. FROM
  250. report_open_items_partner rpo
  251. INNER JOIN
  252. report_open_items_account rao ON rpo.report_account_id = rao.id
  253. INNER JOIN
  254. report_aged_partner_balance_account ra ON rao.code = ra.code
  255. WHERE
  256. rao.report_id = %s
  257. AND ra.report_id = %s
  258. """
  259. query_inject_partner_params = (
  260. self.env.uid,
  261. self.open_items_id.id,
  262. self.id,
  263. )
  264. self.env.cr.execute(query_inject_partner, query_inject_partner_params)
  265. def _inject_line_values(self, only_empty_partner_line=False):
  266. """ Inject report values for report_aged_partner_balance_line.
  267. The "only_empty_partner_line" value is used
  268. to compute data without partner.
  269. """
  270. query_inject_line = """
  271. WITH
  272. date_range AS
  273. (
  274. SELECT
  275. %s AS date_current,
  276. DATE %s - INTEGER '30' AS date_less_30_days,
  277. DATE %s - INTEGER '60' AS date_less_60_days,
  278. DATE %s - INTEGER '90' AS date_less_90_days,
  279. DATE %s - INTEGER '120' AS date_less_120_days,
  280. DATE %s - INTEGER '150' AS date_older
  281. )
  282. INSERT INTO
  283. report_aged_partner_balance_line
  284. (
  285. report_partner_id,
  286. create_uid,
  287. create_date,
  288. partner,
  289. amount_residual,
  290. current,
  291. age_30_days,
  292. age_60_days,
  293. age_90_days,
  294. age_120_days,
  295. older
  296. )
  297. SELECT
  298. rp.id AS report_partner_id,
  299. %s AS create_uid,
  300. NOW() AS create_date,
  301. rp.name,
  302. SUM(rlo.amount_residual) AS amount_residual,
  303. SUM(
  304. CASE
  305. WHEN rlo.date_due > date_range.date_less_30_days
  306. THEN rlo.amount_residual
  307. END
  308. ) AS current,
  309. SUM(
  310. CASE
  311. WHEN
  312. rlo.date_due > date_range.date_less_60_days
  313. AND rlo.date_due <= date_range.date_less_30_days
  314. THEN rlo.amount_residual
  315. END
  316. ) AS age_30_days,
  317. SUM(
  318. CASE
  319. WHEN
  320. rlo.date_due > date_range.date_less_90_days
  321. AND rlo.date_due <= date_range.date_less_60_days
  322. THEN rlo.amount_residual
  323. END
  324. ) AS age_60_days,
  325. SUM(
  326. CASE
  327. WHEN
  328. rlo.date_due > date_range.date_less_120_days
  329. AND rlo.date_due <= date_range.date_less_90_days
  330. THEN rlo.amount_residual
  331. END
  332. ) AS age_90_days,
  333. SUM(
  334. CASE
  335. WHEN
  336. rlo.date_due > date_range.date_older
  337. AND rlo.date_due <= date_range.date_less_120_days
  338. THEN rlo.amount_residual
  339. END
  340. ) AS age_120_days,
  341. SUM(
  342. CASE
  343. WHEN rlo.date_due <= date_range.date_older
  344. THEN rlo.amount_residual
  345. END
  346. ) AS older
  347. FROM
  348. date_range,
  349. report_open_items_move_line rlo
  350. INNER JOIN
  351. report_open_items_partner rpo ON rlo.report_partner_id = rpo.id
  352. INNER JOIN
  353. report_open_items_account rao ON rpo.report_account_id = rao.id
  354. INNER JOIN
  355. report_aged_partner_balance_account ra ON rao.code = ra.code
  356. INNER JOIN
  357. report_aged_partner_balance_partner rp
  358. ON
  359. ra.id = rp.report_account_id
  360. """
  361. if not only_empty_partner_line:
  362. query_inject_line += """
  363. AND rpo.partner_id = rp.partner_id
  364. """
  365. elif only_empty_partner_line:
  366. query_inject_line += """
  367. AND rpo.partner_id IS NULL
  368. AND rp.partner_id IS NULL
  369. """
  370. query_inject_line += """
  371. WHERE
  372. rao.report_id = %s
  373. AND ra.report_id = %s
  374. GROUP BY
  375. rp.id
  376. """
  377. query_inject_line_params = (self.date_at,) * 6
  378. query_inject_line_params += (
  379. self.env.uid,
  380. self.open_items_id.id,
  381. self.id,
  382. )
  383. self.env.cr.execute(query_inject_line, query_inject_line_params)
  384. def _inject_move_line_values(self, only_empty_partner_line=False):
  385. """ Inject report values for report_aged_partner_balance_move_line
  386. The "only_empty_partner_line" value is used
  387. to compute data without partner.
  388. """
  389. query_inject_move_line = """
  390. WITH
  391. date_range AS
  392. (
  393. SELECT
  394. %s AS date_current,
  395. DATE %s - INTEGER '30' AS date_less_30_days,
  396. DATE %s - INTEGER '60' AS date_less_60_days,
  397. DATE %s - INTEGER '90' AS date_less_90_days,
  398. DATE %s - INTEGER '120' AS date_less_120_days,
  399. DATE %s - INTEGER '150' AS date_older
  400. )
  401. INSERT INTO
  402. report_aged_partner_balance_move_line
  403. (
  404. report_partner_id,
  405. create_uid,
  406. create_date,
  407. date,
  408. date_due,
  409. entry,
  410. journal,
  411. account,
  412. partner,
  413. label,
  414. amount_residual,
  415. current,
  416. age_30_days,
  417. age_60_days,
  418. age_90_days,
  419. age_120_days,
  420. older
  421. )
  422. SELECT
  423. rp.id AS report_partner_id,
  424. %s AS create_uid,
  425. NOW() AS create_date,
  426. rlo.date,
  427. rlo.date_due,
  428. rlo.entry,
  429. rlo.journal,
  430. rlo.account,
  431. rlo.partner,
  432. rlo.label,
  433. rlo.amount_residual AS amount_residual,
  434. CASE
  435. WHEN rlo.date_due > date_range.date_less_30_days
  436. THEN rlo.amount_residual
  437. END AS current,
  438. CASE
  439. WHEN
  440. rlo.date_due > date_range.date_less_60_days
  441. AND rlo.date_due <= date_range.date_less_30_days
  442. THEN rlo.amount_residual
  443. END AS age_30_days,
  444. CASE
  445. WHEN
  446. rlo.date_due > date_range.date_less_90_days
  447. AND rlo.date_due <= date_range.date_less_60_days
  448. THEN rlo.amount_residual
  449. END AS age_60_days,
  450. CASE
  451. WHEN
  452. rlo.date_due > date_range.date_less_120_days
  453. AND rlo.date_due <= date_range.date_less_90_days
  454. THEN rlo.amount_residual
  455. END AS age_90_days,
  456. CASE
  457. WHEN
  458. rlo.date_due > date_range.date_older
  459. AND rlo.date_due <= date_range.date_less_120_days
  460. THEN rlo.amount_residual
  461. END AS age_120_days,
  462. CASE
  463. WHEN rlo.date_due <= date_range.date_older
  464. THEN rlo.amount_residual
  465. END AS older
  466. FROM
  467. date_range,
  468. report_open_items_move_line rlo
  469. INNER JOIN
  470. report_open_items_partner rpo ON rlo.report_partner_id = rpo.id
  471. INNER JOIN
  472. report_open_items_account rao ON rpo.report_account_id = rao.id
  473. INNER JOIN
  474. report_aged_partner_balance_account ra ON rao.code = ra.code
  475. INNER JOIN
  476. report_aged_partner_balance_partner rp
  477. ON
  478. ra.id = rp.report_account_id
  479. """
  480. if not only_empty_partner_line:
  481. query_inject_move_line += """
  482. AND rpo.partner_id = rp.partner_id
  483. """
  484. elif only_empty_partner_line:
  485. query_inject_move_line += """
  486. AND rpo.partner_id IS NULL
  487. AND rp.partner_id IS NULL
  488. """
  489. query_inject_move_line += """
  490. WHERE
  491. rao.report_id = %s
  492. AND ra.report_id = %s
  493. """
  494. query_inject_move_line_params = (self.date_at,) * 6
  495. query_inject_move_line_params += (
  496. self.env.uid,
  497. self.open_items_id.id,
  498. self.id,
  499. )
  500. self.env.cr.execute(query_inject_move_line,
  501. query_inject_move_line_params)
  502. def _compute_accounts_cumul(self):
  503. """ Compute cumulative amount for
  504. report_aged_partner_balance_account.
  505. """
  506. query_compute_accounts_cumul = """
  507. WITH
  508. cumuls AS
  509. (
  510. SELECT
  511. ra.id AS report_account_id,
  512. SUM(rl.amount_residual) AS cumul_amount_residual,
  513. SUM(rl.current) AS cumul_current,
  514. SUM(rl.age_30_days) AS cumul_age_30_days,
  515. SUM(rl.age_60_days) AS cumul_age_60_days,
  516. SUM(rl.age_90_days) AS cumul_age_90_days,
  517. SUM(rl.age_120_days) AS cumul_age_120_days,
  518. SUM(rl.older) AS cumul_older
  519. FROM
  520. report_aged_partner_balance_line rl
  521. INNER JOIN
  522. report_aged_partner_balance_partner rp
  523. ON rl.report_partner_id = rp.id
  524. INNER JOIN
  525. report_aged_partner_balance_account ra
  526. ON rp.report_account_id = ra.id
  527. WHERE
  528. ra.report_id = %s
  529. GROUP BY
  530. ra.id
  531. )
  532. UPDATE
  533. report_aged_partner_balance_account
  534. SET
  535. cumul_amount_residual = c.cumul_amount_residual,
  536. cumul_current = c.cumul_current,
  537. cumul_age_30_days = c.cumul_age_30_days,
  538. cumul_age_60_days = c.cumul_age_60_days,
  539. cumul_age_90_days = c.cumul_age_90_days,
  540. cumul_age_120_days = c.cumul_age_120_days,
  541. cumul_older = c.cumul_older,
  542. percent_current =
  543. CASE
  544. WHEN c.cumul_amount_residual != 0
  545. THEN 100 * c.cumul_current / c.cumul_amount_residual
  546. END,
  547. percent_age_30_days =
  548. CASE
  549. WHEN c.cumul_amount_residual != 0
  550. THEN 100 * c.cumul_age_30_days / c.cumul_amount_residual
  551. END,
  552. percent_age_60_days =
  553. CASE
  554. WHEN c.cumul_amount_residual != 0
  555. THEN 100 * c.cumul_age_60_days / c.cumul_amount_residual
  556. END,
  557. percent_age_90_days =
  558. CASE
  559. WHEN c.cumul_amount_residual != 0
  560. THEN 100 * c.cumul_age_90_days / c.cumul_amount_residual
  561. END,
  562. percent_age_120_days =
  563. CASE
  564. WHEN c.cumul_amount_residual != 0
  565. THEN 100 * c.cumul_age_120_days / c.cumul_amount_residual
  566. END,
  567. percent_older =
  568. CASE
  569. WHEN c.cumul_amount_residual != 0
  570. THEN 100 * c.cumul_older / c.cumul_amount_residual
  571. END
  572. FROM
  573. cumuls c
  574. WHERE
  575. id = c.report_account_id
  576. """
  577. params_compute_accounts_cumul = (self.id,)
  578. self.env.cr.execute(query_compute_accounts_cumul,
  579. params_compute_accounts_cumul)