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.

617 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. ** AgedPartnerBalanceReportAccount
  11. *** AgedPartnerBalanceReportPartner
  12. **** AgedPartnerBalanceReportLine
  13. **** AgedPartnerBalanceReportMoveLine
  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 AgedPartnerBalanceReportAccount(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 AgedPartnerBalanceReportPartner(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 AgedPartnerBalanceReportLine(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 AgedPartnerBalanceReportMoveLine(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, xlsx_report=False):
  148. self.ensure_one()
  149. self.compute_data_for_report()
  150. if xlsx_report:
  151. report_name = 'account_financial_report_qweb.' \
  152. 'report_aged_partner_balance_xlsx'
  153. else:
  154. report_name = 'account_financial_report_qweb.' \
  155. 'report_aged_partner_balance_qweb'
  156. return self.env['report'].get_action(records=self,
  157. report_name=report_name)
  158. def _prepare_report_open_items(self):
  159. self.ensure_one()
  160. return {
  161. 'date_at': self.date_at,
  162. 'only_posted_moves': self.only_posted_moves,
  163. 'company_id': self.company_id.id,
  164. 'filter_account_ids': [(6, 0, self.filter_account_ids.ids)],
  165. 'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)],
  166. }
  167. @api.multi
  168. def compute_data_for_report(self):
  169. self.ensure_one()
  170. # Compute Open Items Report Data.
  171. # The data of Aged Partner Balance Report
  172. # are based on Open Items Report data.
  173. model = self.env['report_open_items_qweb']
  174. self.open_items_id = model.create(self._prepare_report_open_items())
  175. self.open_items_id.compute_data_for_report()
  176. # Compute report data
  177. self._inject_account_values()
  178. self._inject_partner_values()
  179. self._inject_line_values()
  180. self._inject_line_values(only_empty_partner_line=True)
  181. if self.show_move_line_details:
  182. self._inject_move_line_values()
  183. self._inject_move_line_values(only_empty_partner_line=True)
  184. self._compute_accounts_cumul()
  185. # Refresh cache because all data are computed with SQL requests
  186. self.refresh()
  187. def _inject_account_values(self):
  188. """Inject report values for report_aged_partner_balance_qweb_account"""
  189. query_inject_account = """
  190. INSERT INTO
  191. report_aged_partner_balance_qweb_account
  192. (
  193. report_id,
  194. create_uid,
  195. create_date,
  196. account_id,
  197. code,
  198. name
  199. )
  200. SELECT
  201. %s AS report_id,
  202. %s AS create_uid,
  203. NOW() AS create_date,
  204. rao.account_id,
  205. rao.code,
  206. rao.name
  207. FROM
  208. report_open_items_qweb_account rao
  209. WHERE
  210. rao.report_id = %s
  211. """
  212. query_inject_account_params = (
  213. self.id,
  214. self.env.uid,
  215. self.open_items_id.id,
  216. )
  217. self.env.cr.execute(query_inject_account, query_inject_account_params)
  218. def _inject_partner_values(self):
  219. """Inject report values for report_aged_partner_balance_qweb_partner"""
  220. query_inject_partner = """
  221. INSERT INTO
  222. report_aged_partner_balance_qweb_partner
  223. (
  224. report_account_id,
  225. create_uid,
  226. create_date,
  227. partner_id,
  228. name
  229. )
  230. SELECT
  231. ra.id AS report_account_id,
  232. %s AS create_uid,
  233. NOW() AS create_date,
  234. rpo.partner_id,
  235. rpo.name
  236. FROM
  237. report_open_items_qweb_partner rpo
  238. INNER JOIN
  239. report_open_items_qweb_account rao ON rpo.report_account_id = rao.id
  240. INNER JOIN
  241. report_aged_partner_balance_qweb_account ra ON rao.code = ra.code
  242. WHERE
  243. rao.report_id = %s
  244. AND ra.report_id = %s
  245. """
  246. query_inject_partner_params = (
  247. self.env.uid,
  248. self.open_items_id.id,
  249. self.id,
  250. )
  251. self.env.cr.execute(query_inject_partner, query_inject_partner_params)
  252. def _inject_line_values(self, only_empty_partner_line=False):
  253. """ Inject report values for report_aged_partner_balance_qweb_line.
  254. The "only_empty_partner_line" value is used
  255. to compute data without partner.
  256. """
  257. query_inject_line = """
  258. WITH
  259. date_range AS
  260. (
  261. SELECT
  262. %s AS date_current,
  263. DATE %s - INTEGER '30' AS date_less_30_days,
  264. DATE %s - INTEGER '60' AS date_less_60_days,
  265. DATE %s - INTEGER '90' AS date_less_90_days,
  266. DATE %s - INTEGER '120' AS date_less_120_days,
  267. DATE %s - INTEGER '150' AS date_older
  268. )
  269. INSERT INTO
  270. report_aged_partner_balance_qweb_line
  271. (
  272. report_partner_id,
  273. create_uid,
  274. create_date,
  275. partner,
  276. amount_residual,
  277. current,
  278. age_30_days,
  279. age_60_days,
  280. age_90_days,
  281. age_120_days,
  282. older
  283. )
  284. SELECT
  285. rp.id AS report_partner_id,
  286. %s AS create_uid,
  287. NOW() AS create_date,
  288. rp.name,
  289. SUM(rlo.amount_residual) AS amount_residual,
  290. SUM(
  291. CASE
  292. WHEN rlo.date_due > date_range.date_less_30_days
  293. THEN rlo.amount_residual
  294. END
  295. ) AS current,
  296. SUM(
  297. CASE
  298. WHEN
  299. rlo.date_due > date_range.date_less_60_days
  300. AND rlo.date_due <= date_range.date_less_30_days
  301. THEN rlo.amount_residual
  302. END
  303. ) AS age_30_days,
  304. SUM(
  305. CASE
  306. WHEN
  307. rlo.date_due > date_range.date_less_90_days
  308. AND rlo.date_due <= date_range.date_less_60_days
  309. THEN rlo.amount_residual
  310. END
  311. ) AS age_60_days,
  312. SUM(
  313. CASE
  314. WHEN
  315. rlo.date_due > date_range.date_less_120_days
  316. AND rlo.date_due <= date_range.date_less_90_days
  317. THEN rlo.amount_residual
  318. END
  319. ) AS age_90_days,
  320. SUM(
  321. CASE
  322. WHEN
  323. rlo.date_due > date_range.date_older
  324. AND rlo.date_due <= date_range.date_less_120_days
  325. THEN rlo.amount_residual
  326. END
  327. ) AS age_120_days,
  328. SUM(
  329. CASE
  330. WHEN rlo.date_due <= date_range.date_older
  331. THEN rlo.amount_residual
  332. END
  333. ) AS older
  334. FROM
  335. date_range,
  336. report_open_items_qweb_move_line rlo
  337. INNER JOIN
  338. report_open_items_qweb_partner rpo ON rlo.report_partner_id = rpo.id
  339. INNER JOIN
  340. report_open_items_qweb_account rao ON rpo.report_account_id = rao.id
  341. INNER JOIN
  342. report_aged_partner_balance_qweb_account ra ON rao.code = ra.code
  343. INNER JOIN
  344. report_aged_partner_balance_qweb_partner rp
  345. ON
  346. ra.id = rp.report_account_id
  347. """
  348. if not only_empty_partner_line:
  349. query_inject_line += """
  350. AND rpo.partner_id = rp.partner_id
  351. """
  352. elif only_empty_partner_line:
  353. query_inject_line += """
  354. AND rpo.partner_id IS NULL
  355. AND rp.partner_id IS NULL
  356. """
  357. query_inject_line += """
  358. WHERE
  359. rao.report_id = %s
  360. AND ra.report_id = %s
  361. GROUP BY
  362. rp.id
  363. """
  364. query_inject_line_params = (self.date_at,) * 6
  365. query_inject_line_params += (
  366. self.env.uid,
  367. self.open_items_id.id,
  368. self.id,
  369. )
  370. self.env.cr.execute(query_inject_line, query_inject_line_params)
  371. def _inject_move_line_values(self, only_empty_partner_line=False):
  372. """ Inject report values for report_aged_partner_balance_qweb_move_line
  373. The "only_empty_partner_line" value is used
  374. to compute data without partner.
  375. """
  376. query_inject_move_line = """
  377. WITH
  378. date_range AS
  379. (
  380. SELECT
  381. %s AS date_current,
  382. DATE %s - INTEGER '30' AS date_less_30_days,
  383. DATE %s - INTEGER '60' AS date_less_60_days,
  384. DATE %s - INTEGER '90' AS date_less_90_days,
  385. DATE %s - INTEGER '120' AS date_less_120_days,
  386. DATE %s - INTEGER '150' AS date_older
  387. )
  388. INSERT INTO
  389. report_aged_partner_balance_qweb_move_line
  390. (
  391. report_partner_id,
  392. create_uid,
  393. create_date,
  394. date,
  395. date_due,
  396. entry,
  397. journal,
  398. account,
  399. partner,
  400. label,
  401. amount_residual,
  402. current,
  403. age_30_days,
  404. age_60_days,
  405. age_90_days,
  406. age_120_days,
  407. older
  408. )
  409. SELECT
  410. rp.id AS report_partner_id,
  411. %s AS create_uid,
  412. NOW() AS create_date,
  413. rlo.date,
  414. rlo.date_due,
  415. rlo.entry,
  416. rlo.journal,
  417. rlo.account,
  418. rlo.partner,
  419. rlo.label,
  420. rlo.amount_residual AS amount_residual,
  421. CASE
  422. WHEN rlo.date_due > date_range.date_less_30_days
  423. THEN rlo.amount_residual
  424. END AS current,
  425. CASE
  426. WHEN
  427. rlo.date_due > date_range.date_less_60_days
  428. AND rlo.date_due <= date_range.date_less_30_days
  429. THEN rlo.amount_residual
  430. END AS age_30_days,
  431. CASE
  432. WHEN
  433. rlo.date_due > date_range.date_less_90_days
  434. AND rlo.date_due <= date_range.date_less_60_days
  435. THEN rlo.amount_residual
  436. END AS age_60_days,
  437. CASE
  438. WHEN
  439. rlo.date_due > date_range.date_less_120_days
  440. AND rlo.date_due <= date_range.date_less_90_days
  441. THEN rlo.amount_residual
  442. END AS age_90_days,
  443. CASE
  444. WHEN
  445. rlo.date_due > date_range.date_older
  446. AND rlo.date_due <= date_range.date_less_120_days
  447. THEN rlo.amount_residual
  448. END AS age_120_days,
  449. CASE
  450. WHEN rlo.date_due <= date_range.date_older
  451. THEN rlo.amount_residual
  452. END AS older
  453. FROM
  454. date_range,
  455. report_open_items_qweb_move_line rlo
  456. INNER JOIN
  457. report_open_items_qweb_partner rpo ON rlo.report_partner_id = rpo.id
  458. INNER JOIN
  459. report_open_items_qweb_account rao ON rpo.report_account_id = rao.id
  460. INNER JOIN
  461. report_aged_partner_balance_qweb_account ra ON rao.code = ra.code
  462. INNER JOIN
  463. report_aged_partner_balance_qweb_partner rp
  464. ON
  465. ra.id = rp.report_account_id
  466. """
  467. if not only_empty_partner_line:
  468. query_inject_move_line += """
  469. AND rpo.partner_id = rp.partner_id
  470. """
  471. elif only_empty_partner_line:
  472. query_inject_move_line += """
  473. AND rpo.partner_id IS NULL
  474. AND rp.partner_id IS NULL
  475. """
  476. query_inject_move_line += """
  477. WHERE
  478. rao.report_id = %s
  479. AND ra.report_id = %s
  480. """
  481. query_inject_move_line_params = (self.date_at,) * 6
  482. query_inject_move_line_params += (
  483. self.env.uid,
  484. self.open_items_id.id,
  485. self.id,
  486. )
  487. self.env.cr.execute(query_inject_move_line,
  488. query_inject_move_line_params)
  489. def _compute_accounts_cumul(self):
  490. """ Compute cumulative amount for
  491. report_aged_partner_balance_qweb_account.
  492. """
  493. query_compute_accounts_cumul = """
  494. WITH
  495. cumuls AS
  496. (
  497. SELECT
  498. ra.id AS report_account_id,
  499. SUM(rl.amount_residual) AS cumul_amount_residual,
  500. SUM(rl.current) AS cumul_current,
  501. SUM(rl.age_30_days) AS cumul_age_30_days,
  502. SUM(rl.age_60_days) AS cumul_age_60_days,
  503. SUM(rl.age_90_days) AS cumul_age_90_days,
  504. SUM(rl.age_120_days) AS cumul_age_120_days,
  505. SUM(rl.older) AS cumul_older
  506. FROM
  507. report_aged_partner_balance_qweb_line rl
  508. INNER JOIN
  509. report_aged_partner_balance_qweb_partner rp
  510. ON rl.report_partner_id = rp.id
  511. INNER JOIN
  512. report_aged_partner_balance_qweb_account ra
  513. ON rp.report_account_id = ra.id
  514. WHERE
  515. ra.report_id = %s
  516. GROUP BY
  517. ra.id
  518. )
  519. UPDATE
  520. report_aged_partner_balance_qweb_account
  521. SET
  522. cumul_amount_residual = c.cumul_amount_residual,
  523. cumul_current = c.cumul_current,
  524. cumul_age_30_days = c.cumul_age_30_days,
  525. cumul_age_60_days = c.cumul_age_60_days,
  526. cumul_age_90_days = c.cumul_age_90_days,
  527. cumul_age_120_days = c.cumul_age_120_days,
  528. cumul_older = c.cumul_older,
  529. percent_current =
  530. CASE
  531. WHEN c.cumul_amount_residual != 0
  532. THEN 100 * c.cumul_current / c.cumul_amount_residual
  533. END,
  534. percent_age_30_days =
  535. CASE
  536. WHEN c.cumul_amount_residual != 0
  537. THEN 100 * c.cumul_age_30_days / c.cumul_amount_residual
  538. END,
  539. percent_age_60_days =
  540. CASE
  541. WHEN c.cumul_amount_residual != 0
  542. THEN 100 * c.cumul_age_60_days / c.cumul_amount_residual
  543. END,
  544. percent_age_90_days =
  545. CASE
  546. WHEN c.cumul_amount_residual != 0
  547. THEN 100 * c.cumul_age_90_days / c.cumul_amount_residual
  548. END,
  549. percent_age_120_days =
  550. CASE
  551. WHEN c.cumul_amount_residual != 0
  552. THEN 100 * c.cumul_age_120_days / c.cumul_amount_residual
  553. END,
  554. percent_older =
  555. CASE
  556. WHEN c.cumul_amount_residual != 0
  557. THEN 100 * c.cumul_older / c.cumul_amount_residual
  558. END
  559. FROM
  560. cumuls c
  561. WHERE
  562. id = c.report_account_id
  563. """
  564. params_compute_accounts_cumul = (self.id,)
  565. self.env.cr.execute(query_compute_accounts_cumul,
  566. params_compute_accounts_cumul)