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.invalidate_cache()
  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. DATE %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. )
  281. INSERT INTO
  282. report_aged_partner_balance_line
  283. (
  284. report_partner_id,
  285. create_uid,
  286. create_date,
  287. partner,
  288. amount_residual,
  289. current,
  290. age_30_days,
  291. age_60_days,
  292. age_90_days,
  293. age_120_days,
  294. older
  295. )
  296. SELECT
  297. rp.id AS report_partner_id,
  298. %s AS create_uid,
  299. NOW() AS create_date,
  300. rp.name,
  301. SUM(rlo.amount_residual) AS amount_residual,
  302. SUM(
  303. CASE
  304. WHEN rlo.date_due >= date_range.date_current
  305. THEN rlo.amount_residual
  306. END
  307. ) AS current,
  308. SUM(
  309. CASE
  310. WHEN
  311. rlo.date_due >= date_range.date_less_30_days
  312. AND rlo.date_due < date_range.date_current
  313. THEN rlo.amount_residual
  314. END
  315. ) AS age_30_days,
  316. SUM(
  317. CASE
  318. WHEN
  319. rlo.date_due >= date_range.date_less_60_days
  320. AND rlo.date_due < date_range.date_less_30_days
  321. THEN rlo.amount_residual
  322. END
  323. ) AS age_60_days,
  324. SUM(
  325. CASE
  326. WHEN
  327. rlo.date_due >= date_range.date_less_90_days
  328. AND rlo.date_due < date_range.date_less_60_days
  329. THEN rlo.amount_residual
  330. END
  331. ) AS age_90_days,
  332. SUM(
  333. CASE
  334. WHEN
  335. rlo.date_due >= date_range.date_less_120_days
  336. AND rlo.date_due < date_range.date_less_90_days
  337. THEN rlo.amount_residual
  338. END
  339. ) AS age_120_days,
  340. SUM(
  341. CASE
  342. WHEN rlo.date_due < date_range.date_less_120_days
  343. THEN rlo.amount_residual
  344. END
  345. ) AS older
  346. FROM
  347. date_range,
  348. report_open_items_move_line rlo
  349. INNER JOIN
  350. report_open_items_partner rpo ON rlo.report_partner_id = rpo.id
  351. INNER JOIN
  352. report_open_items_account rao ON rpo.report_account_id = rao.id
  353. INNER JOIN
  354. report_aged_partner_balance_account ra ON rao.code = ra.code
  355. INNER JOIN
  356. report_aged_partner_balance_partner rp
  357. ON
  358. ra.id = rp.report_account_id
  359. """
  360. if not only_empty_partner_line:
  361. query_inject_line += """
  362. AND rpo.partner_id = rp.partner_id
  363. """
  364. elif only_empty_partner_line:
  365. query_inject_line += """
  366. AND rpo.partner_id IS NULL
  367. AND rp.partner_id IS NULL
  368. """
  369. query_inject_line += """
  370. WHERE
  371. rao.report_id = %s
  372. AND ra.report_id = %s
  373. GROUP BY
  374. rp.id
  375. """
  376. query_inject_line_params = (self.date_at,) * 5
  377. query_inject_line_params += (
  378. self.env.uid,
  379. self.open_items_id.id,
  380. self.id,
  381. )
  382. self.env.cr.execute(query_inject_line, query_inject_line_params)
  383. def _inject_move_line_values(self, only_empty_partner_line=False):
  384. """ Inject report values for report_aged_partner_balance_move_line
  385. The "only_empty_partner_line" value is used
  386. to compute data without partner.
  387. """
  388. query_inject_move_line = """
  389. WITH
  390. date_range AS
  391. (
  392. SELECT
  393. DATE %s AS date_current,
  394. DATE %s - INTEGER '30' AS date_less_30_days,
  395. DATE %s - INTEGER '60' AS date_less_60_days,
  396. DATE %s - INTEGER '90' AS date_less_90_days,
  397. DATE %s - INTEGER '120' AS date_less_120_days
  398. )
  399. INSERT INTO
  400. report_aged_partner_balance_move_line
  401. (
  402. report_partner_id,
  403. create_uid,
  404. create_date,
  405. move_line_id,
  406. date,
  407. date_due,
  408. entry,
  409. journal,
  410. account,
  411. partner,
  412. label,
  413. amount_residual,
  414. current,
  415. age_30_days,
  416. age_60_days,
  417. age_90_days,
  418. age_120_days,
  419. older
  420. )
  421. SELECT
  422. rp.id AS report_partner_id,
  423. %s AS create_uid,
  424. NOW() AS create_date,
  425. rlo.move_line_id,
  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_current
  436. THEN rlo.amount_residual
  437. END AS current,
  438. CASE
  439. WHEN
  440. rlo.date_due >= date_range.date_less_30_days
  441. AND rlo.date_due < date_range.date_current
  442. THEN rlo.amount_residual
  443. END AS age_30_days,
  444. CASE
  445. WHEN
  446. rlo.date_due >= date_range.date_less_60_days
  447. AND rlo.date_due < date_range.date_less_30_days
  448. THEN rlo.amount_residual
  449. END AS age_60_days,
  450. CASE
  451. WHEN
  452. rlo.date_due >= date_range.date_less_90_days
  453. AND rlo.date_due < date_range.date_less_60_days
  454. THEN rlo.amount_residual
  455. END AS age_90_days,
  456. CASE
  457. WHEN
  458. rlo.date_due >= date_range.date_less_120_days
  459. AND rlo.date_due < date_range.date_less_90_days
  460. THEN rlo.amount_residual
  461. END AS age_120_days,
  462. CASE
  463. WHEN rlo.date_due < date_range.date_less_120_days
  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,) * 5
  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)