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.

600 lines
18 KiB

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