[NEW] first commit for all modules coming from training-tools

This commit is contained in:
clementthomas
2023-11-21 12:54:02 +01:00
parent 823f04a7b6
commit 61e01e4be0
94 changed files with 3534 additions and 0 deletions

165
.gitignore vendored Normal file
View File

@@ -0,0 +1,165 @@
*.docx
.~*
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

1
survey_base/__init__.py Normal file
View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,26 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey base",
'summary': 'Add fields used by several survey addons',
'description': """
Add fields used by several survey addons
----------------------------------------------------
* Add record reference in survey_question and survey.user_input.line
* Add value_file in survey.user_input.line
* Implementation of theses fields should be in another module
""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["survey"],
"data": [
],
"installable": True,
}

View File

@@ -0,0 +1,2 @@
from . import survey_question_answer
from . import survey_user_input_line

View File

@@ -0,0 +1,19 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyQuestionAnswer(models.Model):
_inherit = 'survey.question.answer'
record_reference = fields.Many2oneReference(model_field="record_reference_model", string="Record")
record_reference_model = fields.Char("Record Model")

View File

@@ -0,0 +1,46 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyUserInputLine(models.Model):
_inherit = 'survey.user_input.line'
#attachment fields
value_file = fields.Binary(string="File")
value_file_fname = fields.Char(string="File Name")
#record reference fields
record_reference = fields.Many2oneReference(model_field="record_reference_model", string="Record")
record_reference_model = fields.Char('Record model')
"""set record_reference when saving survey_user_input line
"""
def set_record_reference_data(self, vals):
if vals.get('answer_type') == "suggestion" and 'suggested_answer_id' in vals:
#find model
answer = self.env['survey.question.answer'].browse(vals['suggested_answer_id'])
vals['record_reference_model'] = answer.record_reference_model
vals['record_reference'] = answer.record_reference
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
self.set_record_reference_data(vals)
return super(SurveyUserInputLine, self).create(vals_list)
def write(self, vals):
self.set_record_reference_data(vals)
return super(SurveyUserInputLine, self).write(vals)

View File

@@ -0,0 +1,109 @@
==========================
Survey contacts generation
==========================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0a298d1600a5f93ffe77357631c7e799e78b23b84c362b126720e36655dd5227
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/15.0/survey_contact_generation
:alt: OCA/survey
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/survey-15-0/survey-15-0-survey_contact_generation
: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=15.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to generate new contacts from surveys answers.
**Table of contents**
.. contents::
:local:
Configuration
=============
To configure the contact generation:
#. Go to the configured survey.
#. In the *Contact* section of the *Options* tab, set
*Generate Contact* on, if you want contacts to be
generated from the answers to this survey.
#. In each question associated with a future new contact,
specify the corresponding contact field. To do this,
go to the 'Options' tab, then navigate to the 'Contact' group,
and select the 'Contact field' field.
Usage
=====
if the survey is properly configured, once it is submited
by an anonomous user, a new contact is create or an
existing one is linked.
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_contact_generation%0Aversion:%2015.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
* Ernesto Tejeda
* Stefan Ungureanu
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-chienandalu| image:: https://github.com/chienandalu.png?size=40px
:target: https://github.com/chienandalu
:alt: chienandalu
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-chienandalu|
This module is part of the `OCA/survey <https://github.com/OCA/survey/tree/15.0/survey_contact_generation>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,24 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Survey contacts generation",
"summary": "Generate new contacts from surveys",
"version": "16.0.1.0.0",
"development_status": "Beta",
"category": "Marketing/Survey",
"website": "https://github.com/OCA/survey",
"author": "Tecnativa, Odoo Community Association (OCA)",
"maintainers": ["clement_thomas"],
"license": "AGPL-3",
"depends": ["survey"],
"data": [
"views/survey_question_views.xml",
"views/survey_survey_views.xml",
],
"demo": ["demo/survey_contact_generation_demo.xml"],
"assets": {
"web.assets_tests": [
"/survey_contact_generation/static/tests/survey_contact_generation_tour.js",
],
},
}

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Survey-->
<record model="survey.survey" id="survey_contact_creation">
<field name="title">Contact Creation Survey</field>
<field name="access_token">80e5f1e2-1a9d-4c51-8e23-09e93f7fa2c5</field>
<field name="access_mode">public</field>
<field name="users_can_go_back" eval="True" />
<field name="generate_contact" eval="True" />
</record>
<record model="survey.question" id="survey_contact_q0">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">0</field>
<field name="title">Name</field>
<field name="question_type">char_box</field>
<field name="constr_mandatory" eval="True" />
<field name="res_partner_field" ref="base.field_res_partner__name" />
</record>
<record model="survey.question" id="survey_contact_q1">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">1</field>
<field name="title">Email</field>
<field name="question_type">char_box</field>
<field name="res_partner_field" ref="base.field_res_partner__email" />
</record>
<record model="survey.question" id="survey_contact_q2">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">2</field>
<field name="title">Notes</field>
<field name="question_type">text_box</field>
<field name="res_partner_field" ref="base.field_res_partner__comment" />
</record>
<record model="survey.question" id="survey_contact_q3">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">3</field>
<field name="title">Color</field>
<field name="question_type">numerical_box</field>
<field name="res_partner_field" ref="base.field_res_partner__color" />
</record>
<record model="survey.question" id="survey_contact_q4">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">4</field>
<field name="title">Date</field>
<field name="question_type">date</field>
<field name="res_partner_field" ref="base.field_res_partner__date" />
</record>
<record model="survey.question" id="survey_contact_q5">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">4</field>
<field name="title">Country</field>
<field name="question_type">simple_choice</field>
<field name="res_partner_field" ref="base.field_res_partner__country_id" />
</record>
<record model="survey.question.answer" id="survey_contact_q5_sug1">
<field name="question_id" ref="survey_contact_q5" />
<field name="sequence">1</field>
<field name="value">Spain</field>
<field name="res_partner_field_resource_ref" ref="base.es" />
</record>
<record model="survey.question.answer" id="survey_contact_q5_sug2">
<field name="question_id" ref="survey_contact_q5" />
<field name="sequence">2</field>
<field name="value">Romania</field>
<field name="res_partner_field_resource_ref" ref="base.ro" />
</record>
<record model="survey.question" id="survey_contact_q6">
<field name="survey_id" ref="survey_contact_creation" />
<field name="sequence">4</field>
<field name="title">Tags</field>
<field name="question_type">multiple_choice</field>
<field name="res_partner_field" ref="base.field_res_partner__category_id" />
</record>
<record model="survey.question.answer" id="survey_contact_q6_sug1">
<field name="question_id" ref="survey_contact_q6" />
<field name="sequence">1</field>
<field name="value">Vendor</field>
<field
name="res_partner_field_resource_ref"
ref="base.res_partner_category_0"
/>
</record>
<record model="survey.question.answer" id="survey_contact_q6_sug2">
<field name="question_id" ref="survey_contact_q6" />
<field name="sequence">2</field>
<field name="value">Prospects</field>
<field
name="res_partner_field_resource_ref"
ref="base.res_partner_category_2"
/>
</record>
<record model="survey.question.answer" id="survey_contact_q6_sug3">
<field name="question_id" ref="survey_contact_q6" />
<field name="sequence">3</field>
<field name="value">Employees</field>
<field
name="res_partner_field_resource_ref"
ref="base.res_partner_category_3"
/>
</record>
</odoo>

View File

@@ -0,0 +1,168 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_contact_generation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-14 11:55+0000\n"
"PO-Revision-Date: 2023-06-14 13:58+0200\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: \n"
"X-Generator: Poedit 3.0.1\n"
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question__allowed_field_ids
msgid "Allowed Field"
msgstr "Campo permitido"
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q3
msgid "Color"
msgstr "Color"
#. module: survey_contact_generation
#: model_terms:ir.ui.view,arch_db:survey_contact_generation.survey_form
#: model_terms:ir.ui.view,arch_db:survey_contact_generation.survey_question_form
msgid "Contact"
msgstr "Contacto"
#. module: survey_contact_generation
#: model:survey.survey,title:survey_contact_generation.survey_contact_creation
msgid "Contact Creation Survey"
msgstr "Encuesta de creación de contactos"
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question__res_partner_field
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question_answer__res_partner_field
msgid "Contact field"
msgstr "Campo de contacto"
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question_answer__res_partner_field_resource_ref
msgid "Contact field value"
msgstr ""
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q5
msgid "Country"
msgstr ""
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q4
msgid "Date"
msgstr "Fecha"
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q1
msgid "Email"
msgstr "Correo electrónico"
#. module: survey_contact_generation
#: model:survey.question.answer,value:survey_contact_generation.survey_contact_q6_sug3
msgid "Employees"
msgstr ""
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_survey__generate_contact
msgid "Generate Contact"
msgstr "Generar contacto"
#. module: survey_contact_generation
#: model:ir.model.fields,help:survey_contact_generation.field_survey_survey__generate_contact
msgid "Generate contacts for anonymous survey users"
msgstr "Generar contacto para encuestas anónimas"
#. module: survey_contact_generation
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q0
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q1
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q2
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q3
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q4
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q5
#: model:survey.question,comments_message:survey_contact_generation.survey_contact_q6
msgid "If other, please specify:"
msgstr "Si es otro, especifíquelo:"
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q0
msgid "Name"
msgstr "Nombre"
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q2
msgid "Notes"
msgstr "Notas"
#. module: survey_contact_generation
#: model:survey.question.answer,value:survey_contact_generation.survey_contact_q6_sug2
msgid "Prospects"
msgstr ""
#. module: survey_contact_generation
#: model:survey.question.answer,value:survey_contact_generation.survey_contact_q5_sug2
msgid "Romania"
msgstr ""
#. module: survey_contact_generation
#: model:survey.question.answer,value:survey_contact_generation.survey_contact_q5_sug1
msgid "Spain"
msgstr ""
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_survey
msgid "Survey"
msgstr "Planificación"
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_question_answer
msgid "Survey Label"
msgstr ""
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_question
msgid "Survey Question"
msgstr "Pregunta de la encuesta"
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_user_input
msgid "Survey User Input"
msgstr "Entrada de usuario de la encuesta"
#. module: survey_contact_generation
#: model:survey.question,title:survey_contact_generation.survey_contact_q6
msgid "Tags"
msgstr ""
#. module: survey_contact_generation
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q0
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q1
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q2
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q3
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q4
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q5
#: model:survey.question,validation_error_msg:survey_contact_generation.survey_contact_q6
msgid "The answer you entered is not valid."
msgstr "La respuesta que has introducido no es válida."
#. module: survey_contact_generation
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q0
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q1
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q2
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q3
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q4
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q5
#: model:survey.question,constr_error_msg:survey_contact_generation.survey_contact_q6
msgid "This question requires an answer."
msgstr "Esta pregunta requiere una respuesta."
#. module: survey_contact_generation
#: model:survey.question.answer,value:survey_contact_generation.survey_contact_q6_sug1
msgid "Vendor"
msgstr ""

View File

@@ -0,0 +1,64 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_contact_generation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-19 14:33+0000\n"
"PO-Revision-Date: 2023-09-19 14:33+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: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question__allowed_field_ids
msgid "Allowed Field"
msgstr "Champs autorisés"
#. module: survey_contact_generation
#: model_terms:ir.ui.view,arch_db:survey_contact_generation.survey_form
#: model_terms:ir.ui.view,arch_db:survey_contact_generation.survey_question_form
msgid "Contact"
msgstr ""
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question__res_partner_field
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question_answer__res_partner_field
msgid "Contact field"
msgstr "Champ contact"
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_question_answer__res_partner_field_resource_ref
msgid "Contact field value"
msgstr "Valeur du champ contact"
#. module: survey_contact_generation
#: model:ir.model.fields,field_description:survey_contact_generation.field_survey_survey__generate_contact
msgid "Generate Contact"
msgstr "Générer un contact"
#. module: survey_contact_generation
#: model:ir.model.fields,help:survey_contact_generation.field_survey_survey__generate_contact
msgid "Generate contacts for anonymous survey users"
msgstr "Générer des contacts pour les questionnaires anonymes"
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_question_answer
msgid "Survey Label"
msgstr "Étiquette du questionnaire"
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_question
msgid "Survey Question"
msgstr "Question du questionnaire"
#. module: survey_contact_generation
#: model:ir.model,name:survey_contact_generation.model_survey_user_input
msgid "Survey User Input"
msgstr "Entrée utilisateur du questionnaire"

View File

@@ -0,0 +1,3 @@
from . import survey_question
from . import survey_survey
from . import survey_user_input

View File

@@ -0,0 +1,80 @@
# Copyright 2022 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class SurveyQuestion(models.Model):
_inherit = "survey.question"
allowed_field_ids = fields.Many2many(
comodel_name="ir.model.fields",
compute="_compute_allowed_field_ids",
)
res_partner_field = fields.Many2one(
string="Contact field",
comodel_name="ir.model.fields",
domain="[('id', 'in', allowed_field_ids)]",
)
@api.depends("question_type")
def _compute_allowed_field_ids(self):
type_mapping = {
"char_box": ["char", "text"],
"text_box": ["html"],
"numerical_box": ["integer", "float", "html", "char"],
"date": ["date", "text", "char"],
"datetime": ["datetime", "html", "char"],
"simple_choice": ["many2one", "html", "char"],
"multiple_choice": ["many2many", "html", "char"],
}
for record in self:
record.allowed_field_ids = (
self.env["ir.model.fields"]
.search(
[
("model", "=", "res.partner"),
("ttype", "in", type_mapping.get(record.question_type, [])),
]
)
.ids
)
class SurveyQuestionAnswer(models.Model):
_inherit = "survey.question.answer"
@api.model
def default_get(self, fields):
result = super().default_get(fields)
if (
not result.get("res_partner_field")
or "res_partner_field_resource_ref" not in fields
):
return result
partner_field = self.env["ir.model.fields"].browse(result["res_partner_field"])
# Otherwise we'll just use the value (char, text)
if partner_field.ttype not in {"many2one", "many2many"}:
return result
res = self.env[partner_field.relation].search([], limit=1)
if res:
result["res_partner_field_resource_ref"] = "%s,%s" % (
partner_field.relation,
res.id,
)
return result
@api.model
def _selection_res_partner_field_resource_ref(self):
return [(model.model, model.name) for model in self.env["ir.model"].search([])]
res_partner_field = fields.Many2one(related="question_id.res_partner_field")
res_partner_field_resource_ref = fields.Reference(
string="Contact field value",
selection="_selection_res_partner_field_resource_ref",
)
@api.onchange("res_partner_field_resource_ref")
def _onchange_res_partner_field_resource_ref(self):
"""Set the default value as the product name, although we can change it"""
if self.res_partner_field_resource_ref:
self.value = self.res_partner_field_resource_ref.display_name or ""

View File

@@ -0,0 +1,11 @@
# 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"
generate_contact = fields.Boolean(
help="Generate contacts for anonymous survey users",
)

View File

@@ -0,0 +1,77 @@
# Copyright 2022 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class SurveyUserInput(models.Model):
_inherit = "survey.user_input"
def _prepare_partner(self):
"""Extract partner values from the answers"""
self.ensure_one()
elegible_inputs = self.user_input_line_ids.filtered(
lambda x: x.question_id.res_partner_field and not x.skipped
)
basic_inputs = elegible_inputs.filtered(
lambda x: x.answer_type not in {"suggestion"}
and x.question_id.res_partner_field.name != "comment"
)
vals = {
line.question_id.res_partner_field.name: line[f"value_{line.answer_type}"]
for line in basic_inputs
}
for line in elegible_inputs - basic_inputs:
field_name = line.question_id.res_partner_field.name
if line.question_id.res_partner_field.ttype == "many2one":
vals[
field_name
] = line.suggested_answer_id.res_partner_field_resource_ref.id
elif line.question_id.res_partner_field.ttype == "many2many":
vals.setdefault(field_name, [])
vals[field_name] += [
(4, line.suggested_answer_id.res_partner_field_resource_ref.id)
]
# We'll use the comment field to add any other infos
elif field_name == "comment":
vals.setdefault("comment", "")
value = (
line.suggested_answer_id.value
if line.answer_type == "suggestion"
else line[f"value_{line.answer_type}"]
)
vals["comment"] += f"\n{line.question_id.title}: {value}"
else:
if line.question_id.question_type == "multiple_choice":
if not vals.get(field_name):
vals[field_name] = line.suggested_answer_id.value
else:
vals[field_name] += line.suggested_answer_id.value
else:
vals[field_name] = line.suggested_answer_id.value
return vals
def _create_contact_post_process(self, partner):
"""After creating the lead send an internal message with the input link"""
partner.message_post_with_view(
"mail.message_origin_link",
values={"self": partner, "origin": self.survey_id},
subtype_id=self.env.ref("mail.mt_note").id,
)
def _mark_done(self):
"""Generate the contact when the survey is submitted"""
for user_input in self.filtered(
lambda r: r.survey_id.generate_contact# and not self.partner_id #uncomment to avoid contact generation several times
):
vals = user_input._prepare_partner()
partner = False
email = vals.get("email")
if email:
partner = self.env["res.partner"].search(
[("email", "=", email)], limit=1
)
if not partner:
partner = self.env["res.partner"].create(vals)
self._create_contact_post_process(partner)
self.update({"partner_id": partner.id, "email": partner.email})
return super()._mark_done()

View File

@@ -0,0 +1,10 @@
To configure the contact generation:
#. Go to the configured survey.
#. In the *Contact* section of the *Options* tab, set
*Generate Contact* on, if you want contacts to be
generated from the answers to this survey.
#. In each question associated with a future new contact,
specify the corresponding contact field. To do this,
go to the 'Options' tab, then navigate to the 'Contact' group,
and select the 'Contact field' field.

View File

@@ -0,0 +1,5 @@
* `Tecnativa <https://www.tecnativa.com>`_
* David Vidal
* Ernesto Tejeda
* Stefan Ungureanu

View File

@@ -0,0 +1 @@
This module allows to generate new contacts from surveys answers.

View File

@@ -0,0 +1,3 @@
if the survey is properly configured, once it is submited
by an anonomous user, a new contact is create or an
existing one is linked.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -0,0 +1,450 @@
<?xml version="1.0" encoding="utf-8"?>
<!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 contacts generation</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
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: grey; } /* 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 {
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-contacts-generation">
<h1 class="title">Survey contacts generation</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0a298d1600a5f93ffe77357631c7e799e78b23b84c362b126720e36655dd5227
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/15.0/survey_contact_generation"><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-15-0/survey-15-0-survey_contact_generation"><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=15.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows to generate new contacts from surveys answers.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>To configure the contact generation:</p>
<ol class="arabic simple">
<li>Go to the configured survey.</li>
<li>In the <em>Contact</em> section of the <em>Options</em> tab, set
<em>Generate Contact</em> on, if you want contacts to be
generated from the answers to this survey.</li>
<li>In each question associated with a future new contact,
specify the corresponding contact field. To do this,
go to the Options tab, then navigate to the Contact group,
and select the Contact field field.</li>
</ol>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>if the survey is properly configured, once it is submited
by an anonomous user, a new contact is create or an
existing one is linked.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">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_contact_generation%0Aversion:%2015.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-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a><ul>
<li>David Vidal</li>
<li>Ernesto Tejeda</li>
<li>Stefan Ungureanu</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">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/chienandalu"><img alt="chienandalu" src="https://github.com/chienandalu.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/survey/tree/15.0/survey_contact_generation">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>

View File

@@ -0,0 +1,82 @@
odoo.define("survey.tour_test_survey_contact_generation", function (require) {
"use strict";
const tour = require("web_tour.tour");
tour.register(
"test_survey_contact_generation",
{
test: true,
url: "/survey/start/80e5f1e2-1a9d-4c51-8e23-09e93f7fa2c5",
},
[
{
content: "Click on Start",
trigger: "button.btn:contains('Start Survey')",
},
{
content: "Name",
trigger: "div.js_question-wrapper:contains('Name') input",
run: "text My Name",
},
{
content: "Email",
trigger: "div.js_question-wrapper:contains('Email') input",
run: "text survey_contact_generation@test.com",
},
{
content: "Notes",
trigger: "div.js_question-wrapper:contains('Notes') textarea",
run: "text This is a test note",
},
{
content: "Color",
trigger: "div.js_question-wrapper:contains('Color') input",
run: "text 1",
},
{
content: "Date",
trigger: "div.js_question-wrapper:contains('Date') input",
run: "text 01/01/2023",
},
{
content: "Country",
trigger:
"div.js_question-wrapper:contains('Country') label:contains('Romania') i",
run: function () {
$(
"div.js_question-wrapper:contains('Country') label:contains('Romania') i"
).prop("checked", true);
},
},
{
content: "Tags",
trigger:
"div.js_question-wrapper:contains('Tags') label:contains('Prospects') i",
run: function () {
$(
"div.js_question-wrapper:contains('Tags') label:contains('Prospects') i"
).prop("checked", true);
},
},
{
content: "Tags",
trigger:
"div.js_question-wrapper:contains('Tags') label:contains('Vendor') i",
run: function () {
$(
"div.js_question-wrapper:contains('Tags') label:contains('Vendor') i"
).prop("checked", true);
},
},
{
content: "Click Submit",
trigger: "button[value='finish']",
},
{
content: "Thank you",
trigger: "h1:contains('Thank you!')",
},
]
);
});

View File

@@ -0,0 +1 @@
from . import test_survey_contact_generation

View File

@@ -0,0 +1,31 @@
# Copyright 2023 Tecnativa - David Vidal
# Copyright 2023 Tecnativa - Stefan Ungureanu
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tests import HttpCase, tagged
from odoo.addons.survey.tests.common import SurveyCase
@tagged("-at_install", "post_install")
class SurveyContactGenerationCase(SurveyCase, HttpCase):
def setUp(self):
"""We run the tour in the setup so we can share the tests case with other
modules"""
super().setUp()
self.survey = self.env.ref("survey_contact_generation.survey_contact_creation")
initial_user_inputs = self.survey.user_input_ids
# Run the survey as a portal user and get the generated quotation
self.start_tour(
f"/survey/start/{self.survey.access_token}",
"test_survey_contact_generation",
)
self.user_input = self.survey.user_input_ids - initial_user_inputs
@tagged("-at_install", "post_install")
class SurveyContactGenerationTests(SurveyContactGenerationCase):
def test_contact_generation(self):
partner = self.env["res.partner"].search(
[("email", "=", "survey_contact_generation@test.com")]
)
self.assertEqual(partner, self.user_input.partner_id)

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="survey_question_form" model="ir.ui.view">
<field name="model">survey.question</field>
<field name="inherit_id" ref="survey.survey_question_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='comments_allowed']/.." position="after">
<group name="contact" string="Contact">
<field name="res_partner_field" widget="selection" />
<field name="allowed_field_ids" invisible="1" />
</group>
</xpath>
<xpath expr="//field[@name='suggested_answer_ids']" position="attributes">
<attribute
name="context"
>{'default_question_id': active_id, 'default_res_partner_field': res_partner_field}</attribute>
</xpath>
<xpath
expr="//field[@name='suggested_answer_ids']//field[@name='value']"
position="after"
>
<field name="res_partner_field" invisible="1" />
<field
name="res_partner_field_resource_ref"
readonly="False"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
attrs="{'column_invisible': [('parent.res_partner_field', '=', False)]}"
/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,14 @@
<?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">
<group name="options" position="inside">
<group name="contact_options" string="Contact">
<field name="generate_contact" />
</group>
</group>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,105 @@
=======================
Survey leads generation
=======================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:72ceb1068020aaec4baba6df86a6b1b024793db57bdfc2cec8374da9cac8d031
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/15.0/survey_crm_generation
:alt: OCA/survey
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/survey-15-0/survey-15-0-survey_crm_generation
: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=15.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to generate leads/opportunities from surveys.
**Table of contents**
.. contents::
:local:
Configuration
=============
To configure the leads/opportunities generation:
#. Go to the configured survey.
#. In the *CRM* section of the *Options* tab, set *Generate leads* on.
#. Optionally you can set default tags for the generated leads.
#. You can set the crm team to assign the leads to.
The questions marked to be shown in the lead description will be shown there.
Usage
=====
Once the surveys are submited a lead/opportunity (depending on the default options for
the company) will be generated with a link to the answers.
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_crm_generation%0Aversion:%2015.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
* Stefan ungureanu
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-chienandalu| image:: https://github.com/chienandalu.png?size=40px
:target: https://github.com/chienandalu
:alt: chienandalu
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-chienandalu|
This module is part of the `OCA/survey <https://github.com/OCA/survey/tree/15.0/survey_crm_generation>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,26 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Survey leads generation",
"summary": "Generate CRM leads/opportunities from surveys",
"version": "16.0.1.0.0",
"development_status": "Beta",
"category": "Marketing/Survey",
"website": "https://github.com/OCA/survey",
"author": "Tecnativa, Odoo Community Association (OCA)",
"maintainers": ["chienandalu"],
"license": "AGPL-3",
"depends": ["survey", "crm"],
"data": [
"views/survey_survey_views.xml",
"views/survey_question_views.xml",
"views/survey_user_input_views.xml",
"views/crm_lead_views.xml",
],
"demo": ["demo/survey_crm_demo.xml"],
"assets": {
"web.assets_tests": [
"/survey_crm_generation/static/tests/survey_crm_generation_tour.js",
],
},
}

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="crm.tag" id="tag_survey_leads">
<field name="name">Survey Leads</field>
</record>
<record model="crm.tag" id="tag_oca_partnership">
<field name="name">OCA Partnership</field>
</record>
<!-- Sales Team -->
<record model="crm.team" id="oca_partnership_leads">
<field name="name">OCA Partnership</field>
</record>
<!-- Survey -->
<record model="survey.survey" id="become_partner">
<field name="title">Become OCA Partner</field>
<field name="description">Be part of the Odoo Community!</field>
<field name="access_token">08b4db21-66cc-4c69-a712-cc364c54902c</field>
<field name="access_mode">public</field>
<field name="generate_leads" eval="True" />
<field name="crm_team_id" ref="oca_partnership_leads" />
<field
name="crm_tag_ids"
eval="[(6,0,[ref('survey_crm_generation.tag_oca_partnership'), ref('survey_crm_generation.tag_survey_leads')])]"
/>
<field name="users_can_go_back" eval="True" />
</record>
<record model="survey.question" id="survey_oca_q0">
<field name="survey_id" ref="become_partner" />
<field name="sequence">0</field>
<field name="title">E-mail address</field>
<field name="question_type">text_box</field>
<field name="show_in_lead_description" eval="True" />
<field name="constr_mandatory" eval="True" />
</record>
<record model="survey.question" id="survey_oca_q1">
<field name="survey_id" ref="become_partner" />
<field name="sequence">1</field>
<field name="title">Your company name?</field>
<field name="question_type">text_box</field>
<field name="show_in_lead_description" eval="True" />
<field name="constr_mandatory" eval="True" />
</record>
<record model="survey.question" id="survey_oca_q3">
<field name="survey_id" ref="become_partner" />
<field name="sequence">2</field>
<field name="title">And your name?</field>
<field name="question_type">text_box</field>
<field name="show_in_lead_description" eval="True" />
<field name="constr_mandatory" eval="True" />
</record>
</odoo>

View File

@@ -0,0 +1,137 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_crm_generation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-01 09:49+0000\n"
"PO-Revision-Date: 2023-06-01 12:07+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es_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.0.1\n"
#. module: survey_crm_generation
#: model:survey.question,title:survey_crm_generation.survey_oca_q3
msgid "And your name?"
msgstr "¿Y tu nombre? "
#. module: survey_crm_generation
#: model:survey.survey,description:survey_crm_generation.become_partner
msgid "Be part of the Odoo Community!"
msgstr "¡Se parte de la comunidad de Odoo!"
#. module: survey_crm_generation
#: model:survey.survey,title:survey_crm_generation.become_partner
msgid "Become OCA Partner"
msgstr "Conviértete en miembro OCA"
#. module: survey_crm_generation
#: model_terms:ir.ui.view,arch_db:survey_crm_generation.survey_form
#: model_terms:ir.ui.view,arch_db:survey_crm_generation.survey_question_form
msgid "CRM"
msgstr "CRM"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_survey__crm_tag_ids
msgid "Crm Tag"
msgstr "Etiqueta CRM"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_survey__crm_team_id
msgid "Crm Team"
msgstr "Equipo CRM"
#. module: survey_crm_generation
#: model:survey.question,title:survey_crm_generation.survey_oca_q0
msgid "E-mail address"
msgstr "Dirección de correo electrónico"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_survey__generate_leads
msgid "Generate Leads"
msgstr "Generación de iniciativa"
#. module: survey_crm_generation
#: model:ir.model.fields,help:survey_crm_generation.field_survey_survey__generate_leads
msgid "Generate leads/opportunities linked to the generated quotations"
msgstr "Generar iniciativas/oportunidades vinculadas a las ofertas generadas"
#. module: survey_crm_generation
#: model:survey.question,comments_message:survey_crm_generation.survey_oca_q0
#: model:survey.question,comments_message:survey_crm_generation.survey_oca_q1
#: model:survey.question,comments_message:survey_crm_generation.survey_oca_q3
msgid "If other, please specify:"
msgstr "Si es otro, especifíquelo:"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_crm_lead
msgid "Lead/Opportunity"
msgstr "Iniciativa/Oportunidad"
#. module: survey_crm_generation
#: model:crm.tag,name:survey_crm_generation.tag_oca_partnership
#: model:crm.team,name:survey_crm_generation.oca_partnership_leads
msgid "OCA Partnership"
msgstr "Asociación OCA"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_user_input__opportunity_id
msgid "Opportunity"
msgstr "Oportunidad"
#. module: survey_crm_generation
#: model:ir.model.fields,help:survey_crm_generation.field_survey_survey__crm_tag_ids
msgid "Set the default crm tags in the generated leads/opportunities"
msgstr "Establecer las etiquetas crm por defecto en las iniciativas/oportunidades generadas"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_question__show_in_lead_description
msgid "Show In Lead Description"
msgstr "Mostrar en la descripción de la iniciativa"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_survey_survey
msgid "Survey"
msgstr "Encuesta"
#. module: survey_crm_generation
#: model:crm.tag,name:survey_crm_generation.tag_survey_leads
msgid "Survey Leads"
msgstr "Encuesta de la iniciativa"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_survey_question
msgid "Survey Question"
msgstr "Pregunta de la encuesta"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_survey_user_input
#: model:ir.model.fields,field_description:survey_crm_generation.field_crm_lead__survey_user_input_id
msgid "Survey User Input"
msgstr "Respuestas de los usuarios"
#. module: survey_crm_generation
#: model:survey.question,validation_error_msg:survey_crm_generation.survey_oca_q0
#: model:survey.question,validation_error_msg:survey_crm_generation.survey_oca_q1
#: model:survey.question,validation_error_msg:survey_crm_generation.survey_oca_q3
msgid "The answer you entered is not valid."
msgstr "La respuesta que has introducido no es válida."
#. module: survey_crm_generation
#: model:survey.question,constr_error_msg:survey_crm_generation.survey_oca_q0
#: model:survey.question,constr_error_msg:survey_crm_generation.survey_oca_q1
#: model:survey.question,constr_error_msg:survey_crm_generation.survey_oca_q3
msgid "This question requires an answer."
msgstr "Esta pregunta requiere una respuesta."
#. module: survey_crm_generation
#: model:survey.question,title:survey_crm_generation.survey_oca_q1
msgid "Your company name?"
msgstr "¿El nombre de su empresa?"

View File

@@ -0,0 +1,85 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_crm_generation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-10-24 08:50+0000\n"
"PO-Revision-Date: 2023-10-24 08:50+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: survey_crm_generation
#: model_terms:ir.ui.view,arch_db:survey_crm_generation.survey_form
#: model_terms:ir.ui.view,arch_db:survey_crm_generation.survey_question_form
msgid "CRM"
msgstr ""
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_survey__crm_tag_ids
msgid "Crm Tag"
msgstr "Étiquette CRM"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_survey__crm_team_id
msgid "Crm Team"
msgstr "Équipe commerciale"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_survey__generate_leads
msgid "Generate Leads"
msgstr "Générer des pistes"
#. module: survey_crm_generation
#: model:ir.model.fields,help:survey_crm_generation.field_survey_survey__generate_leads
msgid "Generate leads/opportunities linked to the generated quotations"
msgstr "Générer des pistes / opportunités liées aux devis"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_crm_lead
msgid "Lead/Opportunity"
msgstr "Piste/Opportunité"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_user_input__opportunity_id
msgid "Opportunity"
msgstr "Opportunité"
#. module: survey_crm_generation
#: model:ir.model.fields,help:survey_crm_generation.field_survey_survey__crm_tag_ids
msgid "Set the default crm tags in the generated leads/opportunities"
msgstr "Attribuer les étiquettes par défault dans les pistes générées"
#. module: survey_crm_generation
#: model:ir.model.fields,field_description:survey_crm_generation.field_survey_question__show_in_lead_description
msgid "Show In Lead Description"
msgstr "Montrer dans la description de la piste CRM"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_survey_survey
msgid "Survey"
msgstr "Questionnaire"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_survey_question
msgid "Survey Question"
msgstr "Question du questionnaire"
#. module: survey_crm_generation
#: model:ir.model,name:survey_crm_generation.model_survey_user_input
#: model:ir.model.fields,field_description:survey_crm_generation.field_crm_lead__survey_user_input_id
msgid "Survey User Input"
msgstr "Entrée utilisateur du questionnaire"
#. module: survey_crm_generation
#. odoo-python
#: code:addons/survey_crm_generation/models/survey_user_input.py:0
#, python-format
msgid "Survey answers: "
msgstr "Réponses au questionnaire : "

View File

@@ -0,0 +1,4 @@
from . import crm_lead
from . import survey_question
from . import survey_survey
from . import survey_user_input

View File

@@ -0,0 +1,9 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class CrmLead(models.Model):
_inherit = "crm.lead"
survey_user_input_id = fields.Many2one(comodel_name="survey.user_input")

View File

@@ -0,0 +1,9 @@
# Copyright 2022 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class SurveyQuestion(models.Model):
_inherit = "survey.question"
show_in_lead_description = fields.Boolean()

View File

@@ -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"
generate_leads = fields.Boolean(
help="Generate leads/opportunities linked to the generated quotations",
)
crm_tag_ids = fields.Many2many(
comodel_name="crm.tag",
help="Set the default crm tags in the generated leads/opportunities",
)
crm_team_id = fields.Many2one(comodel_name="crm.team")

View File

@@ -0,0 +1,76 @@
# Copyright 2022 Tecnativa - David Vidal
# 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"
opportunity_id = fields.Many2one(comodel_name="crm.lead")
def _prepare_opportunity(self):
return {
"name": self.survey_id.title,
"tag_ids": [(6, 0, self.survey_id.crm_tag_ids.ids)],
"partner_id": self.partner_id.id,
"user_id": self.survey_id.crm_team_id.user_id.id,
"team_id": self.survey_id.crm_team_id.id,
"company_id": self.create_uid.company_id.id,
"survey_user_input_id": self.id,
"description": self._prepare_lead_description(),
}
def _prepare_lead_description(self):
"""We can have surveys without partner. It's handy to have some relevant info
in the description although the answers are linked themselves.
:return str: description for the lead
"""
relevant_answers = self.user_input_line_ids.filtered(
lambda x: not x.skipped and x.question_id.show_in_lead_description
)
li = ''
for answer in relevant_answers:
li += '<li>'
if answer.answer_type == "suggestion":
answer_value = answer.suggested_answer_id
else:
answer_value = answer[f'value_{answer.answer_type}']
#case of value Models
if isinstance(answer_value,models.Model):
# case of Multi Models
if len(answer_value._ids) > 1:
ul2 = f'{answer.question_id.title}: <ul>'
for answer_value_object in answer_value:
ul2 += '<li>'+f"{answer_value_object.display_name}"+'</li>'
ul2 += '</ul>'
li += ul2
# case of One Models
else:
li += f"{answer.question_id.title}: {answer_value.display_name}"
else:
# case of string value
li += f"{answer.question_id.title}: {answer_value}"
li += '</li>'
description = '<u>'+_('Survey answers: ')+"</u><ul>"+li+"</ul>"
return description
def _create_opportunity_post_process(self):
"""After creating the lead send an internal message with the input link"""
self.opportunity_id.message_post_with_view(
"mail.message_origin_link",
values={"self": self.opportunity_id, "origin": self},
subtype_id=self.env.ref("mail.mt_note").id,
)
def _mark_done(self):
"""Generate the opportunity when the survey is submitted"""
res = super()._mark_done()
if not self.survey_id.generate_leads:
return res
vals = self._prepare_opportunity()
self.opportunity_id = self.env["crm.lead"].sudo().create(vals)
self._create_opportunity_post_process()
return res

View File

@@ -0,0 +1,8 @@
To configure the leads/opportunities generation:
#. Go to the configured survey.
#. In the *CRM* section of the *Options* tab, set *Generate leads* on.
#. Optionally you can set default tags for the generated leads.
#. You can set the crm team to assign the leads to.
The questions marked to be shown in the lead description will be shown there.

View File

@@ -0,0 +1,4 @@
* `Tecnativa <https://www.tecnativa.com>`_
* David Vidal
* Stefan ungureanu

View File

@@ -0,0 +1 @@
This module allows to generate leads/opportunities from surveys.

View File

@@ -0,0 +1,2 @@
Once the surveys are submited a lead/opportunity (depending on the default options for
the company) will be generated with a link to the answers.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -0,0 +1,445 @@
<?xml version="1.0" encoding="utf-8"?>
<!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 leads generation</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
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: grey; } /* 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 {
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-leads-generation">
<h1 class="title">Survey leads generation</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:72ceb1068020aaec4baba6df86a6b1b024793db57bdfc2cec8374da9cac8d031
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/15.0/survey_crm_generation"><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-15-0/survey-15-0-survey_crm_generation"><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=15.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows to generate leads/opportunities from surveys.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>To configure the leads/opportunities generation:</p>
<ol class="arabic simple">
<li>Go to the configured survey.</li>
<li>In the <em>CRM</em> section of the <em>Options</em> tab, set <em>Generate leads</em> on.</li>
<li>Optionally you can set default tags for the generated leads.</li>
<li>You can set the crm team to assign the leads to.</li>
</ol>
<p>The questions marked to be shown in the lead description will be shown there.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>Once the surveys are submited a lead/opportunity (depending on the default options for
the company) will be generated with a link to the answers.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">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_crm_generation%0Aversion:%2015.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-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a><ul>
<li>David Vidal</li>
<li>Stefan ungureanu</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-7">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/chienandalu"><img alt="chienandalu" src="https://github.com/chienandalu.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/survey/tree/15.0/survey_crm_generation">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>

View File

@@ -0,0 +1,48 @@
odoo.define(
"survey_crm_generation.tour_test_survey_crm_generation",
function (require) {
"use strict";
const tour = require("web_tour.tour");
tour.register(
"test_survey_crm_generation",
{
test: true,
url: "/survey/start/08b4db21-66cc-4c69-a712-cc364c54902c",
},
[
{
content: "Start Survey",
trigger: "button.btn:contains('Start Survey')",
},
{
content: "E-mail address",
trigger:
"div.js_question-wrapper:contains('E-mail address') textarea",
run: "text test@test.com",
},
{
content: "Your company name?",
trigger:
"div.js_question-wrapper:contains('Your company name?') textarea",
run: "text Tecnativa",
},
{
content: "And your name?",
trigger:
"div.js_question-wrapper:contains('And your name?') textarea",
run: "text Tecnativa",
},
{
content: "Click Submit",
trigger: "button[value='finish']",
},
{
content: "Thank you",
trigger: "h1:contains('Thank you!')",
},
]
);
}
);

View File

@@ -0,0 +1 @@
from . import test_survey_crm_sale_generation

View File

@@ -0,0 +1,49 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from markupsafe import Markup
from odoo.tests import HttpCase, tagged
from odoo.addons.survey.tests.common import SurveyCase
@tagged("-at_install", "post_install")
class SurveyCrmGenerationCase(SurveyCase, HttpCase):
def setUp(self):
"""We run the tour in the setup so we can share the tests case with other
modules"""
super().setUp()
self.oca_leads = self.env.ref("survey_crm_generation.oca_partnership_leads")
self.survey = self.env.ref("survey_crm_generation.become_partner")
initial_user_inputs = self.survey.user_input_ids
# Run the survey as a portal user and get the generated quotation
self.start_tour(
f"/survey/start/{self.survey.access_token}",
"test_survey_crm_generation",
login="portal",
)
self.user_input = self.survey.user_input_ids - initial_user_inputs
self.generated_lead = self.user_input.opportunity_id
@tagged("-at_install", "post_install")
class SurveyCrmGenerationTests(SurveyCrmGenerationCase):
def test_lead_generation(self):
self.assertFalse(self.generated_lead.stage_id.is_won)
self.assertEqual(self.generated_lead.team_id, self.oca_leads)
self.assertEqual(
self.generated_lead.tag_ids,
(
self.env.ref("survey_crm_generation.tag_oca_partnership")
+ self.env.ref("survey_crm_generation.tag_survey_leads")
),
)
expected_lead_description = Markup(
"<p>E-mail address: test@test.com\n"
"Your company name?: Tecnativa\n"
"And your name?: Tecnativa</p>"
)
self.assertEqual(
self.generated_lead.description,
expected_lead_description,
)

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="crm_lead_view_form" model="ir.ui.view">
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_lead_view_form" />
<field name="arch" type="xml">
<field name="tag_ids" position="after">
<field
string="Created from survey"
name="survey_user_input_id"
attrs="{'invisible': [('survey_user_input_id', '=', False)]}"
readonly="1"
/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="survey_question_form" model="ir.ui.view">
<field name="model">survey.question</field>
<field name="inherit_id" ref="survey.survey_question_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='comments_allowed']/.." position="after">
<group name="crm" string="CRM">
<field
name="show_in_lead_description"
attrs="{'invisible': [('question_type', 'in', ['matrix'])]}"
/>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,23 @@
<?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">
<group name="options" position="inside">
<group name="crm_options" string="CRM">
<field name="generate_leads" />
<field
name="crm_team_id"
attrs="{'invisible': [('generate_leads', '=', False)]}"
/>
<field
name="crm_tag_ids"
widget="many2many_tags"
attrs="{'invisible': [('generate_leads', '=', False)]}"
/>
</group>
</group>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="survey_user_input_view_form">
<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="partner_id" position="before">
<field
name="opportunity_id"
attrs="{'invisible': [('opportunity_id', '=', False)]}"
/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,25 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey lead generation attachment",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Elabore",
'summary': 'Link Attachments from Surveys to generated leads',
'description': """
Link Attachments from Surveys to generated leads
----------------------------------------------------
* Create new attachments on lead creation, based on attached file of survey answer (survey.user_input.line)
""",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["survey_base","survey_crm_generation"],
"data": [
],
"installable": True,
"auto_install":True
}

View File

@@ -0,0 +1 @@
from . import survey_user_input

View File

@@ -0,0 +1,32 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyUserInput(models.Model):
_inherit = 'survey.user_input'
def _mark_done(self):
"""Copy attachments to crm lead"""
res = super()._mark_done()
for user_input in self:
if user_input.survey_id.generate_leads and user_input.opportunity_id:
for user_input_line in user_input.user_input_line_ids:
if user_input_line.value_file:
self.env['ir.attachment'].create({
'res_model':'crm.lead',
'res_id':user_input.opportunity_id.id,
'name': user_input_line.value_file_fname,
'datas': user_input_line.value_file,
'type': 'binary'
})
return res

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,26 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey Crm Generation Training",
'summary': 'Customize lead creation from survey according to trainings',
'description': """
Customize lead creation from survey according to trainings
----------------------------------------------------
* add event products in crm.lead
* Copy event products selected in survey answer, to created lead
""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["event","survey_crm_generation","survey_event_registration_generation","survey_event_base"],
"data": [
"views/crm_lead_views.xml"
],
"installable": True,
"auto_install":True
}

View File

@@ -0,0 +1,48 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_crm_generation_training
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-10-31 10:46+0000\n"
"PO-Revision-Date: 2023-10-31 10:46+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: survey_crm_generation_training
#: model_terms:ir.ui.view,arch_db:survey_crm_generation_training.crm_lead_view_for_crm_generation_training
msgid ""
"<span class=\"o_stat_value\">\n"
" Responses\n"
" </span>"
msgstr ""
"<span class=\"o_stat_value\">\n"
" Réponses\n"
" </span>"
#. module: survey_crm_generation_training
#: model:ir.model.fields,field_description:survey_crm_generation_training.field_crm_lead__event_products_ids
#: model_terms:ir.ui.view,arch_db:survey_crm_generation_training.view_crm_case_opportunities_filter_survey_crm_generation_training
msgid "Event product"
msgstr "Produit de formation"
#. module: survey_crm_generation_training
#: model:ir.model,name:survey_crm_generation_training.model_crm_lead
msgid "Lead/Opportunity"
msgstr "Piste/Opportunité"
#. module: survey_crm_generation_training
#: model:ir.model,name:survey_crm_generation_training.model_survey_user_input
msgid "Survey User Input"
msgstr "Entrée utilisateur du questionnaire"
#. module: survey_crm_generation_training
#: model_terms:ir.ui.view,arch_db:survey_crm_generation_training.crm_lead_view_for_crm_generation_training
msgid "View survey answer that created this lead"
msgstr "Voir les réponses aux questionnaire qui ont créé cette piste"

View File

@@ -0,0 +1,2 @@
from . import crm_lead
from . import survey_user_input

View File

@@ -0,0 +1,23 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class CrmLead(models.Model):
_inherit = "crm.lead"
event_products_ids = fields.Many2many('product.product', string='Event product')
def action_view_survey_user_input_id(self):
form_view_id = self.env.ref('survey.survey_user_input_view_form').id
if self.survey_user_input_id:
action = self.env["ir.actions.actions"]._for_xml_id("survey.action_survey_user_input")
action['res_id'] = self.survey_user_input_id.id
action['domain'] = [('id', '=', self.survey_user_input_id.id)]
action['view_type'] = 'form'
action['view_mode'] = 'form'
action['binding_view_types'] = 'form'
action['view_id'] = form_view_id
action['views'] = [(form_view_id, 'form')]
return action

View File

@@ -0,0 +1,15 @@
# Copyright 2022 Tecnativa - David Vidal
# 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"
def _create_opportunity_post_process(self):
"""After lead creation from survey answer (module survey_crm_generation),
if answer (survey_user_input) contains event_products_ids, copy it in lead.
"""
self.opportunity_id.event_products_ids = self.event_products_ids.ids
return super(SurveyUserInput, self)._create_opportunity_post_process()

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_crm_case_opportunities_filter_survey_crm_generation_training" model="ir.ui.view">
<field name="name">crm.lead.search.opportunity.crm.generation.training"</field>
<field name="inherit_id" ref="crm.view_crm_case_opportunities_filter" />
<field name="model">crm.lead</field>
<field name="arch" type="xml">
<filter name="salesperson" position="before">
<filter string="Event product" name="event_product" context="{'group_by':'event_products_ids'}" />
</filter>
<field name="tag_ids" position="before">
<field name="event_products_ids" />
</field>
</field>
</record>
<record id="crm_lead_view_for_crm_generation_training" model="ir.ui.view">
<field name="model">crm.lead</field>
<field name="name">crm.lead.crm.generation.training</field>
<field name="inherit_id" ref="crm.crm_lead_view_form" />
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button class="oe_stat_button"
name="action_view_survey_user_input_id"
type="object"
icon="fa-wpforms"
help="View survey answer that created this lead"
attrs="{'invisible': [('survey_user_input_id', '=', False)]}"
>
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
Responses
</span>
</div>
</button>
</div>
<field name="survey_user_input_id" position="after">
<field
name="event_products_ids"
readonly="1"
attrs="{'invisible': [('event_products_ids', '=', False)]}"
widget="many2many_tags"
/>
</field>
</field>
</record>
<!-- CRM LEAD TREE VIEW -->
<record id="crm_case_tree_view_oppor_survey_crm_generation_training" model="ir.ui.view">
<field name="name">crm.lead.tree.opportunity.survey.crm.generation.training</field>
<field name="model">crm.lead</field>
<field name="priority">1</field>
<field name="inherit_id" ref="crm.crm_case_tree_view_oppor" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="event_products_ids" widget="many2many_tags" />
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,23 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey event base",
'summary': 'Add field to reference events and product events in servey answers (survey.user_input)',
'description': """
Add field to reference events and product events in servey answers (survey.user_input)
----------------------------------------------------
*
""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["event","survey_base"],
"data": [
],
"installable": True,
}

View File

@@ -0,0 +1 @@
from . import survey_user_input

View File

@@ -0,0 +1,61 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyUserInput(models.Model):
_inherit = 'survey.user_input'
events_ids = fields.Many2many('event.event', string='Events', compute="compute_events_ids", search="search_events_ids") #related events
event_products_ids = fields.Many2many('product.product', string='Event products', compute="compute_event_products_ids", search="search_event_products_ids") #related event products
def search_events_ids(self, operator, value):
user_input_ids = set()
user_input_lines = self.env['survey.user_input.line'].search([('record_reference_model','=','event.event'),('record_reference','=', value)])
for user_input_line in user_input_lines:
user_input_ids.add(user_input_line.user_input_id.id)
return [('id',operator,list(user_input_ids))]
@api.depends('user_input_line_ids')
def compute_events_ids(self):
"""set events_ids as answer of "event" question
"""
for user_input in self:
event_ids = []
for user_input in self:
for user_input_line in user_input.user_input_line_ids:
if user_input_line.record_reference_model == 'event.event':
event_ids.append(user_input_line.record_reference)
user_input.events_ids = event_ids
def search_event_products_ids(self, operator, value):
user_input_ids = set()
user_input_lines = self.env['survey.user_input.line'].search([('record_reference_model','=','product.product'),('record_reference','=', value)])
for user_input_line in user_input_lines:
user_input_ids.add(user_input_line.user_input_id.id)
return [('id',operator,list(user_input_ids))]
@api.depends('user_input_line_ids')
def compute_event_products_ids(self):
"""set event_products_ids as answer of "event product" or "multiple event products" question
"""
for user_input in self:
event_products_ids = []
for user_input in self:
for user_input_line in user_input.user_input_line_ids:
if user_input_line.record_reference_model == 'product.product':
event_products_ids.append(user_input_line.record_reference)
user_input.event_products_ids = event_products_ids

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,18 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey event registration generation",
"version": "16.0.0.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["survey", "survey_base"],
"data": [
'views/survey_question_views.xml',
'views/survey_survey_views.xml',
],
"installable": True,
}

View File

@@ -0,0 +1,3 @@
from . import survey_user_input
from . import survey_survey
from . import survey_question

View File

@@ -0,0 +1,38 @@
from email.policy import default
from odoo import models, fields, api
class SurveyQuestion(models.Model):
_inherit = 'survey.question'
event_registration_allowed_field_ids = fields.Many2many(
comodel_name="ir.model.fields",
compute="_compute_event_registration_allowed_field_ids",
) #fields of event registration, proposed in question, to associate answer to good event registration field, during event registration creation
event_registration_field = fields.Many2one(
string="Event registration field",
comodel_name="ir.model.fields",
domain="[('id', 'in', event_registration_allowed_field_ids)]",
) #field of event registration selected, used in event registration creation
@api.depends("question_type")
def _compute_event_registration_allowed_field_ids(self):
"""propose all event registration fields corresponding to selected question type
"""
type_mapping = {
"char_box": ["char", "text"],
"text_box": ["html"],
"numerical_box": ["integer", "float", "html", "char"],
"date": ["date", "text", "char"],
"datetime": ["datetime", "html", "char"],
"simple_choice": ["many2one", "html", "char"],
"multiple_choice": ["many2many", "html", "char"],
}
for record in self:
search_domain = [
("model", "=", "event.registration"),
("ttype", "in", type_mapping.get(record.question_type, [])),
]
record.event_registration_allowed_field_ids = self.env["ir.model.fields"].search(search_domain).ids

View File

@@ -0,0 +1,12 @@
# 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"
generate_registration = fields.Boolean(
help="Generate event registration for selected event",
)

View File

@@ -0,0 +1,82 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyUserInput(models.Model):
_inherit = 'survey.user_input'
registration_id = fields.Many2one('event.registration', 'Event registration') #registration created automaticaly on survey post
def _prepare_registration(self):
"""Extract registration values from the answers"""
elegible_inputs = self.user_input_line_ids.filtered(
lambda x: x.question_id.event_registration_field and not x.skipped
)
vals = {}
for line in elegible_inputs:
if line.question_id.event_registration_field.ttype == 'many2one':
vals[line.question_id.event_registration_field.name] = line.suggested_answer_id.record_reference
else:
vals[line.question_id.event_registration_field.name] = line[f"value_{line.answer_type}"]
return vals
def _create_registration_post_process(self, registration):
"""After creating the event registration send an internal message with the input link"""
registration.message_post_with_view(
"mail.message_origin_link",
values={"self": registration, "origin": self},
subtype_id=self.env.ref("mail.mt_note").id,
)
def _mark_done(self):
"""Generate registration when the survey is submitted"""
for user_input in self.filtered(
lambda r: r.survey_id.generate_registration and not self.registration_id
):
vals = user_input._prepare_registration()
# check doublon : if old draft registration already exists : update it
email = vals.get('email')
event_id = vals.get('event_id')
old_registration = False
if email and event_id:
old_registration = self.env["event.registration"].search([('email','=',email),('event_id','=',event_id)])
if old_registration:
old_registration = old_registration[0]
if old_registration.state == 'draft':
registration = old_registration
registration.write(vals)
registration.message_post_with_view(
"mail.message_origin_link",
values={"edit":True, "self": registration, "origin": user_input},
subtype_id=self.env.ref("mail.mt_note").id,
)
if not old_registration:
registration = self.env["event.registration"].create(vals)
self._create_registration_post_process(registration)
self.update({"registration_id": registration.id})
res = super()._mark_done()
# after all, set partner id of registration as the partner of user input
for user_input in self:
if user_input.registration_id and user_input.partner_id:
user_input.registration_id.partner_id = user_input.partner_id
return res

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ir_model_fields_survey_user,ir.model.fields.survey.user,base.model_ir_model_fields,survey.group_survey_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ir_model_fields_survey_user ir.model.fields.survey.user base.model_ir_model_fields survey.group_survey_user 1 0 0 0

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Survey question form -->
<record id="survey_question_form" model="ir.ui.view">
<field name="model">survey.question</field>
<field name="inherit_id" ref="survey.survey_question_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='comments_allowed']/.." position="after">
<group name="event_registration" string="Event registration">
<!-- event registration field, filtered by event_registration_allowed_field_ids (invisible) -->
<field name="event_registration_field" widget="selection" />
<field name="event_registration_allowed_field_ids" invisible="1" />
</group>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Survey form -->
<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">
<group name="options" position="inside">
<group name="event_registration_options" string="Events registrations">
<!-- Possibility to generate event registration -->
<field name="generate_registration" />
</group>
</group>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,25 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey event generation attachment",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
'summary': 'Link Attachments from Surveys to generated event registration',
'description': """
Link Attachments from Surveys to generated event registration
----------------------------------------------------
* Create new attachments on event registration creation, based on attached file of survey answer (survey.user_input.line)
""",
"depends": ["survey_base","survey_event_registration_generation"],
"data": [
],
"installable": True,
"auto_install":True
}

View File

@@ -0,0 +1 @@
from . import survey_user_input

View File

@@ -0,0 +1,32 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyUserInput(models.Model):
_inherit = 'survey.user_input'
def _mark_done(self):
"""Copy attachments to event registration"""
res = super()._mark_done()
for user_input in self:
if user_input.survey_id.generate_registration and user_input.registration_id:
for user_input_line in user_input.user_input_line_ids:
if user_input_line.value_file:
self.env['ir.attachment'].create({
'res_model':'event.registration',
'res_id':user_input.registration_id.id,
'name': user_input_line.value_file_fname,
'datas': user_input_line.value_file,
'type': 'binary'
})
return res

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,18 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey event speaker generation",
"version": "16.0.0.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["survey", "event_speaker"],
"data": [
'views/mail_templates_chatter.xml',
'views/survey_survey_views.xml',
],
"installable": True,
}

View File

@@ -0,0 +1,56 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_event_speaker_generation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-19 14:42+0000\n"
"PO-Revision-Date: 2023-09-19 14:42+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: survey_event_speaker_generation
#: model_terms:ir.ui.view,arch_db:survey_event_speaker_generation.survey_form
msgid "Events speakers"
msgstr "Intervenants d'événements"
#. module: survey_event_speaker_generation
#: model:ir.model.fields,field_description:survey_event_speaker_generation.field_survey_user_input__speaker_id
msgid "Event speaker"
msgstr "Intervenants"
#. module: survey_event_speaker_generation
#: model:ir.model.fields,field_description:survey_event_speaker_generation.field_survey_survey__generate_speaker
msgid "Generate Speaker"
msgstr "Générer un intervenant"
#. module: survey_event_speaker_generation
#: model:ir.model.fields,help:survey_event_speaker_generation.field_survey_survey__generate_speaker
msgid "Generate speaker for selected event"
msgstr "Générer un intervenant pour l'événement selectionné"
#. module: survey_event_speaker_generation
#: model:ir.model,name:survey_event_speaker_generation.model_survey_survey
msgid "Survey"
msgstr "Questionnaire"
#. module: survey_event_speaker_generation
#: model:ir.model,name:survey_event_speaker_generation.model_survey_user_input
msgid "Survey User Input"
msgstr "Entrée utilisateur du questionnaire"
#. module: survey_event_speaker_generation
#: model_terms:ir.ui.view,arch_db:survey_event_speaker_generation.message_event_speaker_assigned
msgid "from survey answers"
msgstr "depuis la réponse au questionnaire"
#. module: survey_event_speaker_generation
#: model_terms:ir.ui.view,arch_db:survey_event_speaker_generation.message_event_speaker_assigned
msgid "has been assigned to"
msgstr "a été assigné à"

View File

@@ -0,0 +1,2 @@
from . import survey_user_input
from . import survey_survey

View File

@@ -0,0 +1,11 @@
# 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"
generate_speaker = fields.Boolean(
help="Generate speaker for selected event",
) #Field to check if user wants to generate a speaker on survey submit

View File

@@ -0,0 +1,46 @@
import logging
import textwrap
import uuid
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _, Command
from odoo.exceptions import ValidationError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
class SurveyUserInput(models.Model):
_inherit = 'survey.user_input'
speaker_id = fields.Many2one('res.partner', 'Event speaker') #created partner when submit survey
def _get_event(self):
"""Find event selected, in all answers"""
for line in self.user_input_line_ids:
if line.question_id.question_type == 'event' and not line.skipped \
and line.answer_type != "suggestion" \
and line.question_id.event_registration_field.name != "comment":
return line.value_event
def _create_speaker_post_process(self, speaker):
"""Add message to chatter to note speaker creation and association with event"""
speaker.message_post_with_view(
"survey_event_speaker_generation.message_event_speaker_assigned",
values={"speaker": speaker, "user_input": self, "event":self._get_event()},
subtype_id=self.env.ref("mail.mt_note").id,
)
def _mark_done(self):
"""Attach partner as speaker of event"""
res = super()._mark_done()
for user_input in self.filtered(lambda r: r.survey_id.generate_speaker and not r.speaker_id and r.partner_id):
user_input.update({"speaker_id": user_input.partner_id.id})
user_input._get_event().speakers = [Command.link(user_input.speaker_id.id)]
user_input._create_speaker_post_process(user_input.speaker_id)
return res

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- Message notification in res.partner chatter -->
<template id="message_event_speaker_assigned">
<div style="margin: 0px; padding: 0px; font-size: 13px;">
<a href="#" t-att-data-oe-model="speaker._name" t-att-data-oe-id="speaker.id">
<t t-esc="speaker.name" />
</a>
has been assigned as speaker to
<a href="#" t-att-data-oe-model="event._name" t-att-data-oe-id="event.id">
<t t-esc="event.display_name" />
</a>
from survey answers
<a href="#" t-att-data-oe-model="user_input._name" t-att-data-oe-id="user_input.id">
<t t-esc="user_input.display_name" />
</a>
</div>
</template>
</data>
</odoo>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Survey form -->
<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">
<group name="options" position="inside">
<group name="event_speaker_options" string="Events speakers">
<field name="generate_speaker" />
</group>
</group>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,24 @@
# Copyright 2016-2020 Akretion France (<https://www.akretion.com>)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey event visibility",
"version": "16.0.0.0.0",
"license": "AGPL-3",
"author": "Elabore",
'summary': 'Add visibility field in event stage',
'description': """
Add visibility information in product and events
----------------------------------------------------
The product and event visibility is computed from event stage visibility.
""",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["survey"],
"data": [
'views/event_stage_views.xml',
],
"installable": True,
}

View File

@@ -0,0 +1,3 @@
from . import event_stage
from . import event_event
from . import product_product

View File

@@ -0,0 +1,28 @@
from odoo import models, fields, api
class EventEvent(models.Model):
_inherit = 'event.event'
visible_in_survey = fields.Boolean('Visible in survey', related='stage_id.visible_in_survey', readonly=True,
help="""Events in step configured as 'visible in survey'.""")
def get_events_from_event_products(self, product_ids=None, only_visible_in_survey=False):
"""Search events in stage filtered by product present in ticket..
Args:
product_ids (list[product.product ids], optional): to filter only events with tickets using product in this list. Defaults to None.
Returns:
event.event: Events
"""
event_search = []
if product_ids:
event_tickets = self.env['event.event.ticket'].search([('product_id','in',product_ids)])
event_search.append(('event_ticket_ids','in',event_tickets.ids))
if only_visible_in_survey:
event_search.append(('visible_in_survey','=',True))
return self.env['event.event'].search(event_search)

View File

@@ -0,0 +1,7 @@
from odoo import models, fields, api
class EventStage(models.Model):
_inherit = 'event.stage'
visible_in_survey = fields.Boolean('Visible in surveys') #if checked, only events on this stage are visible in surveys

View File

@@ -0,0 +1,23 @@
from odoo import models, fields, api
class ProductProduct(models.Model):
_inherit = 'product.product'
visible_in_survey = fields.Boolean('Visible in survey', compute='_compute_visible_in_survey', readonly=True,
help="""Events in step configured as 'visible in survey'.""")
def _compute_visible_in_survey(self):
#get all events in step 'visible in survey'
product_ids = set()
events = self.env['event.event'].search([('visible_in_survey','=',True)])
for event in events:
for ticket in event.event_ticket_ids:
product_ids.add(ticket.product_id.id)
for event_product in self:
if event_product.id in product_ids:
event_product.visible_in_survey = True
else:
event_product.visible_in_survey = False

View File

@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<odoo>
<data>
<!-- Event stage view form -->
<!-- * Add field visible_in_survey -->
<record id="event_stage_view_form_survey_event_registration_generation" model="ir.ui.view">
<field name="name">event.stage.view.form.survey.event.registration.generation</field>
<field name="model">event.stage</field>
<field name="inherit_id" ref="event.event_stage_view_form" />
<field name="arch" type="xml">
<field name="pipe_end" position="after">
<field name="visible_in_survey" />
</field>
</field>
</record>
<!-- Event stage view tree -->
<!-- * Add field visible_in_survey -->
<record id="event_stage_view_tree_survey_event_registration_generation" model="ir.ui.view">
<field name="name">event.stage.view.tree.survey.event.registration.generation</field>
<field name="model">event.stage</field>
<field name="inherit_id" ref="event.event_stage_view_tree" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="visible_in_survey" />
</field>
</field>
</record>
</data>
</odoo>