OCA reporting engine fork for dev and update.
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.

162 lines
5.5 KiB

  1. # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
  3. import base64
  4. from odoo import api, fields, models, _
  5. from odoo.tools.safe_eval import safe_eval
  6. from odoo.exceptions import UserError
  7. from odoo.addons.queue_job.job import job
  8. # Define all supported report_type
  9. REPORT_TYPES_FUNC = {'qweb-pdf': 'render_qweb_pdf',
  10. 'qweb-text': 'render_qweb_text',
  11. 'qweb-xml': 'render_qweb_xml',
  12. 'csv': 'render_csv',
  13. 'excel': 'render_excel',
  14. 'xlsx': 'render_xlsx', }
  15. class ReportAsync(models.Model):
  16. _name = 'report.async'
  17. _description = 'Report Async'
  18. action_id = fields.Many2one(
  19. comodel_name='ir.actions.act_window',
  20. string='Reports',
  21. required=True,
  22. )
  23. allow_async = fields.Boolean(
  24. string='Allow Async',
  25. default=False,
  26. help="This is not automatic field, please check if you want to allow "
  27. "this report in background process",
  28. )
  29. name = fields.Char(
  30. string='Name',
  31. related='action_id.display_name',
  32. )
  33. email_notify = fields.Boolean(
  34. string='Email Notification',
  35. help="Send email with link to report, when it is ready",
  36. )
  37. group_ids = fields.Many2many(
  38. string='Groups',
  39. comodel_name='res.groups',
  40. help="Only user in selected groups can use this report."
  41. "If left blank, everyone can use",
  42. )
  43. job_ids = fields.Many2many(
  44. comodel_name='queue.job',
  45. compute='_compute_job',
  46. help="List all jobs related to this running report",
  47. )
  48. job_status = fields.Selection(
  49. selection=[('pending', 'Pending'),
  50. ('enqueued', 'Enqueued'),
  51. ('started', 'Started'),
  52. ('done', 'Done'),
  53. ('failed', 'Failed')],
  54. compute='_compute_job',
  55. help="Latest Job Status",
  56. )
  57. job_info = fields.Text(
  58. compute='_compute_job',
  59. help="Latest Job Error Message",
  60. )
  61. file_ids = fields.Many2many(
  62. comodel_name='ir.attachment',
  63. compute='_compute_file',
  64. help="List all files created by this report background process",
  65. )
  66. @api.multi
  67. def _compute_job(self):
  68. for rec in self:
  69. rec.job_ids = self.sudo().env['queue.job'].search(
  70. [('func_string', 'like', 'report.async(%s,)' % rec.id),
  71. ('user_id', '=', self._uid)],
  72. order='id desc')
  73. rec.job_status = (rec.job_ids[0].sudo().state
  74. if rec.job_ids else False)
  75. rec.job_info = (rec.job_ids[0].sudo().exc_info
  76. if rec.job_ids else False)
  77. @api.multi
  78. def _compute_file(self):
  79. files = self.env['ir.attachment'].search(
  80. [('res_model', '=', 'report.async'),
  81. ('res_id', 'in', self.ids),
  82. ('create_uid', '=', self._uid)],
  83. order='id desc')
  84. for rec in self:
  85. rec.file_ids = files.filtered(lambda l: l.res_id == rec.id)
  86. def run_now(self):
  87. self.ensure_one()
  88. action = self.env.ref(self.action_id.xml_id)
  89. result = action.read()[0]
  90. ctx = safe_eval(result.get('context', {}))
  91. ctx.update({'async_process': False})
  92. result['context'] = ctx
  93. return result
  94. @api.multi
  95. def run_async(self):
  96. self.ensure_one()
  97. if not self.allow_async:
  98. raise UserError(_('Background process not allowed.'))
  99. action = self.env.ref(self.action_id.xml_id)
  100. result = action.read()[0]
  101. ctx = safe_eval(result.get('context', {}))
  102. ctx.update({'async_process': True})
  103. result['context'] = ctx
  104. return result
  105. @api.multi
  106. def view_files(self):
  107. self.ensure_one()
  108. action = self.env.ref('report_async.action_view_files')
  109. result = action.read()[0]
  110. result['domain'] = [('id', 'in', self.file_ids.ids)]
  111. return result
  112. @api.multi
  113. def view_jobs(self):
  114. self.ensure_one()
  115. action = self.env.ref('queue_job.action_queue_job')
  116. result = action.read()[0]
  117. result['domain'] = [('id', 'in', self.job_ids.ids)]
  118. result['context'] = {}
  119. return result
  120. @api.model
  121. @job
  122. def run_report(self, docids, data, report_id, user_id):
  123. report = self.env['ir.actions.report'].browse(report_id)
  124. func = REPORT_TYPES_FUNC[report.report_type]
  125. # Run report
  126. out_file, file_ext = getattr(report, func)(docids, data)
  127. out_file = base64.b64encode(out_file)
  128. out_name = '%s.%s' % (report.name, file_ext)
  129. # Save report to attachment
  130. attachment = self.env['ir.attachment'].sudo().create({
  131. 'name': out_name,
  132. 'datas': out_file,
  133. 'datas_fname': out_name,
  134. 'type': 'binary',
  135. 'res_model': 'report.async',
  136. 'res_id': self.id,
  137. })
  138. self._cr.execute("""
  139. UPDATE ir_attachment SET create_uid = %s, write_uid = %s
  140. WHERE id = %s""", (self._uid, self._uid, attachment.id))
  141. # Send email
  142. if self.email_notify:
  143. self._send_email(attachment)
  144. def _send_email(self, attachment):
  145. template = self.env.ref('report_async.async_report_delivery')
  146. template.send_mail(attachment.id,
  147. notif_layout='mail.mail_notification_light',
  148. force_send=False)