From 225780aa80624b87bf31f63fe3f8ec7ddbb917fc Mon Sep 17 00:00:00 2001 From: Florian da Costa Date: Mon, 17 Jul 2017 17:44:13 +0200 Subject: [PATCH] Add module sql_export_mail --- sql_export_mail/README.rst | 65 ++++++++ sql_export_mail/__init__.py | 1 + sql_export_mail/__openerp__.py | 39 +++++ sql_export_mail/i18n/fr.po | 94 +++++++++++ sql_export_mail/i18n/sql_export_mail.pot | 89 +++++++++++ sql_export_mail/mail_template.xml | 28 ++++ sql_export_mail/models/__init__.py | 1 + sql_export_mail/models/sql_export.py | 155 +++++++++++++++++++ sql_export_mail/tests/__init__.py | 2 + sql_export_mail/tests/test_sql_query_mail.py | 33 ++++ sql_export_mail/views/sql_export_view.xml | 31 ++++ sql_export_mail/wizard/__init__.py | 1 + sql_export_mail/wizard/wizard_file.py | 106 +++++++++++++ sql_export_mail/wizard/wizard_file_view.xml | 27 ++++ 14 files changed, 672 insertions(+) create mode 100644 sql_export_mail/README.rst create mode 100644 sql_export_mail/__init__.py create mode 100644 sql_export_mail/__openerp__.py create mode 100644 sql_export_mail/i18n/fr.po create mode 100644 sql_export_mail/i18n/sql_export_mail.pot create mode 100644 sql_export_mail/mail_template.xml create mode 100644 sql_export_mail/models/__init__.py create mode 100644 sql_export_mail/models/sql_export.py create mode 100644 sql_export_mail/tests/__init__.py create mode 100644 sql_export_mail/tests/test_sql_query_mail.py create mode 100644 sql_export_mail/views/sql_export_view.xml create mode 100644 sql_export_mail/wizard/__init__.py create mode 100644 sql_export_mail/wizard/wizard_file.py create mode 100644 sql_export_mail/wizard/wizard_file_view.xml diff --git a/sql_export_mail/README.rst b/sql_export_mail/README.rst new file mode 100644 index 000000000..c575b0034 --- /dev/null +++ b/sql_export_mail/README.rst @@ -0,0 +1,65 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +SQL Export Mail +=============== + +Allow to send the result of a query (made with the module sql_export) by mail. + + +Configuration +============= + +To configure this module, you need to: + +#. Go to the sql query for which you want users to be notified by e-mail. +#. Add users to be notified in the field Users Notified by e-mail. +#. Click on the button create a cron and then configure the cron to run when + you want to. If you already have created a cron for another query, you can + use it again for other queries + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/149/10.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Florian da Costa + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + diff --git a/sql_export_mail/__init__.py b/sql_export_mail/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/sql_export_mail/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sql_export_mail/__openerp__.py b/sql_export_mail/__openerp__.py new file mode 100644 index 000000000..58b11ef5a --- /dev/null +++ b/sql_export_mail/__openerp__.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2015 Akretion (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'SQL Export Mail', + 'version': '8.0.1.0.0', + 'author': 'Akretion,Odoo Community Association (OCA)', + 'website': 'http://www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Generic Modules/Others', + 'summary': 'Export data in csv file with SQL requests', + 'depends': [ + 'sql_export', + 'mail', + ], + 'data': [ + 'views/sql_export_view.xml', + 'mail_template.xml', + ], + 'installable': True, + } diff --git a/sql_export_mail/i18n/fr.po b/sql_export_mail/i18n/fr.po new file mode 100644 index 000000000..9bbd26c72 --- /dev/null +++ b/sql_export_mail/i18n/fr.po @@ -0,0 +1,94 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sql_export_mail +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-18 13:15+0000\n" +"PO-Revision-Date: 2017-07-18 13:15+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sql_export_mail +#: model:email.template,body_html:sql_export_mail.sql_export_mailer +msgid "\n" +"
\n" +"\n" +"

You will find the report ${object.name or ''} as an attachment of the mail.

\n" +"\n" +"
\n" +" " +msgstr "\n" +"
\n" +"\n" +"

Vous trouverez le rapport ${object.name or ''} en pièce jointe de ce mail.

\n" +"\n" +"
\n" +" " + +#. module: sql_export_mail +#: model:email.template,subject:sql_export_mail.sql_export_mailer +msgid "${object.name or ''}" +msgstr "${object.name or ''}" + +#. module: sql_export_mail +#: help:sql.export,mail_user_ids:0 +msgid "Add the users who want to receive the report by e-mail. You need to link the sql query with a cron to send mail automatically" +msgstr "Ajoutez les utilisateurs voulant recevoir le rapport par mail. Vous devez ensuite créer une tâche planifiée pour envoyer le mail automatiquement." + +#. module: sql_export_mail +#: view:sql.export:sql_export_mail.sql_export_mail_view_form +msgid "Create Cron" +msgstr "Créer une tâche planfiée" + +#. module: sql_export_mail +#: model:ir.model.fields,field_description:sql_export_mail.field_sql_export_cron_ids +#: view:sql.export:sql_export_mail.sql_export_mail_view_form +#: field:sql.export,cron_ids:0 +msgid "Crons" +msgstr "Tâches planifiées" + +#. module: sql_export_mail +#: selection:sql.export,mail_condition:0 +msgid "File Not Empty" +msgstr "Fichier non vide." + +#. module: sql_export_mail +#: code:addons/sql_export_mail/models/sql_export.py:126 +#, python-format +msgid "It is not possible to execute and send a query automatically by e-mail if there are parameters to fill" +msgstr "Il n'est pas possible d'exécuter en envoyer le résultat d'une requête par mail si celle-ci contient des paramètres." + +#. module: sql_export_mail +#: model:ir.model.fields,field_description:sql_export_mail.field_sql_export_mail_condition +#: field:sql.export,mail_condition:0 +msgid "Mail condition" +msgstr "Condition d'envoi par mail" + +#. module: sql_export_mail +#: model:ir.model,name:sql_export_mail.model_sql_export +msgid "SQL export" +msgstr "Export SQL" + +#. module: sql_export_mail +#: code:addons/sql_export_mail/models/sql_export.py:135 +#, python-format +msgid "The user does not have any e-mail address." +msgstr "L'utilisateur selectionné n'a pas d'addresse mail." + +#. module: sql_export_mail +#: model:ir.model.fields,field_description:sql_export_mail.field_sql_export_mail_user_ids +#: field:sql.export,mail_user_ids:0 +msgid "User to notify" +msgstr "Utilisateurs à notifier par mail" + +#. module: sql_export_mail +#: view:sql.export:sql_export_mail.sql_export_mail_view_form +msgid "Users Notified by e-mail" +msgstr "Utilisateurs notifiés par mail" diff --git a/sql_export_mail/i18n/sql_export_mail.pot b/sql_export_mail/i18n/sql_export_mail.pot new file mode 100644 index 000000000..a27de68f6 --- /dev/null +++ b/sql_export_mail/i18n/sql_export_mail.pot @@ -0,0 +1,89 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sql_export_mail +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-18 13:24+0000\n" +"PO-Revision-Date: 2017-07-18 13:24+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sql_export_mail +#: model:email.template,body_html:sql_export_mail.sql_export_mailer +msgid "\n" +"
\n" +"\n" +"

You will find the report ${object.name or ''} as an attachment of the mail.

\n" +"\n" +"
\n" +" " +msgstr "" + +#. module: sql_export_mail +#: model:email.template,subject:sql_export_mail.sql_export_mailer +msgid "${object.name or ''}" +msgstr "" + +#. module: sql_export_mail +#: help:sql.export,mail_user_ids:0 +msgid "Add the users who want to receive the report by e-mail. You need to link the sql query with a cron to send mail automatically" +msgstr "" + +#. module: sql_export_mail +#: view:sql.export:sql_export_mail.sql_export_mail_view_form +msgid "Create Cron" +msgstr "" + +#. module: sql_export_mail +#: model:ir.model.fields,field_description:sql_export_mail.field_sql_export_cron_ids +#: view:sql.export:sql_export_mail.sql_export_mail_view_form +#: field:sql.export,cron_ids:0 +msgid "Crons" +msgstr "" + +#. module: sql_export_mail +#: selection:sql.export,mail_condition:0 +msgid "File Not Empty" +msgstr "" + +#. module: sql_export_mail +#: code:addons/sql_export_mail/models/sql_export.py:126 +#, python-format +msgid "It is not possible to execute and send a query automatically by e-mail if there are parameters to fill" +msgstr "" + +#. module: sql_export_mail +#: model:ir.model.fields,field_description:sql_export_mail.field_sql_export_mail_condition +#: field:sql.export,mail_condition:0 +msgid "Mail condition" +msgstr "" + +#. module: sql_export_mail +#: model:ir.model,name:sql_export_mail.model_sql_export +msgid "SQL export" +msgstr "" + +#. module: sql_export_mail +#: code:addons/sql_export_mail/models/sql_export.py:135 +#, python-format +msgid "The user does not have any e-mail address." +msgstr "" + +#. module: sql_export_mail +#: model:ir.model.fields,field_description:sql_export_mail.field_sql_export_mail_user_ids +#: field:sql.export,mail_user_ids:0 +msgid "User to notify" +msgstr "" + +#. module: sql_export_mail +#: view:sql.export:sql_export_mail.sql_export_mail_view_form +msgid "Users Notified by e-mail" +msgstr "" + diff --git a/sql_export_mail/mail_template.xml b/sql_export_mail/mail_template.xml new file mode 100644 index 000000000..db5599c6a --- /dev/null +++ b/sql_export_mail/mail_template.xml @@ -0,0 +1,28 @@ + + + + + + + + + SQL Export + admin@example.com + ${object.get_email_address_for_template()} + ${object.name or ''} + + + + +

You will find the report ${object.name or ''} as an attachment of the mail.

+ + + ]]>
+
+ +
+
diff --git a/sql_export_mail/models/__init__.py b/sql_export_mail/models/__init__.py new file mode 100644 index 000000000..014462062 --- /dev/null +++ b/sql_export_mail/models/__init__.py @@ -0,0 +1 @@ +from . import sql_export diff --git a/sql_export_mail/models/sql_export.py b/sql_export_mail/models/sql_export.py new file mode 100644 index 000000000..ca84a47ab --- /dev/null +++ b/sql_export_mail/models/sql_export.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2017 Akretion (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import models, fields, api, _ +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +from openerp.exceptions import Warning as UserError +from datetime import datetime, timedelta +from openerp import SUPERUSER_ID + + +class SqlExport(models.Model): + _inherit = 'sql.export' + + mail_user_ids = fields.Many2many( + 'res.users', + 'mail_user_sqlquery_rel', + 'sql_id', + 'user_id', + 'User to notify', + help='Add the users who want to receive the report by e-mail. You ' + 'need to link the sql query with a cron to send mail automatically') + cron_ids = fields.Many2many( + 'ir.cron', + 'cron_sqlquery_rel', + 'sql_id', + 'cron_id', + 'Crons') + # We could implement other conditions, that is why it is a selection field + mail_condition = fields.Selection( + [('not_empty', 'File Not Empty')], default='not_empty') + + @api.multi + def create_cron(self): + self.ensure_one() + nextcall = datetime.now() + timedelta(hours=2) + nextcall_fmt = datetime.strftime(nextcall, + DEFAULT_SERVER_DATETIME_FORMAT) + cron_vals = { + 'active': True, + 'model': 'sql.export', + 'function': '_run_all_sql_export_for_cron', + 'name': 'SQL Export : %s' % self.name, + 'nextcall': nextcall_fmt, + 'doall': False, + 'numbercall': -1, + 'user_id': SUPERUSER_ID, + } + cron = self.env['ir.cron'].create(cron_vals) + write_vals = {'args': '[[%s]]' % cron.id} + cron.write(write_vals) + + self.write({'cron_ids': [(4, cron.id)]}) + + @api.one + def send_mail(self, params=None): + mail_template = self.env.ref('sql_export_mail.sql_export_mailer') + now_time = datetime.strftime(datetime.now(), + DEFAULT_SERVER_DATETIME_FORMAT) + attach_obj = self.env['ir.attachment'] + if self.mail_condition == 'not_empty': + res = self._execute_sql_request( + params=params, mode='fetchone') + if not res: + return + + binary = self._execute_sql_request( + params=params, mode='stdout', copy_options=self.copy_options) + attach_vals = { + 'name': now_time + ' - ' + self.name, + 'datas_fname': now_time + ' - ' + self.name + '.csv', + 'datas': binary, + } + attachment = attach_obj.create(attach_vals) + msg_id = mail_template.send_mail(self.id, force_send=False) + mail = self.env['mail.mail'].browse(msg_id) + mail.write({'attachment_ids': [(4, attachment.id)]}) + + @api.model + def _run_all_sql_export_for_cron(self, cron_ids): + exports = self.search([('cron_ids', 'in', cron_ids)]) + for export in exports: + if "%(company_id)s" in export.query and \ + "%(user_id)s" not in export.query: + variable_dict = {} + companies = self.env['res.company'].search([]) + for company in companies: + users = export.mail_user_ids.filtered( + lambda u: u.company_id == company) + if users: + variable_dict['company_id'] = users[0].company_id.id + export.with_context(mail_to=users.ids).send_mail( + params=variable_dict) + elif "%(user_id)s" in export.query: + variable_dict = {} + for user in export.mail_user_ids: + variable_dict['user_id'] = user.id + if "%(company_id)s" in export.query: + variable_dict['company_id'] = user.company_id.id + export.with_context(mail_to=[user.id]).send_mail( + params=variable_dict) + else: + export.send_mail() + + @api.one + @api.constrains('field_ids', 'mail_user_ids') + def check_no_parameter_if_sent_by_mail(self): + if self.field_ids and self.mail_user_ids: + raise UserError(_( + "It is not possible to execute and send a query automatically" + " by e-mail if there are parameters to fill")) + + @api.one + @api.constrains('mail_user_ids') + def check_mail_user(self): + for user in self.mail_user_ids: + if not user.email: + raise UserError(_( + "The user does not have any e-mail address.")) + + @api.multi + def get_email_address_for_template(self): + """ + Called from mail template + """ + self.ensure_one() + if self.env.context.get('mail_to'): + mail_users = self.env['res.users'].browse( + self.env.context.get('mail_to')) + else: + mail_users = self.mail_user_ids + emails = '' + for user in mail_users: + if emails and user.email: + emails += ',' + user.email + elif user.email: + emails += user.email + return emails diff --git a/sql_export_mail/tests/__init__.py b/sql_export_mail/tests/__init__.py new file mode 100644 index 000000000..1d119c788 --- /dev/null +++ b/sql_export_mail/tests/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import test_sql_query_mail diff --git a/sql_export_mail/tests/test_sql_query_mail.py b/sql_export_mail/tests/test_sql_query_mail.py new file mode 100644 index 000000000..45bc2d2d3 --- /dev/null +++ b/sql_export_mail/tests/test_sql_query_mail.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Akretion () +# @author: Florian da Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.tests.common import TransactionCase +from openerp import SUPERUSER_ID + + +class TestExportSqlQueryMail(TransactionCase): + + def setUp(self): + super(TestExportSqlQueryMail, self).setUp() + self.sql_report_demo = self.env.ref('sql_export.sql_export_partner') + self.sql_report_demo.write({'mail_user_ids': [(4, SUPERUSER_ID)]}) + + def test_sql_query_create_cron(self): + self.sql_report_demo.create_cron() + self.assertTrue(self.sql_report_demo.cron_ids) + cron = self.sql_report_demo.cron_ids + self.assertEqual(cron.function, '_run_all_sql_export_for_cron') + + def test_sql_query_mail(self): + mail_obj = self.env['mail.mail'] + mails = mail_obj.search( + [('model', '=', 'sql.export'), + ('res_id', '=', self.sql_report_demo.id)]) + self.assertFalse(mails) + self.sql_report_demo.send_mail() + mails = mail_obj.search( + [('model', '=', 'sql.export'), + ('res_id', '=', self.sql_report_demo.id)]) + self.assertTrue(mails) diff --git a/sql_export_mail/views/sql_export_view.xml b/sql_export_mail/views/sql_export_view.xml new file mode 100644 index 000000000..a2f0fe781 --- /dev/null +++ b/sql_export_mail/views/sql_export_view.xml @@ -0,0 +1,31 @@ + + + + + + + sql.export + + + + + + + + + + + + + + + + + + + + + diff --git a/sql_export_mail/wizard/__init__.py b/sql_export_mail/wizard/__init__.py new file mode 100644 index 000000000..ddf406aa1 --- /dev/null +++ b/sql_export_mail/wizard/__init__.py @@ -0,0 +1 @@ +from . import wizard_file diff --git a/sql_export_mail/wizard/wizard_file.py b/sql_export_mail/wizard/wizard_file.py new file mode 100644 index 000000000..3f62198a7 --- /dev/null +++ b/sql_export_mail/wizard/wizard_file.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2015 Akretion (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import datetime +from lxml import etree + +from openerp import models, fields, api, osv +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT + + +class SqlFileWizard(models.TransientModel): + _name = "sql.file.wizard" + _description = "Allow the user to save the file with sql request's data" + + binary_file = fields.Binary('File', readonly=True) + file_name = fields.Char('File Name', readonly=True) + sql_export_id = fields.Many2one(comodel_name='sql.export', required=True) + + @api.model + def fields_view_get(self, view_id=None, view_type='form', + toolbar=False, submenu=False): + """ + Display dinamicaly parameter fields depending on the sql_export. + """ + res = super(SqlFileWizard, self).fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, + submenu=submenu) + export_obj = self.env['sql.export'] + if view_type == 'form': + sql_export = export_obj.browse(self._context.get('active_id')) + if sql_export.field_ids: + eview = etree.fromstring(res['arch']) + group = etree.Element( + 'group', name="variables_group", colspan="4") + toupdate_fields = [] + for field in sql_export.field_ids: + kwargs = {'name': "%s" % field.name} + toupdate_fields.append(field.name) + view_field = etree.SubElement(group, 'field', **kwargs) + osv.orm.setup_modifiers( + view_field, self.fields_get(field.name)) + + res['fields'].update(self.fields_get(toupdate_fields)) + placeholder = eview.xpath( + "//separator[@string='variables_placeholder']")[0] + placeholder.getparent().replace( + placeholder, group) + res['arch'] = etree.tostring(eview, pretty_print=True) + return res + + @api.multi + def export_sql(self): + self.ensure_one() + sql_export = self.sql_export_id + + # Manage Params + variable_dict = {} + today = datetime.datetime.now() + today_tz = fields.Datetime.context_timestamp( + sql_export, today) + date = today_tz.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + if sql_export.field_ids: + for field in sql_export.field_ids: + variable_dict[field.name] = self[field.name] + if "%(company_id)s" in sql_export.query: + variable_dict['company_id'] = self.env.user.company_id.id + if "%(user_id)s" in sql_export.query: + variable_dict['user_id'] = self._uid + + # Execute Request + res = sql_export._execute_sql_request( + params=variable_dict, mode='stdout', + copy_options=sql_export.copy_options) + + self.write({ + 'binary_file': res, + 'file_name': sql_export.name + '_' + date + '.csv' + }) + return { + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'sql.file.wizard', + 'res_id': self.id, + 'type': 'ir.actions.act_window', + 'target': 'new', + 'context': self._context, + 'nodestroy': True, + } diff --git a/sql_export_mail/wizard/wizard_file_view.xml b/sql_export_mail/wizard/wizard_file_view.xml new file mode 100644 index 000000000..162bcccb5 --- /dev/null +++ b/sql_export_mail/wizard/wizard_file_view.xml @@ -0,0 +1,27 @@ + + + + + + sql.file.wizard.view.form + sql.file.wizard + +
+ + + + + + + +
+ +
+