diff --git a/survey_result_mail/README.rst b/survey_result_mail/README.rst new file mode 100644 index 00000000..b783aca7 --- /dev/null +++ b/survey_result_mail/README.rst @@ -0,0 +1,110 @@ +================== +Survey Result Mail +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:acb56876a3be92a6474b4858fbd735f2c4286990e7d295866b879dcdbabba931 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsurvey-lightgray.png?logo=github + :target: https://github.com/OCA/survey/tree/18.0/survey_result_mail + :alt: OCA/survey +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/survey-18-0/survey-18-0-survey_result_mail + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/survey&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the functionality of surveys to allow you to send an +automatic email with the survey answers to the filler as soon as the +questionary is completed. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure automatic result sending to the user: + +1. Go to the survey you want to configure. +2. In the *Options* tab, you'll find a new *Send survey answers* option + that you can set on to send an automatic email with the user's + answers. +3. You can also choose which mail template to send. This module provides + two. One with the answers inlined and another with the answers + attached as pdf report. + +Usage +===== + +To test this module, you can: + +1. Go to a survey an *Test* it. +2. Once finsished a mail with the aswers attached will be sent. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Tecnativa + +Contributors +------------ + +- `Tecnativa `__ + + - David Vidal + - Pilar Vargas + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +.. |maintainer-pilarvargas-tecnativa| image:: https://github.com/pilarvargas-tecnativa.png?size=40px + :target: https://github.com/pilarvargas-tecnativa + :alt: pilarvargas-tecnativa + +Current `maintainer `__: + +|maintainer-pilarvargas-tecnativa| + +This module is part of the `OCA/survey `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/survey_result_mail/__init__.py b/survey_result_mail/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/survey_result_mail/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/survey_result_mail/__manifest__.py b/survey_result_mail/__manifest__.py new file mode 100644 index 00000000..6f2eb74d --- /dev/null +++ b/survey_result_mail/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2023 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Survey Result Mail", + "summary": "Send survey answers to the survey user", + "version": "18.0.1.0.0", + "development_status": "Beta", + "category": "Marketing/Survey", + "website": "https://github.com/OCA/survey", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["pilarvargas-tecnativa"], + "license": "AGPL-3", + "depends": ["survey"], + "data": [ + "templates/survey_answer_templates.xml", + "reports/survey_answer_report.xml", + "data/mail_template.xml", + "views/survey_survey_views.xml", + "views/survey_user_input_views.xml", + ], +} diff --git a/survey_result_mail/data/mail_template.xml b/survey_result_mail/data/mail_template.xml new file mode 100644 index 00000000..989f7558 --- /dev/null +++ b/survey_result_mail/data/mail_template.xml @@ -0,0 +1,59 @@ + + + + Survey: Results (with report) + + Results for your {{object.survey_id.title}} survey + {{ (object.partner_id.email_formatted or object.email) }} + +
+

+ Dear . +

+ + You'll find attached your survey answers so you can review them. + + + Thanks you for your participation. +

+
+
+ + {{object.partner_id.lang}} + +
+ + Survey: Results (inline) + + Results for your {{object.survey_id.title}} survey + {{ (object.survey_id.user_id.email_formatted or user.email_formatted) }} + {{ (object.partner_id.email_formatted or object.email) }} + +
+

+ Dear . +

+ + Here you have a copy of your answers: +

+

+ +

+

+ Thanks you for your participation. +

+
+
+ {{object.partner_id.lang}} + +
+
diff --git a/survey_result_mail/i18n/es.po b/survey_result_mail/i18n/es.po new file mode 100644 index 00000000..65005cfb --- /dev/null +++ b/survey_result_mail/i18n/es.po @@ -0,0 +1,159 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * survey_result_mail +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 11:28+0000\n" +"PO-Revision-Date: 2023-11-16 12:36+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.4\n" + +#. module: survey_result_mail +#: model:ir.actions.report,print_report_name:survey_result_mail.survey_result_report +msgid "'Survey results - %s' % (object.survey_id.display_name)" +msgstr "'Resultados de la encuesta - %s' % (object.survey_id.display_name)" + +#. module: survey_result_mail +#: model:mail.template,body_html:survey_result_mail.mail_template_user_input_result_inline +msgid "" +"
\n" +"

\n" +" Dear .\n" +"

\n" +"\n" +" Here you have a copy of your answers:\n" +"

\n" +"

\n" +" \n" +"

\n" +"

\n" +" Thanks you for your participation.\n" +"

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

\n" +" Estimado/a .\n" +"

\n" +"\n" +" Aquí tiene una copia de sus respuestas:\n" +"

\n" +"

\n" +" \n" +"

\n" +"

\n" +" Gracias por su participación.\n" +"

\n" +"
\n" +" " + +#. module: survey_result_mail +#: model:mail.template,body_html:survey_result_mail.mail_template_user_input_result_report +msgid "" +"
\n" +"

\n" +" Dear .\n" +"

\n" +"\n" +" You'll find attached your survey answers so you can review them.\n" +"\n" +"\n" +" Thanks you for your participation.\n" +"

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

\n" +" Estimado/a .\n" +"

\n" +"\n" +" Encontrará adjuntas sus respuestas a la encuesta para que pueda " +"revisarlas.\n" +"\n" +"\n" +" Gracias por su participación.\n" +"

\n" +"
\n" +" " + +#. module: survey_result_mail +#: model_terms:ir.ui.view,arch_db:survey_result_mail.survey_result_report_view_document +msgid "" +"Reponses to your survey" +msgstr "" +"Respuestas de su encuesta" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_survey__result_mail_template_id +msgid "Result Mail Template" +msgstr "Plantilla de resultados de encuesta" + +#. module: survey_result_mail +#: model:mail.template,subject:survey_result_mail.mail_template_user_input_result_inline +#: model:mail.template,subject:survey_result_mail.mail_template_user_input_result_report +msgid "Results for your {{object.survey_id.title}} survey" +msgstr "Resultados para su encuesta {{object.survey_id.title}}" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_survey__send_result_mail +msgid "Send survey answers" +msgstr "Enviar respuestas de encuesta" + +#. module: survey_result_mail +#: model:ir.model,name:survey_result_mail.model_survey_survey +msgid "Survey" +msgstr "Encuesta" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_user_input__survey_result +msgid "Survey Result" +msgstr "Resultado de la encuesta" + +#. module: survey_result_mail +#: model:mail.template,report_name:survey_result_mail.mail_template_user_input_result_report +msgid "Survey Results" +msgstr "Resultados de la encuesta" + +#. module: survey_result_mail +#: model:ir.model,name:survey_result_mail.model_survey_user_input +msgid "Survey User Input" +msgstr "Entrada de usuario de la encuesta" + +#. module: survey_result_mail +#: model:ir.actions.report,name:survey_result_mail.survey_result_report +msgid "Survey results" +msgstr "Resultados de la encuesta" + +#. module: survey_result_mail +#: model:mail.template,name:survey_result_mail.mail_template_user_input_result_inline +msgid "Survey: Results (inline)" +msgstr "Encuesta: Resultados (en línea)" + +#. module: survey_result_mail +#: model:mail.template,name:survey_result_mail.mail_template_user_input_result_report +msgid "Survey: Results (with report)" +msgstr "Encuesta: Resultados (con informe)" + +#. module: survey_result_mail +#: model:ir.model.fields,help:survey_result_mail.field_survey_survey__send_result_mail +msgid "" +"When the survey is submitted, an email will be sent to the user withthe " +"answers" +msgstr "" +"Cuando se completa la encuesta se envía un correo al usuario con las " +"respuestas" diff --git a/survey_result_mail/i18n/it.po b/survey_result_mail/i18n/it.po new file mode 100644 index 00000000..7083bf43 --- /dev/null +++ b/survey_result_mail/i18n/it.po @@ -0,0 +1,155 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * survey_result_mail +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-20 08:35+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: survey_result_mail +#: model:ir.actions.report,print_report_name:survey_result_mail.survey_result_report +msgid "'Survey results - %s' % (object.survey_id.display_name)" +msgstr "'Risultati sondaggio - %s' % (object.survey_id.display_name)" + +#. module: survey_result_mail +#: model:mail.template,body_html:survey_result_mail.mail_template_user_input_result_inline +msgid "" +"
\n" +"

\n" +" Dear .\n" +"

\n" +"\n" +" Here you have a copy of your answers:\n" +"

\n" +"

\n" +" \n" +"

\n" +"

\n" +" Thanks you for your participation.\n" +"

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

\n" +" Spettabile .\n" +"

\n" +"\n" +" Di seguito una copia delle sue risposte:\n" +"

\n" +"

\n" +" \n" +"

\n" +"

\n" +" Ringraziamo per la partecipazione.\n" +"

\n" +"
\n" +" " + +#. module: survey_result_mail +#: model:mail.template,body_html:survey_result_mail.mail_template_user_input_result_report +msgid "" +"
\n" +"

\n" +" Dear .\n" +"

\n" +"\n" +" You'll find attached your survey answers so you can review them.\n" +"\n" +"\n" +" Thanks you for your participation.\n" +"

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

\n" +" Spettabile .\n" +"

\n" +"\n" +" Trova in allegato le sue risposte al sondaggio così le può " +"revisionare.\n" +"\n" +"\n" +" Ringraziamo per la partecipazione.\n" +"

\n" +"
\n" +" " + +#. module: survey_result_mail +#: model_terms:ir.ui.view,arch_db:survey_result_mail.survey_result_report_view_document +msgid "Reponses to your survey" +msgstr "" +"Risposte al tuo sondaggio" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_survey__result_mail_template_id +msgid "Result Mail Template" +msgstr "Modello e-mail risultato" + +#. module: survey_result_mail +#: model:mail.template,subject:survey_result_mail.mail_template_user_input_result_inline +#: model:mail.template,subject:survey_result_mail.mail_template_user_input_result_report +msgid "Results for your {{object.survey_id.title}} survey" +msgstr "Risultati per il suo sondaggio {{object.survey_id.title}}" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_survey__send_result_mail +msgid "Send survey answers" +msgstr "Invia risposte sondaggio" + +#. module: survey_result_mail +#: model:ir.model,name:survey_result_mail.model_survey_survey +msgid "Survey" +msgstr "Sondaggio" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_user_input__survey_result +msgid "Survey Result" +msgstr "Risultato sondaggio" + +#. module: survey_result_mail +#: model:mail.template,report_name:survey_result_mail.mail_template_user_input_result_report +msgid "Survey Results" +msgstr "Risultati sondaggio" + +#. module: survey_result_mail +#: model:ir.model,name:survey_result_mail.model_survey_user_input +msgid "Survey User Input" +msgstr "Risposta utente al sondaggio" + +#. module: survey_result_mail +#: model:ir.actions.report,name:survey_result_mail.survey_result_report +msgid "Survey results" +msgstr "Risultati sondaggio" + +#. module: survey_result_mail +#: model:mail.template,name:survey_result_mail.mail_template_user_input_result_inline +msgid "Survey: Results (inline)" +msgstr "Sondaggio: risultati (in linea)" + +#. module: survey_result_mail +#: model:mail.template,name:survey_result_mail.mail_template_user_input_result_report +msgid "Survey: Results (with report)" +msgstr "Sondaggio: risultati (con resoconto)" + +#. module: survey_result_mail +#: model:ir.model.fields,help:survey_result_mail.field_survey_survey__send_result_mail +msgid "" +"When the survey is submitted, an email will be sent to the user withthe " +"answers" +msgstr "" +"Quando viene inviato il sondaggio, una e-mail verrà inviata all'utente con " +"le sue risposte" diff --git a/survey_result_mail/i18n/survey_result_mail.pot b/survey_result_mail/i18n/survey_result_mail.pot new file mode 100644 index 00000000..be4cacc8 --- /dev/null +++ b/survey_result_mail/i18n/survey_result_mail.pot @@ -0,0 +1,114 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * survey_result_mail +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \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: survey_result_mail +#: model:ir.actions.report,print_report_name:survey_result_mail.survey_result_report +msgid "'Survey results - %s' % (object.survey_id.display_name)" +msgstr "" + +#. module: survey_result_mail +#: model:mail.template,body_html:survey_result_mail.mail_template_user_input_result_inline +msgid "" +"
\n" +"

\n" +" Dear .\n" +"

\n" +"\n" +" Here you have a copy of your answers:\n" +"

\n" +"

\n" +" \n" +"

\n" +"

\n" +" Thanks you for your participation.\n" +"

\n" +"
\n" +" " +msgstr "" + +#. module: survey_result_mail +#: model:mail.template,body_html:survey_result_mail.mail_template_user_input_result_report +msgid "" +"
\n" +"

\n" +" Dear .\n" +"

\n" +"\n" +" You'll find attached your survey answers so you can review them.\n" +"\n" +"\n" +" Thanks you for your participation.\n" +"

\n" +"
\n" +" " +msgstr "" + +#. module: survey_result_mail +#: model_terms:ir.ui.view,arch_db:survey_result_mail.survey_result_report_view_document +msgid "Reponses to your survey" +msgstr "" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_survey__result_mail_template_id +msgid "Result Mail Template" +msgstr "" + +#. module: survey_result_mail +#: model:mail.template,subject:survey_result_mail.mail_template_user_input_result_inline +#: model:mail.template,subject:survey_result_mail.mail_template_user_input_result_report +msgid "Results for your {{object.survey_id.title}} survey" +msgstr "" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_survey__send_result_mail +msgid "Send survey answers" +msgstr "" + +#. module: survey_result_mail +#: model:ir.model,name:survey_result_mail.model_survey_survey +msgid "Survey" +msgstr "" + +#. module: survey_result_mail +#: model:ir.model.fields,field_description:survey_result_mail.field_survey_user_input__survey_result +msgid "Survey Result" +msgstr "" + +#. module: survey_result_mail +#: model:ir.model,name:survey_result_mail.model_survey_user_input +msgid "Survey User Input" +msgstr "" + +#. module: survey_result_mail +#: model:ir.actions.report,name:survey_result_mail.survey_result_report +msgid "Survey results" +msgstr "" + +#. module: survey_result_mail +#: model:mail.template,name:survey_result_mail.mail_template_user_input_result_inline +msgid "Survey: Results (inline)" +msgstr "" + +#. module: survey_result_mail +#: model:mail.template,name:survey_result_mail.mail_template_user_input_result_report +msgid "Survey: Results (with report)" +msgstr "" + +#. module: survey_result_mail +#: model:ir.model.fields,help:survey_result_mail.field_survey_survey__send_result_mail +msgid "" +"When the survey is submitted, an email will be sent to the user withthe " +"answers" +msgstr "" diff --git a/survey_result_mail/migrations/17.0.1.0.0/noupdate_changes.xml b/survey_result_mail/migrations/17.0.1.0.0/noupdate_changes.xml new file mode 100644 index 00000000..0c6c506c --- /dev/null +++ b/survey_result_mail/migrations/17.0.1.0.0/noupdate_changes.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/survey_result_mail/migrations/17.0.1.0.0/post-migration.py b/survey_result_mail/migrations/17.0.1.0.0/post-migration.py new file mode 100644 index 00000000..f2da8983 --- /dev/null +++ b/survey_result_mail/migrations/17.0.1.0.0/post-migration.py @@ -0,0 +1,10 @@ +# Copyright 2025 Tecnativa - Pilar Vargas +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data( + env, "survey_result_mail", "migrations/17.0.1.0.0/noupdate_changes.xml" + ) diff --git a/survey_result_mail/models/__init__.py b/survey_result_mail/models/__init__.py new file mode 100644 index 00000000..8db4d71b --- /dev/null +++ b/survey_result_mail/models/__init__.py @@ -0,0 +1,2 @@ +from . import survey_survey +from . import survey_user_input diff --git a/survey_result_mail/models/survey_survey.py b/survey_result_mail/models/survey_survey.py new file mode 100644 index 00000000..a2514e12 --- /dev/null +++ b/survey_result_mail/models/survey_survey.py @@ -0,0 +1,16 @@ +# Copyright 2023 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class SurveySurvey(models.Model): + _inherit = "survey.survey" + + send_result_mail = fields.Boolean( + string="Send survey answers", + help="When the survey is submitted, an email will be sent to the user with" + "the answers", + ) + result_mail_template_id = fields.Many2one( + comodel_name="mail.template", domain="[('model', '=', 'survey.user_input')]" + ) diff --git a/survey_result_mail/models/survey_user_input.py b/survey_result_mail/models/survey_user_input.py new file mode 100644 index 00000000..d07723f5 --- /dev/null +++ b/survey_result_mail/models/survey_user_input.py @@ -0,0 +1,123 @@ +# Copyright 2023 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from markupsafe import Markup + +from odoo import fields, models +from odoo.tools import format_date, format_datetime, is_html_empty + + +class SurveyUserInput(models.Model): + _inherit = "survey.user_input" + + survey_result = fields.Html(compute="_compute_survey_result") + + def _compute_survey_result(self): + mode = self.env.context.get("survey_result_mode", "basic") + for user_input in self: + if mode == "bootstrap": + user_input.survey_result = user_input._render_user_input() + elif mode == "basic": + user_input.survey_result = user_input._build_answers_html() + else: + user_input.survey_result = False + + def _render_user_input(self): + """We're rendering the template results to add them to the pdf report""" + self.ensure_one() + survey_sudo = self.survey_id.sudo() + answer_sudo = self.sudo() + return ( + self.env["ir.ui.view"] + .sudo() + ._render_template( + "survey_result_mail.survey_page_print", + { + "is_html_empty": is_html_empty, + "review": False, + "survey": survey_sudo, + "answer": answer_sudo, + "questions_to_display": answer_sudo._get_print_questions(), + "scoring_display_correction": ( + survey_sudo.scoring_type == "scoring_with_answers" + and answer_sudo + ), + "format_datetime": lambda dt: format_datetime( + self.env, dt, dt_format=False + ), + "format_date": lambda date: format_date(self.env, date), + }, + ) + ) + + def _build_answers_html(self, given_answers=False): + """Basic html formatted answers. Can be used in mail communications and other + html fields without worring about styles""" + + def _answer_element(title, value): + return f"
  • {title}: {value}
  • " + + given_answers = (given_answers or self.user_input_line_ids).filtered( + lambda x: not x.skipped + ) + questions_dict = {} + for answer in given_answers.filtered(lambda x: x.answer_type != "suggestion"): + if answer.answer_type == "date": + value = format_date(self.env, answer.value_date) + elif answer.answer_type == "datetime": + value = format_datetime(self.env, answer.value_datetime) + else: + value = answer[f"value_{answer.answer_type}"] + questions_dict[answer.question_id] = _answer_element( + answer.question_id.title, value + ) + for answer in given_answers.filtered( + lambda x: x.question_id.question_type == "simple_choice" + ): + questions_dict[answer.question_id] = _answer_element( + answer.question_id.title, + answer.suggested_answer_id.value or answer.value_char_box, + ) + multiple_choice_dict = {} + for answer in given_answers.filtered( + lambda x: x.question_id.question_type == "multiple_choice" + ): + multiple_choice_dict.setdefault(answer.question_id, []) + multiple_choice_dict[answer.question_id].append( + answer.suggested_answer_id.value or answer.value_char_box + ) + for question, answers in multiple_choice_dict.items(): + questions_dict[question] = _answer_element( + question.title, " / ".join([x for x in answers if x]) + ) + matrix_dict = {} + for answer in given_answers.filtered( + lambda x: x.question_id.question_type == "matrix" + ): + matrix_dict.setdefault(answer.question_id, {}) + matrix_dict[answer.question_id].setdefault(answer.matrix_row_id, []) + matrix_dict[answer.question_id][answer.matrix_row_id].append( + answer.suggested_answer_id.value or answer.value_char_box + ) + for question, rows in matrix_dict.items(): + questions_dict[question] = f"
  • {question.title}:
      " + for row, answers in rows.items(): + questions_dict[question] += _answer_element( + row.value, " / ".join([x for x in answers if x]) + ) + questions_dict[question] += "
  • " + answers_html = "".join([questions_dict[q] for q in given_answers.question_id]) + return Markup(answers_html) + + def _mark_done(self): + """Send the answers when submitted on the so configured surveys""" + res = super()._mark_done() + for user_input in self.filtered( + lambda x: x.survey_id.send_result_mail and (x.partner_id.email or x.email) + ): + template = self.survey_id.result_mail_template_id or self.env.ref( + "survey_result_mail.mail_template_user_input_result_inline" + ) + template.send_mail( + user_input.id, email_layout_xmlid="mail.mail_notification_light" + ) + return res diff --git a/survey_result_mail/pyproject.toml b/survey_result_mail/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/survey_result_mail/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/survey_result_mail/readme/CONFIGURE.md b/survey_result_mail/readme/CONFIGURE.md new file mode 100644 index 00000000..00ac5aec --- /dev/null +++ b/survey_result_mail/readme/CONFIGURE.md @@ -0,0 +1,9 @@ +To configure automatic result sending to the user: + +1. Go to the survey you want to configure. +2. In the *Options* tab, you'll find a new *Send survey answers* option + that you can set on to send an automatic email with the user's + answers. +3. You can also choose which mail template to send. This module + provides two. One with the answers inlined and another with the + answers attached as pdf report. diff --git a/survey_result_mail/readme/CONTRIBUTORS.md b/survey_result_mail/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..68e9227d --- /dev/null +++ b/survey_result_mail/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Tecnativa](https://www.tecnativa.com) + - David Vidal + - Pilar Vargas diff --git a/survey_result_mail/readme/DESCRIPTION.md b/survey_result_mail/readme/DESCRIPTION.md new file mode 100644 index 00000000..be04b0c9 --- /dev/null +++ b/survey_result_mail/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module extends the functionality of surveys to allow you to send an +automatic email with the survey answers to the filler as soon as the +questionary is completed. diff --git a/survey_result_mail/readme/USAGE.md b/survey_result_mail/readme/USAGE.md new file mode 100644 index 00000000..936b3e9e --- /dev/null +++ b/survey_result_mail/readme/USAGE.md @@ -0,0 +1,4 @@ +To test this module, you can: + +1. Go to a survey an *Test* it. +2. Once finsished a mail with the aswers attached will be sent. diff --git a/survey_result_mail/reports/survey_answer_report.xml b/survey_result_mail/reports/survey_answer_report.xml new file mode 100644 index 00000000..0c63d1c7 --- /dev/null +++ b/survey_result_mail/reports/survey_answer_report.xml @@ -0,0 +1,48 @@ + + + + + + survey.user_input + Survey results + survey_result_mail.survey_result_report_view + qweb-pdf + 'surevey_results.pdf' + 'Survey results - %s' % (object.survey_id.display_name) + + diff --git a/survey_result_mail/static/description/icon.png b/survey_result_mail/static/description/icon.png new file mode 100644 index 00000000..dc2bf859 Binary files /dev/null and b/survey_result_mail/static/description/icon.png differ diff --git a/survey_result_mail/static/description/index.html b/survey_result_mail/static/description/index.html new file mode 100644 index 00000000..29650c30 --- /dev/null +++ b/survey_result_mail/static/description/index.html @@ -0,0 +1,454 @@ + + + + + +Survey Result Mail + + + +
    +

    Survey Result Mail

    + + +

    Beta License: AGPL-3 OCA/survey Translate me on Weblate Try me on Runboat

    +

    This module extends the functionality of surveys to allow you to send an +automatic email with the survey answers to the filler as soon as the +questionary is completed.

    +

    Table of contents

    + +
    +

    Configuration

    +

    To configure automatic result sending to the user:

    +
      +
    1. Go to the survey you want to configure.
    2. +
    3. In the Options tab, you’ll find a new Send survey answers option +that you can set on to send an automatic email with the user’s +answers.
    4. +
    5. You can also choose which mail template to send. This module provides +two. One with the answers inlined and another with the answers +attached as pdf report.
    6. +
    +
    +
    +

    Usage

    +

    To test this module, you can:

    +
      +
    1. Go to a survey an Test it.
    2. +
    3. Once finsished a mail with the aswers attached will be sent.
    4. +
    +
    +
    +

    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 to smash it by providing a detailed and welcomed +feedback.

    +

    Do not contact contributors directly about support or help with technical issues.

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Tecnativa
    • +
    +
    +
    +

    Contributors

    + +
    +
    +

    Maintainers

    +

    This module is maintained by the OCA.

    + +Odoo Community Association + +

    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.

    +

    Current maintainer:

    +

    pilarvargas-tecnativa

    +

    This module is part of the OCA/survey project on GitHub.

    +

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    +
    +
    +
    + + diff --git a/survey_result_mail/templates/survey_answer_templates.xml b/survey_result_mail/templates/survey_answer_templates.xml new file mode 100644 index 00000000..316c1a75 --- /dev/null +++ b/survey_result_mail/templates/survey_answer_templates.xml @@ -0,0 +1,404 @@ + + + + Survey answers for print + + primary + 999 + + + web.layout + + + False + + + + + + + + + + + + + + + + + + + + + survey_result_mail.question_date + + + survey_result_mail.question_datetime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/survey_result_mail/tests/__init__.py b/survey_result_mail/tests/__init__.py new file mode 100644 index 00000000..a250b23b --- /dev/null +++ b/survey_result_mail/tests/__init__.py @@ -0,0 +1 @@ +from . import test_survey_result_mail diff --git a/survey_result_mail/tests/test_survey_result_mail.py b/survey_result_mail/tests/test_survey_result_mail.py new file mode 100644 index 00000000..5772df0d --- /dev/null +++ b/survey_result_mail/tests/test_survey_result_mail.py @@ -0,0 +1,313 @@ +# Copyright 2023 Tecnativa - David Vidal +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import datetime +from unittest.mock import patch + +from odoo.tests import tagged + +from odoo.addons.survey.tests.common import TestSurveyCommon + + +@tagged("-at_install", "post_install", "functional") +class TestSurveyResultMail(TestSurveyCommon): + def test_certification_auto_sending(self): + survey = self.env["survey.survey"].create( + { + "title": "Test Survey Resul Mail", + "access_mode": "public", + "users_login_required": True, + "send_result_mail": True, + } + ) + q_01 = self._add_question( + None, + "2+2", + "simple_choice", + sequence=1, + constr_mandatory=True, + constr_error_msg="Please select an answer", + survey_id=survey.id, + labels=[ + {"value": "2"}, + {"value": "3"}, + {"value": "4"}, + {"value": "5"}, + ], + ) + q_02 = self._add_question( + None, + "2x2", + "simple_choice", + sequence=2, + constr_mandatory=True, + constr_error_msg="Please select an answer", + survey_id=survey.id, + labels=[ + {"value": "2"}, + {"value": "3"}, + {"value": "4"}, + {"value": "5"}, + ], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q_01, answer, q_01.suggested_answer_ids[2].id) + self._add_answer_line(q_02, answer, q_02.suggested_answer_ids[2].id) + answer.with_user(self.env.user).write({"state": "done"}) + answer._mark_done() + # Verify that the result has been sent automatically. + mail = self.env["mail.message"].search( + [ + ("res_id", "=", answer.id), + ("model", "=", "survey.user_input"), + ("subject", "ilike", "Results for"), + ] + ) + self.assertTrue(mail) + + # --- shared helper --- + + def _make_result_survey(self, title, extra_vals=None): + vals = { + "title": title, + "access_mode": "public", + "users_login_required": True, + "send_result_mail": True, + } + if extra_vals: + vals.update(extra_vals) + return self.env["survey.survey"].create(vals) + + # --- _mark_done branches --- + + def test_mark_done_no_send_result_mail(self): + survey = self._make_result_survey( + "No Send", extra_vals={"send_result_mail": False} + ) + q = self._add_question( + None, + "Q1", + "simple_choice", + sequence=1, + survey_id=survey.id, + labels=[{"value": "A"}, {"value": "B"}], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q, answer, q.suggested_answer_ids[0].id) + answer.write({"state": "done"}) + answer._mark_done() + mail = self.env["mail.message"].search( + [ + ("res_id", "=", answer.id), + ("model", "=", "survey.user_input"), + ("subject", "ilike", "Results for"), + ] + ) + self.assertFalse(mail) + + def test_mark_done_no_email(self): + survey = self._make_result_survey("No Email Survey") + q = self._add_question( + None, + "Q1", + "simple_choice", + sequence=1, + survey_id=survey.id, + labels=[{"value": "A"}, {"value": "B"}], + ) + # partner=False, email=False → filtered out in _mark_done + answer = self._add_answer(survey, False) + self._add_answer_line(q, answer, q.suggested_answer_ids[0].id) + answer.write({"state": "done"}) + answer._mark_done() + mail = self.env["mail.message"].search( + [ + ("res_id", "=", answer.id), + ("model", "=", "survey.user_input"), + ("subject", "ilike", "Results for"), + ] + ) + self.assertFalse(mail) + + def test_mark_done_custom_template(self): + model_id = ( + self.env["ir.model"] + .search([("model", "=", "survey.user_input")], limit=1) + .id + ) + custom_template = self.env["mail.template"].create( + { + "name": "Custom Survey Results", + "model_id": model_id, + "subject": "Custom Results for {{ object.survey_id.title }}", + "body_html": "

    Your custom results

    ", + "email_to": "{{ object.partner_id.email or object.email or '' }}", + } + ) + survey = self._make_result_survey( + "Custom Template Survey", + extra_vals={"result_mail_template_id": custom_template.id}, + ) + q = self._add_question( + None, + "Q1", + "simple_choice", + sequence=1, + survey_id=survey.id, + labels=[{"value": "A"}, {"value": "B"}], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q, answer, q.suggested_answer_ids[0].id) + answer.write({"state": "done"}) + answer._mark_done() + mail = self.env["mail.message"].search( + [ + ("res_id", "=", answer.id), + ("model", "=", "survey.user_input"), + ("subject", "ilike", "Custom Results for"), + ] + ) + self.assertTrue(mail) + + # --- _build_answers_html branches --- + + def test_build_answers_html_char_and_simple_choice(self): + survey = self._make_result_survey("Build Answers HTML") + q_char = self._add_question( + None, "Full Name", "char_box", sequence=1, survey_id=survey.id + ) + q_sc = self._add_question( + None, + "Favorite color", + "simple_choice", + sequence=2, + survey_id=survey.id, + labels=[{"value": "Red"}, {"value": "Blue"}], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q_char, answer, "Alice") + self._add_answer_line(q_sc, answer, q_sc.suggested_answer_ids[0].id) + html = answer._build_answers_html() + self.assertIn("Alice", html) + self.assertIn("Red", html) + + def test_build_answers_html_date_datetime(self): + survey = self._make_result_survey("Date Datetime HTML") + q_date = self._add_question( + None, "Survey date", "date", sequence=1, survey_id=survey.id + ) + q_dt = self._add_question( + None, "Exact moment", "datetime", sequence=2, survey_id=survey.id + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q_date, answer, datetime.date(2024, 6, 15)) + self._add_answer_line(q_dt, answer, datetime.datetime(2024, 6, 15, 10, 30, 0)) + html = answer._build_answers_html() + # Both question titles must appear; confirms date/datetime code paths ran + self.assertIn("Survey date", html) + self.assertIn("Exact moment", html) + + def test_build_answers_html_multiple_choice(self): + survey = self._make_result_survey("Multiple Choice HTML") + q_mc = self._add_question( + None, + "Languages", + "multiple_choice", + sequence=1, + survey_id=survey.id, + labels=[ + {"value": "Python"}, + {"value": "JavaScript"}, + {"value": "Go"}, + ], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q_mc, answer, q_mc.suggested_answer_ids[0].id) + self._add_answer_line(q_mc, answer, q_mc.suggested_answer_ids[1].id) + html = answer._build_answers_html() + self.assertIn("Python", html) + self.assertIn("JavaScript", html) + + def test_build_answers_html_matrix(self): + survey = self._make_result_survey("Matrix HTML") + q_mx = self._add_question( + None, + "Harvest season", + "matrix", + sequence=1, + survey_id=survey.id, + labels=[{"value": "Spring"}, {"value": "Summer"}], + labels_2=[{"value": "Apples"}, {"value": "Strawberries"}], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line( + q_mx, + answer, + q_mx.suggested_answer_ids[0].id, + answer_value_row=q_mx.matrix_row_ids[0].id, + ) + html = answer._build_answers_html() + self.assertIn("Harvest season", html) + self.assertIn("Apples", html) + self.assertIn("Spring", html) + + def test_build_answers_html_skipped_excluded(self): + survey = self._make_result_survey("Skipped HTML") + q_answered = self._add_question( + None, "Answered question", "char_box", sequence=1, survey_id=survey.id + ) + q_skipped = self._add_question( + None, "Skipped question", "char_box", sequence=2, survey_id=survey.id + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q_answered, answer, "Hello") + # answer_type=False required: constraint rejects skipped=True + any answer_type + self._add_answer_line(q_skipped, answer, False, answer_type=False, skipped=True) + html = answer._build_answers_html() + self.assertIn("Hello", html) + self.assertNotIn("Skipped question", html) + + # --- _compute_survey_result modes --- + + def test_compute_survey_result_basic_mode(self): + survey = self._make_result_survey("Basic Mode") + q = self._add_question( + None, "Feeling today", "char_box", sequence=1, survey_id=survey.id + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q, answer, "Great") + html = answer.with_context(survey_result_mode="basic").survey_result + self.assertIn("Great", html) + + def test_compute_survey_result_bootstrap_mode(self): + # Mock ir.ui.view._render_template (not _render_user_input) so the full + # body of _render_user_input is executed and covered, while avoiding the + # web.layout render that requires an HTTP request context. + survey = self._make_result_survey("Bootstrap Mode") + q = self._add_question( + None, + "Pick one", + "simple_choice", + sequence=1, + survey_id=survey.id, + labels=[{"value": "Option A"}, {"value": "Option B"}], + ) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q, answer, q.suggested_answer_ids[0].id) + answer.write({"state": "done"}) + with patch.object( + type(self.env["ir.ui.view"]), + "_render_template", + return_value="

    bootstrap rendered

    ", + ): + html = answer.with_context(survey_result_mode="bootstrap").survey_result + self.assertTrue(html) + + def test_compute_survey_result_unknown_mode(self): + # covers: elif mode == "basic" → False branch (mode is neither bootstrap + # nor basic — both if/elif evaluate to False, survey_result stays empty) + survey = self._make_result_survey("Unknown Mode") + q = self._add_question(None, "Q1", "char_box", sequence=1, survey_id=survey.id) + answer = self._add_answer(survey, self.env.user) + self._add_answer_line(q, answer, "something") + html = answer.with_context(survey_result_mode="unknown").survey_result + self.assertFalse(html) diff --git a/survey_result_mail/views/survey_survey_views.xml b/survey_result_mail/views/survey_survey_views.xml new file mode 100644 index 00000000..16e789b0 --- /dev/null +++ b/survey_result_mail/views/survey_survey_views.xml @@ -0,0 +1,16 @@ + + + + survey.survey + + + + + + + + + diff --git a/survey_result_mail/views/survey_user_input_views.xml b/survey_result_mail/views/survey_user_input_views.xml new file mode 100644 index 00000000..a65732a6 --- /dev/null +++ b/survey_result_mail/views/survey_user_input_views.xml @@ -0,0 +1,12 @@ + + + + survey.user_input + + + + + + + +