Repository: OCA/survey
Pull request #170: [17.0][MIG] survey_certification_sending: Migration to version 17.0
Author: login: pilarvargas-tecnativa
Labels: name: approved
name: merged 🎉
Created: 2025-05-14T16:42:22Z | Merged: 2025-05-17T09:32:39Z

@tecnativa TT56327

@carlos-lopez-tecnativa @juancarlosonate-tecnativa please review


Diff:
diff --git a/survey_certification_sending/README.rst b/survey_certification_sending/README.rst
new file mode 100644
index 00000000..90ba3ce0
--- /dev/null
+++ b/survey_certification_sending/README.rst
@@ -0,0 +1,106 @@
+============================
+Survey Certification Sending
+============================
+
+.. 
+   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   !! This file is generated by oca-gen-addon-readme !!
+   !! changes will be overwritten.                   !!
+   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+   !! source digest: sha256:8ea8829eaa6afec788b8dd41ab8bdc3a46a0f15d833bcc75a3feaccea4f1def7
+   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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/17.0/survey_certification_sending
+    :alt: OCA/survey
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+    :target: https://translation.odoo-community.org/projects/survey-17-0/survey-17-0-survey_certification_sending
+    :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=17.0
+    :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module extends the Odoo Survey application to allow automatic and
+manual sending of certifications when a participant successfully
+completes a survey.
+
+**Table of contents**
+
+.. contents::
+   :local:
+
+Usage
+=====
+
+To use this module, you need to:
+
+1. Go to Survey > Surveys.
+2. Create or edit a certification survey.
+3. Configure whether you want to send certifications manually by
+   checking the "Skip Auto Email" option.
+4. Participants who pass the survey will receive their certification
+   automatically if manual submission is not enabled.
+5. If the automatic sending is disabled, passed certifications can be
+   submitted manually from the "Send certification email" action.
+6. Certifications that have already been sent will be marked as
+   "Certification sent".
+7. Passed certifications can be sent as many times as necessary.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues <https://github.com/OCA/survey/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 <https://github.com/OCA/survey/issues/new?body=module:%20survey_certification_sending%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+-------
+
+* Tecnativa
+
+Contributors
+------------
+
+-  `Tecnativa <https://www.tecnativa.com>`__
+
+   -  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 <https://odoo-community.org/page/maintainer-role>`__:
+
+|maintainer-pilarvargas-tecnativa| 
+
+This module is part of the `OCA/survey <https://github.com/OCA/survey/tree/17.0/survey_certification_sending>`_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/survey_certification_sending/__init__.py b/survey_certification_sending/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/survey_certification_sending/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/survey_certification_sending/__manifest__.py b/survey_certification_sending/__manifest__.py
new file mode 100644
index 00000000..70265b0c
--- /dev/null
+++ b/survey_certification_sending/__manifest__.py
@@ -0,0 +1,20 @@
+# Copyright 2025 Tecnativa - Pilar Vargas
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+{
+    "name": "Survey Certification Sending",
+    "summary": "Controls the automatic sending of certifications in surveys.",
+    "version": "17.0.1.0.0",
+    "author": "Tecnativa, Odoo Community Association (OCA)",
+    "development_status": "Beta",
+    "category": "Marketing/Survey",
+    "website": "https://github.com/OCA/survey",
+    "maintainers": ["pilarvargas-tecnativa"],
+    "license": "AGPL-3",
+    "depends": ["survey"],
+    "data": [
+        "views/survey_survey_views.xml",
+        "views/survey_templates.xml",
+        "views/survey_user_views.xml",
+    ],
+    "installable": True,
+}
diff --git a/survey_certification_sending/i18n/es.po b/survey_certification_sending/i18n/es.po
new file mode 100644
index 00000000..4fd310db
--- /dev/null
+++ b/survey_certification_sending/i18n/es.po
@@ -0,0 +1,82 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# 	* survey_certification_sending
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 15.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-02-21 06:59+0000\n"
+"PO-Revision-Date: 2025-02-21 08:01+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.2\n"
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#, python-format
+msgid "%s certification(s) successfully sent."
+msgstr "%s certificación(es) enviada(s) con éxito."
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#: model:ir.model.fields,field_description:survey_certification_sending.field_survey_user_input__certification_sent
+#, python-format
+msgid "Certification Sent"
+msgstr "Certificación enviada"
+
+#. module: survey_certification_sending
+#: model:ir.model,name:survey_certification_sending.model_mail_template
+msgid "Email Templates"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model.fields,help:survey_certification_sending.field_survey_user_input__certification_sent
+msgid "It indicates if the certification has been sent."
+msgstr "Indica si la certificación ha sido enviada."
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#, python-format
+msgid "No Certifications Sent"
+msgstr "Certificaciones no enviadas"
+
+#. module: survey_certification_sending
+#: model:ir.actions.server,name:survey_certification_sending.ir_actions_server_send_certification
+msgid "Send Certification Email"
+msgstr "Enviar certificación por correo electrónico"
+
+#. module: survey_certification_sending
+#: model:ir.model.fields,field_description:survey_certification_sending.field_survey_survey__skip_certification_email
+msgid "Skip Certification Email"
+msgstr "Omitir correo de certificación"
+
+#. module: survey_certification_sending
+#: model:ir.model.fields,help:survey_certification_sending.field_survey_survey__skip_certification_email
+msgid ""
+"Skip sending the certification automatically after successful completion of "
+"the survey."
+msgstr ""
+"Omitir el envío automático de la certificación una vez completada con éxito "
+"la la encuesta."
+
+#. module: survey_certification_sending
+#: model:ir.model,name:survey_certification_sending.model_survey_survey
+msgid "Survey"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model,name:survey_certification_sending.model_survey_user_input
+msgid "Survey User Input"
+msgstr ""
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#, python-format
+msgid "The survey does not meet the conditions."
+msgstr "La encuesta no cumple las condiciones."
diff --git a/survey_certification_sending/i18n/survey_certification_sending.pot b/survey_certification_sending/i18n/survey_certification_sending.pot
new file mode 100644
index 00000000..5378b8e1
--- /dev/null
+++ b/survey_certification_sending/i18n/survey_certification_sending.pot
@@ -0,0 +1,76 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# 	* survey_certification_sending
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 15.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_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#, python-format
+msgid "%s certification(s) successfully sent."
+msgstr ""
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#: model:ir.model.fields,field_description:survey_certification_sending.field_survey_user_input__certification_sent
+#, python-format
+msgid "Certification Sent"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model,name:survey_certification_sending.model_mail_template
+msgid "Email Templates"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model.fields,help:survey_certification_sending.field_survey_user_input__certification_sent
+msgid "It indicates if the certification has been sent."
+msgstr ""
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#, python-format
+msgid "No Certifications Sent"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.actions.server,name:survey_certification_sending.ir_actions_server_send_certification
+msgid "Send Certification Email"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model.fields,field_description:survey_certification_sending.field_survey_survey__skip_certification_email
+msgid "Skip Certification Email"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model.fields,help:survey_certification_sending.field_survey_survey__skip_certification_email
+msgid ""
+"Skip sending the certification automatically after successful completion of "
+"the survey."
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model,name:survey_certification_sending.model_survey_survey
+msgid "Survey"
+msgstr ""
+
+#. module: survey_certification_sending
+#: model:ir.model,name:survey_certification_sending.model_survey_user_input
+msgid "Survey User Input"
+msgstr ""
+
+#. module: survey_certification_sending
+#: code:addons/survey_certification_sending/models/survey_user_input.py:0
+#, python-format
+msgid "The survey does not meet the conditions."
+msgstr ""
diff --git a/survey_certification_sending/models/__init__.py b/survey_certification_sending/models/__init__.py
new file mode 100644
index 00000000..2cff3554
--- /dev/null
+++ b/survey_certification_sending/models/__init__.py
@@ -0,0 +1,3 @@
+from . import mail_template
+from . import survey_survey
+from . import survey_user_input
diff --git a/survey_certification_sending/models/mail_template.py b/survey_certification_sending/models/mail_template.py
new file mode 100644
index 00000000..b7bff695
--- /dev/null
+++ b/survey_certification_sending/models/mail_template.py
@@ -0,0 +1,24 @@
+# Copyright 2025 Tecnativa - Pilar Vargas
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from odoo import models
+
+
+class MailTemplate(models.Model):
+    _inherit = "mail.template"
+
+    def send_mail(
+        self,
+        res_id,
+        force_send=False,
+        raise_exception=False,
+        email_values=None,
+        email_layout_xmlid=False,
+    ):
+        if self.model == "survey.user_input":
+            skip_ids = self.env.context.get("skip_certification_email_ids", [])
+            survey = self.env["survey.user_input"].browse(res_id).survey_id
+            if survey.id in skip_ids:
+                return False
+        return super().send_mail(
+            res_id, force_send, raise_exception, email_values, email_layout_xmlid
+        )
diff --git a/survey_certification_sending/models/survey_survey.py b/survey_certification_sending/models/survey_survey.py
new file mode 100644
index 00000000..87f0973e
--- /dev/null
+++ b/survey_certification_sending/models/survey_survey.py
@@ -0,0 +1,12 @@
+# Copyright 2025 Tecnativa - Pilar Vargas
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from odoo import fields, models
+
+
+class Survey(models.Model):
+    _inherit = "survey.survey"
+
+    skip_certification_email = fields.Boolean(
+        help="Skip sending the certification automatically after successful completion "
+        "of the survey."
+    )
diff --git a/survey_certification_sending/models/survey_user_input.py b/survey_certification_sending/models/survey_user_input.py
new file mode 100644
index 00000000..60cdd073
--- /dev/null
+++ b/survey_certification_sending/models/survey_user_input.py
@@ -0,0 +1,68 @@
+# Copyright 2025 Tecnativa - Pilar Vargas
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from odoo import _, fields, models
+
+
+class SurveyUserInput(models.Model):
+    _inherit = "survey.user_input"
+
+    certification_sent = fields.Boolean(
+        default=False, help="It indicates if the certification has been sent."
+    )
+
+    def _mark_done(self):
+        for user_input in self:
+            # The response is marked as sent in anticipation that it will be sent
+            # safely.
+            if (
+                user_input.survey_id.certification
+                and user_input.scoring_success
+                and not user_input.survey_id.skip_certification_email
+                and user_input.survey_id.certification_mail_template_id
+                and not user_input.test_entry
+            ):
+                user_input.certification_sent = True
+        # Add ids of surveys that have to skip automatic sending to the context
+        return super(
+            SurveyUserInput,
+            self.with_context(
+                skip_certification_email_ids=self.mapped("survey_id")
+                .filtered(lambda s: s.skip_certification_email)
+                .ids
+            ),
+        )._mark_done()
+
+    def action_manual_send_certification(self):
+        # Send certifications manually only to those who passed the survey.
+        sent_count = 0
+        for user_input in self:
+            if (
+                user_input.survey_id.certification
+                and user_input.scoring_success
+                and not user_input.test_entry
+            ):
+                template = user_input.survey_id.certification_mail_template_id
+                if template:
+                    template.send_mail(
+                        user_input.id, email_layout_xmlid="mail.mail_notification_light"
+                    )
+                    user_input.certification_sent = True
+                    sent_count += 1
+        if sent_count:
+            title = _("Certification Sent")
+            message = _("%s certification(s) successfully sent.") % sent_count
+            notif_type = "success"
+        else:
+            title = _("No Certifications Sent")
+            message = _("The survey does not meet the conditions.")
+            notif_type = "warning"
+        return {
+            "type": "ir.actions.client",
+            "tag": "display_notification",
+            "params": {
+                "title": title,
+                "message": message,
+                "type": notif_type,
+                "sticky": False,
+            },
+        }
diff --git a/survey_certification_sending/pyproject.toml b/survey_certification_sending/pyproject.toml
new file mode 100644
index 00000000..4231d0cc
--- /dev/null
+++ b/survey_certification_sending/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/survey_certification_sending/readme/CONTRIBUTORS.md b/survey_certification_sending/readme/CONTRIBUTORS.md
new file mode 100644
index 00000000..68e9227d
--- /dev/null
+++ b/survey_certification_sending/readme/CONTRIBUTORS.md
@@ -0,0 +1,3 @@
+- [Tecnativa](https://www.tecnativa.com)
+  - David Vidal
+  - Pilar Vargas
diff --git a/survey_certification_sending/readme/DESCRIPTION.md b/survey_certification_sending/readme/DESCRIPTION.md
new file mode 100644
index 00000000..b377912b
--- /dev/null
+++ b/survey_certification_sending/readme/DESCRIPTION.md
@@ -0,0 +1,3 @@
+This module extends the Odoo Survey application to allow automatic and
+manual sending of certifications when a participant successfully
+completes a survey.
diff --git a/survey_certification_sending/readme/USAGE.md b/survey_certification_sending/readme/USAGE.md
new file mode 100644
index 00000000..a5744530
--- /dev/null
+++ b/survey_certification_sending/readme/USAGE.md
@@ -0,0 +1,13 @@
+To use this module, you need to:
+
+1.  Go to Survey \> Surveys.
+2.  Create or edit a certification survey.
+3.  Configure whether you want to send certifications manually by
+    checking the "Skip Auto Email" option.
+4.  Participants who pass the survey will receive their certification
+    automatically if manual submission is not enabled.
+5.  If the automatic sending is disabled, passed certifications can be
+    submitted manually from the "Send certification email" action.
+6.  Certifications that have already been sent will be marked as
+    "Certification sent".
+7.  Passed certifications can be sent as many times as necessary.
diff --git a/survey_certification_sending/static/description/icon.png b/survey_certification_sending/static/description/icon.png
new file mode 100644
index 00000000..1dcc49c2
Binary files /dev/null and b/survey_certification_sending/static/description/icon.png differ
diff --git a/survey_certification_sending/static/description/index.html b/survey_certification_sending/static/description/index.html
new file mode 100644
index 00000000..55696ba7
--- /dev/null
+++ b/survey_certification_sending/static/description/index.html
@@ -0,0 +1,449 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
+<title>Survey Certification Sending</title>
+<style type="text/css">
+
+/*
+:Author: David Goodger (goodger@python.org)
+:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+Despite the name, some widely supported CSS2 features are used.
+
+See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+  border: 0 }
+
+table.borderless td, table.borderless th {
+  /* Override padding for "table.docutils td" with "! important".
+     The right padding separates the table cells. */
+  padding: 0 0.5em 0 0 ! important }
+
+.first {
+  /* Override more specific margin styles with "! important". */
+  margin-top: 0 ! important }
+
+.last, .with-subtitle {
+  margin-bottom: 0 ! important }
+
+.hidden {
+  display: none }
+
+.subscript {
+  vertical-align: sub;
+  font-size: smaller }
+
+.superscript {
+  vertical-align: super;
+  font-size: smaller }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: black }
+
+blockquote.epigraph {
+  margin: 2em 5em ; }
+
+dl.docutils dd {
+  margin-bottom: 0.5em }
+
+object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
+  overflow: hidden;
+}
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+  font-weight: bold }
+*/
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title, .code .error {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+   compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+  margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+  margin-top: 0.5em }
+*/
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.figure {
+  margin-left: 2em ;
+  margin-right: 2em }
+
+div.footer, div.header {
+  clear: both;
+  font-size: smaller }
+
+div.line-block {
+  display: block ;
+  margin-top: 1em ;
+  margin-bottom: 1em }
+
+div.line-block div.line-block {
+  margin-top: 0 ;
+  margin-bottom: 0 ;
+  margin-left: 1.5em }
+
+div.sidebar {
+  margin: 0 0 0.5em 1em ;
+  border: medium outset ;
+  padding: 1em ;
+  background-color: #ffffee ;
+  width: 40% ;
+  float: right ;
+  clear: right }
+
+div.sidebar p.rubric {
+  font-family: sans-serif ;
+  font-size: medium }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+  margin-top: 0.4em }
+
+h1.title {
+  text-align: center }
+
+h2.subtitle {
+  text-align: center }
+
+hr.docutils {
+  width: 75% }
+
+img.align-left, .figure.align-left, object.align-left, table.align-left {
+  clear: left ;
+  float: left ;
+  margin-right: 1em }
+
+img.align-right, .figure.align-right, object.align-right, table.align-right {
+  clear: right ;
+  float: right ;
+  margin-left: 1em }
+
+img.align-center, .figure.align-center, object.align-center {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+table.align-center {
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.align-left {
+  text-align: left }
+
+.align-center {
+  clear: both ;
+  text-align: center }
+
+.align-right {
+  text-align: right }
+
+/* reset inner alignment in figures */
+div.align-right {
+  text-align: inherit }
+
+/* div.align-center * { */
+/*   text-align: left } */
+
+.align-top    {
+  vertical-align: top }
+
+.align-middle {
+  vertical-align: middle }
+
+.align-bottom {
+  vertical-align: bottom }
+
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+p.attribution {
+  text-align: right ;
+  margin-left: 50% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.rubric {
+  font-weight: bold ;
+  font-size: larger ;
+  color: maroon ;
+  text-align: center }
+
+p.sidebar-title {
+  font-family: sans-serif ;
+  font-weight: bold ;
+  font-size: larger }
+
+p.sidebar-subtitle {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font: inherit }
+
+pre.literal-block, pre.doctest-block, pre.math, pre.code {
+  margin-left: 2em ;
+  margin-right: 2em }
+
+pre.code .ln { color: gray; } /* line numbers */
+pre.code, code { background-color: #eeeeee }
+pre.code .comment, code .comment { color: #5C6576 }
+pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
+pre.code .literal.string, code .literal.string { color: #0C5404 }
+pre.code .name.builtin, code .name.builtin { color: #352B84 }
+pre.code .deleted, code .deleted { background-color: #DEB0A1}
+pre.code .inserted, code .inserted { background-color: #A3D289}
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.pre {
+  white-space: pre }
+
+span.problematic, pre.problematic {
+  color: red }
+
+span.section-subtitle {
+  /* font-size relative to parent (h1..h6 element) */
+  font-size: 80% }
+
+table.citation {
+  border-left: solid 1px gray;
+  margin-left: 1px }
+
+table.docinfo {
+  margin: 2em 4em }
+
+table.docutils {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.footnote {
+  border-left: solid 1px black;
+  margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+  font-weight: bold ;
+  text-align: left ;
+  white-space: nowrap ;
+  padding-left: 0 }
+
+/* "booktabs" style (no vertical lines) */
+table.docutils.booktabs {
+  border: 0px;
+  border-top: 2px solid;
+  border-bottom: 2px solid;
+  border-collapse: collapse;
+}
+table.docutils.booktabs * {
+  border: 0px;
+}
+table.docutils.booktabs th {
+  border-bottom: thin solid;
+  text-align: left;
+}
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+  font-size: 100% }
+
+ul.auto-toc {
+  list-style-type: none }
+
+</style>
+</head>
+<body>
+<div class="document" id="survey-certification-sending">
+<h1 class="title">Survey Certification Sending</h1>
+
+<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!! This file is generated by oca-gen-addon-readme !!
+!! changes will be overwritten.                   !!
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+!! source digest: sha256:8ea8829eaa6afec788b8dd41ab8bdc3a46a0f15d833bcc75a3feaccea4f1def7
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
+<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/survey/tree/17.0/survey_certification_sending"><img alt="OCA/survey" src="https://img.shields.io/badge/github-OCA%2Fsurvey-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/survey-17-0/survey-17-0-survey_certification_sending"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/survey&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
+<p>This module extends the Odoo Survey application to allow automatic and
+manual sending of certifications when a participant successfully
+completes a survey.</p>
+<p><strong>Table of contents</strong></p>
+<div class="contents local topic" id="contents">
+<ul class="simple">
+<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
+<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
+<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
+<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
+<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
+<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="usage">
+<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
+<p>To use this module, you need to:</p>
+<ol class="arabic simple">
+<li>Go to Survey &gt; Surveys.</li>
+<li>Create or edit a certification survey.</li>
+<li>Configure whether you want to send certifications manually by
+checking the “Skip Auto Email” option.</li>
+<li>Participants who pass the survey will receive their certification
+automatically if manual submission is not enabled.</li>
+<li>If the automatic sending is disabled, passed certifications can be
+submitted manually from the “Send certification email” action.</li>
+<li>Certifications that have already been sent will be marked as
+“Certification sent”.</li>
+<li>Passed certifications can be sent as many times as necessary.</li>
+</ol>
+</div>
+<div class="section" id="bug-tracker">
+<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
+<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/survey/issues">GitHub Issues</a>.
+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
+<a class="reference external" href="https://github.com/OCA/survey/issues/new?body=module:%20survey_certification_sending%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
+<p>Do not contact contributors directly about support or help with technical issues.</p>
+</div>
+<div class="section" id="credits">
+<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
+<div class="section" id="authors">
+<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
+<ul class="simple">
+<li>Tecnativa</li>
+</ul>
+</div>
+<div class="section" id="contributors">
+<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
+<ul class="simple">
+<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a><ul>
+<li>David Vidal</li>
+<li>Pilar Vargas</li>
+</ul>
+</li>
+</ul>
+</div>
+<div class="section" id="maintainers">
+<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
+<p>This module is maintained by the OCA.</p>
+<a class="reference external image-reference" href="https://odoo-community.org">
+<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
+</a>
+<p>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.</p>
+<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
+<p><a class="reference external image-reference" href="https://github.com/pilarvargas-tecnativa"><img alt="pilarvargas-tecnativa" src="https://github.com/pilarvargas-tecnativa.png?size=40px" /></a></p>
+<p>This module is part of the <a class="reference external" href="https://github.com/OCA/survey/tree/17.0/survey_certification_sending">OCA/survey</a> project on GitHub.</p>
+<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/survey_certification_sending/tests/__init__.py b/survey_certification_sending/tests/__init__.py
new file mode 100644
index 00000000..e2466bc3
--- /dev/null
+++ b/survey_certification_sending/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_survey_certification_sending
diff --git a/survey_certification_sending/tests/test_survey_certification_sending.py b/survey_certification_sending/tests/test_survey_certification_sending.py
new file mode 100644
index 00000000..85995a16
--- /dev/null
+++ b/survey_certification_sending/tests/test_survey_certification_sending.py
@@ -0,0 +1,185 @@
+from odoo.tests import tagged
+
+from odoo.addons.survey.tests.common import TestSurveyCommon
+
+
+@tagged("-at_install", "post_install", "functional")
+class TestCertificationsending(TestSurveyCommon):
+    def test_certification_auto_sending(self):
+        test_certification = self.env["survey.survey"].create(
+            {
+                "title": "Test Certification Sending",
+                "access_mode": "public",
+                "users_login_required": True,
+                "questions_layout": "page_per_question",
+                "users_can_go_back": True,
+                "scoring_type": "scoring_with_answers",
+                "scoring_success_min": 85.0,
+                "certification": True,
+                "certification_mail_template_id": self.env.ref(
+                    "survey.mail_template_certification"
+                ).id,
+                "is_time_limited": True,
+                "time_limit": 10,
+            }
+        )
+        q_01 = self._add_question(
+            None,
+            "2+2",
+            "simple_choice",
+            sequence=1,
+            constr_mandatory=True,
+            constr_error_msg="Please select an answer",
+            survey_id=test_certification.id,
+            labels=[
+                {"value": "2"},
+                {"value": "3"},
+                {"value": "4", "is_correct": True, "answer_score": 50.0},
+                {"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=test_certification.id,
+            labels=[
+                {"value": "2"},
+                {"value": "3"},
+                {"value": "4", "is_correct": True, "answer_score": 50.0},
+                {"value": "5"},
+            ],
+        )
+        answer = self._add_answer(test_certification, 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 certification has been sent automatically.
+        self.assertTrue(answer.scoring_success)
+        self.assertTrue(answer.certification_sent)
+
+    def test_certification_skip_auto_sending(self):
+        test_certification = self.env["survey.survey"].create(
+            {
+                "title": "Test Certification Skip Auto Sending",
+                "access_mode": "public",
+                "users_login_required": True,
+                "questions_layout": "page_per_question",
+                "users_can_go_back": True,
+                "scoring_type": "scoring_with_answers",
+                "scoring_success_min": 85.0,
+                "certification": True,
+                "skip_certification_email": True,
+                "certification_mail_template_id": self.env.ref(
+                    "survey.mail_template_certification"
+                ).id,
+                "is_time_limited": True,
+                "time_limit": 10,
+            }
+        )
+        q_01 = self._add_question(
+            None,
+            "2+2",
+            "simple_choice",
+            sequence=1,
+            constr_mandatory=True,
+            constr_error_msg="Please select an answer",
+            survey_id=test_certification.id,
+            labels=[
+                {"value": "2"},
+                {"value": "3"},
+                {"value": "4", "is_correct": True, "answer_score": 50.0},
+                {"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=test_certification.id,
+            labels=[
+                {"value": "2"},
+                {"value": "3"},
+                {"value": "4", "is_correct": True, "answer_score": 50.0},
+                {"value": "5"},
+            ],
+        )
+        answer = self._add_answer(test_certification, 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 certification has not been sent automatically
+        self.assertTrue(answer.scoring_success)
+        self.assertFalse(answer.certification_sent)
+        # Verify that the certification has been sent manually
+        answer.action_manual_send_certification()
+        self.assertTrue(answer.certification_sent)
+
+    def test_no_passed_certification_skip_auto_sending(self):
+        test_certification = self.env["survey.survey"].create(
+            {
+                "title": "Test No Passed Certification Skip Auto Sending",
+                "access_mode": "public",
+                "users_login_required": True,
+                "questions_layout": "page_per_question",
+                "users_can_go_back": True,
+                "scoring_type": "scoring_with_answers",
+                "scoring_success_min": 85.0,
+                "certification": True,
+                "skip_certification_email": True,
+                "certification_mail_template_id": self.env.ref(
+                    "survey.mail_template_certification"
+                ).id,
+                "is_time_limited": True,
+                "time_limit": 10,
+            }
+        )
+        q_01 = self._add_question(
+            None,
+            "2+2",
+            "simple_choice",
+            sequence=1,
+            constr_mandatory=True,
+            constr_error_msg="Please select an answer",
+            survey_id=test_certification.id,
+            labels=[
+                {"value": "2"},
+                {"value": "3"},
+                {"value": "4", "is_correct": True, "answer_score": 50.0},
+                {"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=test_certification.id,
+            labels=[
+                {"value": "2"},
+                {"value": "3"},
+                {"value": "4", "is_correct": True, "answer_score": 50.0},
+                {"value": "5"},
+            ],
+        )
+        answer = self._add_answer(test_certification, self.env.user)
+        self._add_answer_line(q_01, answer, q_01.suggested_answer_ids[0].id)
+        self._add_answer_line(q_02, answer, q_02.suggested_answer_ids[3].id)
+        answer.with_user(self.env.user).write({"state": "done"})
+        answer._mark_done()
+        # Verify that the certification has not been sent automatically
+        self.assertFalse(answer.scoring_success)
+        self.assertFalse(answer.certification_sent)
+        # Verify that the certification has not been sent manually
+        answer.action_manual_send_certification()
+        self.assertFalse(answer.certification_sent)
diff --git a/survey_certification_sending/views/survey_survey_views.xml b/survey_certification_sending/views/survey_survey_views.xml
new file mode 100644
index 00000000..4e408612
--- /dev/null
+++ b/survey_certification_sending/views/survey_survey_views.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <record id="survey_form" model="ir.ui.view">
+        <field name="model">survey.survey</field>
+        <field name="inherit_id" ref="survey.survey_survey_view_form" />
+        <field name="arch" type="xml">
+            <field name="certification_mail_template_id" position="before">
+                <field name="skip_certification_email" invisible="not certification" />
+            </field>
+        </field>
+    </record>
+</odoo>
diff --git a/survey_certification_sending/views/survey_templates.xml b/survey_certification_sending/views/survey_templates.xml
new file mode 100644
index 00000000..c79ca64d
--- /dev/null
+++ b/survey_certification_sending/views/survey_templates.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright 2025 Tecnativa - Pilar Vargas
+     License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
+<odoo>
+    <template id="survey_fill_form_done" inherit_id="survey.survey_fill_form_done">
+        <xpath
+            expr="//div[a[contains(@t-att-href, '/survey/%s/get_certification')]]"
+            position="attributes"
+        >
+            <attribute
+                name="t-if"
+            >survey.certification and not survey.skip_certification_email</attribute>
+        </xpath>
+    </template>
+</odoo>
diff --git a/survey_certification_sending/views/survey_user_views.xml b/survey_certification_sending/views/survey_user_views.xml
new file mode 100644
index 00000000..70fd9555
--- /dev/null
+++ b/survey_certification_sending/views/survey_user_views.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <record id="survey_user_input_view_form" model="ir.ui.view">
+        <field name="model">survey.user_input</field>
+        <field name="inherit_id" ref="survey.survey_user_input_view_form" />
+        <field name="arch" type="xml">
+            <field name="scoring_success" position="after">
+                <field
+                    name="certification_sent"
+                    readonly="1"
+                    invisible="scoring_type == 'no_scoring'"
+                />
+            </field>
+        </field>
+    </record>
+
+    <!-- Server action to send multiple certification-->
+    <record id="ir_actions_server_send_certification" model="ir.actions.server">
+        <field name="name">Send Certification Email</field>
+        <field name="type">ir.actions.server</field>
+        <field name="model_id" ref="survey.model_survey_user_input" />
+        <field name="state">code</field>
+        <field name="code">
+            action = records.action_manual_send_certification()
+        </field>
+        <field name="binding_model_id" ref="survey.model_survey_user_input" />
+        <field name="binding_view_types">list,form</field>
+    </record>
+</odoo>


Reviews:
user: login: carlos-lopez-tecnativa
state: CHANGES_REQUESTED
author_association: NONE
submitted_at: 2025-05-15T11:16:05Z
user: login: pilarvargas-tecnativa
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-15T16:26:40Z
user: login: carlos-lopez-tecnativa
body: LGTM
state: APPROVED
author_association: NONE
submitted_at: 2025-05-15T19:23:23Z
user: login: juancarlosonate-tecnativa
state: APPROVED
author_association: NONE
submitted_at: 2025-05-16T10:08:38Z

Review comments:
diff_hunk: @@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <record id="survey_form" model="ir.ui.view">
+        <field name="model">survey.survey</field>
+        <field name="inherit_id" ref="survey.survey_survey_view_form" />
+        <field name="arch" type="xml">
+            <field name="certification" position="after">
+                <field name="skip_certification_email" invisible="not certification" />
+            </field>
path: survey_certification_sending/views/survey_survey_views.xml
user: login: carlos-lopez-tecnativa
body: Please check this XPath. The field is incorrectly positioned and is not displayed correctly.
![image](https://github.com/user-attachments/assets/5feeba2c-c17a-4061-ba3e-7da636328a39)

created_at: 2025-05-15T11:16:00Z
author_association: NONE
diff_hunk: @@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+    <record id="survey_form" model="ir.ui.view">
+        <field name="model">survey.survey</field>
+        <field name="inherit_id" ref="survey.survey_survey_view_form" />
+        <field name="arch" type="xml">
+            <field name="certification" position="after">
+                <field name="skip_certification_email" invisible="not certification" />
+            </field>
path: survey_certification_sending/views/survey_survey_views.xml
user: login: pilarvargas-tecnativa
body: Sorry, the change had not been added to the commit. Now yes, thanks
created_at: 2025-05-15T16:26:40Z
author_association: NONE

PR comments:
user: login: pedrobaeza
created_at: 2025-05-14T17:24:33Z
body: /ocabot migration survey_certification_sending
author_association: MEMBER
user: login: pedrobaeza
created_at: 2025-05-17T09:22:31Z
body: /ocabot merge nobump
author_association: MEMBER
user: login: OCA-git-bot
created_at: 2025-05-17T09:22:39Z
body: What a great day to merge this nice PR. Let's do it!
Prepared branch [17.0-ocabot-merge-pr-170-by-pedrobaeza-bump-nobump](https://github.com/OCA/survey/commits/17.0-ocabot-merge-pr-170-by-pedrobaeza-bump-nobump), awaiting test results.
author_association: CONTRIBUTOR
user: login: OCA-git-bot
created_at: 2025-05-17T09:32:40Z
body: Congratulations, your PR was merged at 0c8bbca54c5cb09f9957e04e1c024f4e9722871b. Thanks a lot for contributing to OCA. ❤️
author_association: CONTRIBUTOR