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.

107 lines
3.8 KiB

  1. # Copyright 2019 Akretion
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  3. from odoo import api, exceptions, fields, models, _
  4. from io import BytesIO
  5. import logging
  6. import base64
  7. _logger = logging.getLogger(__name__)
  8. try:
  9. import openpyxl
  10. except ImportError:
  11. _logger.debug('Can not import openpyxl')
  12. class SqlExport(models.Model):
  13. _inherit = 'sql.export'
  14. file_format = fields.Selection(
  15. selection_add=[('excel', 'Excel')])
  16. header = fields.Boolean(
  17. default=True,
  18. help="Indicate if the header should be exported to the file.")
  19. attachment_id = fields.Many2one(
  20. 'ir.attachment', string='Excel Template',
  21. help="If you configure an excel file (in xlsx format) here, the "
  22. "result of the query will be injected in it.\nIt is usefull to "
  23. "feed data in a excel file pre-configured with calculation")
  24. sheet_position = fields.Integer(
  25. default=1,
  26. help="Indicate the sheet's position of the excel template where the "
  27. "result of the sql query should be injected.")
  28. row_position = fields.Integer(
  29. default=1,
  30. help="Indicate from which row the result of the query should be "
  31. "injected.")
  32. col_position = fields.Integer(
  33. string="Column Position",
  34. default=1,
  35. help="Indicate from which column the result of the query should be "
  36. "injected.")
  37. @api.constrains('sheet_position')
  38. def check_sheet_position(self):
  39. for export in self:
  40. if export.sheet_position < 1:
  41. raise exceptions.ValidationError(
  42. _("The sheet position can't be less than 1."))
  43. @api.constrains('row_position')
  44. def check_row_position(self):
  45. for export in self:
  46. if export.row_position < 1:
  47. raise exceptions.ValidationError(
  48. _("The row position can't be less than 1."))
  49. @api.constrains('col_position')
  50. def check_column_position(self):
  51. for export in self:
  52. if export.col_position < 1:
  53. raise exceptions.ValidationError(
  54. _("The column position can't be less than 1."))
  55. @api.multi
  56. def _get_file_extension(self):
  57. self.ensure_one()
  58. if self.file_format == 'excel':
  59. return 'xlsx'
  60. else:
  61. return super()._get_file_extension()
  62. @api.multi
  63. def excel_get_data_from_query(self, variable_dict):
  64. self.ensure_one()
  65. res = self._execute_sql_request(
  66. params=variable_dict, mode='fetchall', header=self.header)
  67. # Case we insert data in an existing excel file.
  68. if self.attachment_id:
  69. datas = self.attachment_id.datas
  70. infile = BytesIO()
  71. infile.write(base64.b64decode(datas))
  72. infile.seek(0)
  73. wb = openpyxl.load_workbook(filename=infile)
  74. sheets = wb.worksheets
  75. try:
  76. ws = sheets[self.sheet_position - 1]
  77. except IndexError:
  78. raise exceptions.ValidationError(
  79. _("The Excel Template file contains less than %s sheets "
  80. "Please, adjust the Sheet Position parameter."))
  81. row_position = self.row_position or 1
  82. col_position = self.col_position or 1
  83. # Case of excel file creation
  84. else:
  85. wb = openpyxl.Workbook()
  86. ws = wb.active
  87. row_position = 1
  88. col_position = 1
  89. for index, row in enumerate(res, row_position):
  90. for col, val in enumerate(row, col_position):
  91. ws.cell(row=index, column=col).value = val
  92. output = BytesIO()
  93. wb.save(output)
  94. output.getvalue()
  95. output_datas = base64.b64encode(output.getvalue())
  96. output.close()
  97. return output_datas