[ADD] survey_record_generation : migrate from 16 to 18
Some checks failed
pre-commit / pre-commit (pull_request) Failing after 1m30s

This commit is contained in:
2026-03-24 17:29:49 +01:00
parent eced098fbb
commit 94d3bd72b1
28 changed files with 3135 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
========================
Survey record generation
========================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:03eeb5c11b7d8330051c416331c84103b2641351fdc1d0a1bad43acd09501a33
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/16.0/survey_record_generation
:alt: OCA/survey
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/survey-16-0/survey-16-0-survey_record_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=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to generate any record from surveys answers.
**Table of contents**
.. contents::
:local:
Use Cases / Context
===================
In several cases we want to use survey application to create records.
Website form application give the same possibility but the design is integrated on the web site.
Futhermore this module add other functionnalities like "duplicate" check, or creation of several interconnected records
Typical use case : Information request form
#. Submitting the form will create a contact (partner) only if it does not already exist in the database.
#. Submitting the form will also create a crm opportunity linked to previously created partner
Configuration
=============
Record generation configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. |Image of record creation list| image:: https://raw.githubusercontent.com/OCA/survey/16.0/survey_record_generation/static/description/record-creations.png
#. Go to the the survey
#. In *Record creation* tab add a new line
#. Set a name for created record, select the Model of the record (eg: Prospect)
#. Add a field configuration. So for each field :
.. |Image of record creation fields| image:: https://raw.githubusercontent.com/OCA/survey/16.0/survey_record_generation/static/description/record-creation-customer.png
#. You can check "unicity constraint" to prevent duplicates.
In case of duplicates and if other record use this record to fill a m2o field, the founded record will be used
#. You can configure explicitly where Odoo should retrieve the value of field :
* **fixed**: To set explicit value
* **question**: If value come from user's answer
For m2o or m2m links, question should be configured before. See Question answers configuration section below.
* **other created record**: If value come from other created record (m2o case only)
Question answers configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In a survey question : to configure value of choices
.. |Question answers| image:: https://raw.githubusercontent.com/OCA/survey/16.0/survey_record_generation/static/description/question-answers.png
#. Configure a multiple choice question
#. Select value type associated to answer:
* **Value** > eg: to fill a selection field
* **Record** > to fill m2o or m2m field
#. In case of *record* type:
#. Select referenced model
#. You can directly fill answers (eventualy with help of the domain field)
#. Or create new answers and set associated record
#. In case of *value* type:
#. Add new selectable answers and set associated value
Usage
=====
When a survey is properly configured, once it is submited by a user, records should be created.
To find records generated from a participation go to:
#. Survey > participation
#. Click on **# Generated records** button (top-right)
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_record_generation%0Aversion:%2016.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
~~~~~~~
* Elabore
Contributors
~~~~~~~~~~~~
* `Elabore <https://www.elabore.coop>`_
* Clément Thomas
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.
This module is part of the `OCA/survey <https://github.com/OCA/survey/tree/16.0/survey_record_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,28 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Survey record generation",
'summary': 'Allow to create record of any model when sending the form',
'description': """
Allow to create record of any model when sending the form :
----------------------------------------------------
* Choose list of models created on survey submission
* Set default values for record created
* Associate question with fields
* For x2m fields : Associate values to questions
""",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",
"category": "",
"depends": ["survey"],
"data": [
"security/ir.model.access.csv",
"views/survey_survey_views.xml",
"views/survey_question_views.xml",
"views/survey_user_input_views.xml",
"views/survey_generated_record_views.xml",
],
"installable": True,
}

View File

@@ -0,0 +1,540 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * survey_record_generation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-13 16:41+0000\n"
"PO-Revision-Date: 2025-11-13 16:41+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_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation_field_values__value_origin
msgid ""
"* Fixed: you can set the value in value field\n"
" * Question: Response of the question will set the value. If you do not see your question, maybe the type of question do not match the type of field\n"
" * From other created record: You can set other record creation to link several created records. Can only be used with many2one fields."
msgstr ""
"* Fixé: Vous pouvez attribuer une valeur au champ\n"
" * Question: La réponse à la question attribuera la valeur. Si vous ne voyez pas votre question, peut-être que le type de question ne correspond pas au type de champ\n"
" * Depuis un autre enregistrement créé: Vous pouvez attribuer un autre enregistrement créé pour lier plusieurs enregistrements. Peut être utilisé uniquement avec les champs one2many."
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_generated_record_view_search
msgid "Active survey input"
msgstr "Participation active"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__allowed_field_ids
msgid "Allowed Fields"
msgstr "Champs acceptés"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__allowed_question_ids
msgid "Allowed Question"
msgstr "Question autorisée"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#, python-format
msgid "Answer to question: %s"
msgstr "Réponse à la question : %s"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question__answer_values_type
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question_answer__answer_values_type
msgid "Associate value to answer"
msgstr "Associer une valeur à la réponse"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_user_input__generated_records_count
msgid "Attempts Count"
msgstr ""
#. module: survey_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation__field_to_retrieve_existing_records
msgid ""
"Choose the field you want to use to retrieve the existing record. WARNING: "
"We update only the first record found."
msgstr ""
"Choisissez le champs à partir duquel nous allons chercher l'enregistrement "
"existant. Attention : nous mettons à jour seulement le premier "
"enregistrement trouvé."
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__create_uid
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__create_uid
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__create_uid
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__create_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__create_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__create_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__create_date
msgid "Created on"
msgstr "Créé le"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__display_name
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__display_name
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__display_name
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__display_name
msgid "Display Name"
msgstr ""
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question__fill_domain
msgid "Domain"
msgstr "Domaine"
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_question_form
msgid "Empty and fill"
msgstr "Vider et remplir"
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_question_form
msgid ""
"Empty the list and fill it with all items of selected model matching domain"
msgstr ""
"Vide la liste et la remplie avec tous les enregistrements du modèle "
"sélectionné qui correspondent au domaine"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__field_id
msgid "Field"
msgstr "Champ"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__field_to_retrieve_existing_records
msgid "Field To Retrieve Existing Records"
msgstr "Champs pour retrouver l'enregistrement existant"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__field_type
msgid "Field Type"
msgstr "Type de champ"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#, python-format
msgid "Field type is : <b>%s</b>"
msgstr "Le type de champ est : <b>%s</b>"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__field_values_ids
msgid "Field values"
msgstr "Valeurs des champs"
#. module: survey_record_generation
#: model:ir.model.fields.selection,name:survey_record_generation.selection__survey_record_creation_field_values__value_origin__fixed
msgid "Fixed"
msgstr "Fixé"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_many2many
msgid "Fixed Value Many2Many"
msgstr "Valeur fixée Many2Many"
#. module: survey_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation_field_values__field_relation
msgid "For relationship fields, the technical name of the target model"
msgstr "Pour les champs relationnels, le nom technique du modèle ciblé"
#. module: survey_record_generation
#: model:ir.model.fields.selection,name:survey_record_generation.selection__survey_record_creation_field_values__value_origin__other_record
msgid "From other created record"
msgstr "Depuis un autre enregistrement créé"
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_generated_record_view_tree
msgid "Generated from survey input"
msgstr "Généré depuis la participation"
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_generated_record_view_tree
msgid "Generated record type"
msgstr "Type d'enregistrement généré"
#. module: survey_record_generation
#: model:ir.actions.act_window,name:survey_record_generation.survey_generated_record_action
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_user_input__generated_record_ids
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_user_input_view_form_survey_record_generation
msgid "Generated records"
msgstr "Enregistrements générés"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__field_help
msgid "Help"
msgstr "Aide"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__id
msgid "ID"
msgstr ""
#. module: survey_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation__ignore_if_mandatory_field_is_missing
msgid ""
"If a mandatory field is missing when trying to create the record, an error "
"is raised when the survey is submitted. If this option is activated, the "
"error is ignored."
msgstr ""
"Si un champs requis est manquant lors de la création de l'enregistrement, "
"une erreur est levée lors de la soumission du formulaire. "
"En activant cette option, l'erreur sera ignorée."
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__ignore_if_mandatory_field_is_missing
msgid "Ignore creation if a mandatory field is missing"
msgstr "Ignorer la création si un champs requis est manquant"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record____last_update
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation____last_update
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values____last_update
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m____last_update
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__write_uid
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__write_uid
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__write_uid
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__write_uid
msgid "Last Updated by"
msgstr "Dernière modification par"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__write_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__write_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__write_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__write_date
msgid "Last Updated on"
msgstr "Dernière modification le"
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_generated_record_view_tree
msgid "Link to generated record"
msgstr "Lien vers l'enregistrement généré"
#. module: survey_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_survey__survey_record_creation_ids
msgid "List of records created when survey submitted"
msgstr "Liste des enregistrements créés lorsque le sondage est envoyé"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question__model_id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question__model_name
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question_answer__model_id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__model_id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__model_id
msgid "Model"
msgstr "Modèle"
#. module: survey_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation__model_id
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation_field_values__model_id
msgid "Model of generated record"
msgstr "Modèle de l'enregistrement généré"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__survey_record_creation_name
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__name
msgid "Name"
msgstr "Nom"
#. module: survey_record_generation
#: model_terms:ir.actions.act_window,help:survey_record_generation.survey_generated_record_action
msgid "No generated records found"
msgstr "Pas d'enregistrements générés trouvés"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_question.py:0
#: code:addons/survey_record_generation/models/survey_question.py:0
#, python-format
msgid "No record found in %s"
msgstr "Pas d'enregistrements trouvés parmis %s"
#. module: survey_record_generation
#: model:ir.model.fields.selection,name:survey_record_generation.selection__survey_question__answer_values_type__no
msgid "No values"
msgstr "Pas de valeur"
#. module: survey_record_generation
#: model:ir.model.fields,help:survey_record_generation.field_survey_record_creation_field_values__unicity_check
msgid ""
"On record creation, if another record exists with same value, record will "
"not be created."
msgstr ""
"Lors de la création d'un enregistrement, si un autre enregistrement existe "
"avec la même valeur, l'enregistrement ne sera pas créé."
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_survey_view_form
msgid ""
"Only the first matched record will be updated.\n"
" Also to be noticed, the unicity check feature has priority over updating the existing record."
msgstr ""
"Attention, seul le premier enregistrement trouvé sera mis à jour. Aussi, si "
"vous avez des champs avec une contrainte d'unicité, cette contrainte aura la"
" priorité sur la mise à jour des enregistrements."
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#, python-format
msgid "Other created record: "
msgstr "Autre enregistrement créé : "
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__other_created_record_id
msgid "Other record"
msgstr "Autre enregistrement"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__user_input_id
msgid "Participation"
msgstr ""
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__question_id
#: model:ir.model.fields.selection,name:survey_record_generation.selection__survey_record_creation_field_values__value_origin__question
msgid "Question"
msgstr ""
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_many2one
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__value_reference
#: model:ir.model.fields.selection,name:survey_record_generation.selection__survey_question__answer_values_type__record
msgid "Record"
msgstr "Enregistrement"
#. module: survey_record_generation
#: model_terms:ir.ui.view,arch_db:survey_record_generation.survey_survey_view_form
msgid "Record creation"
msgstr "Création d'un enregistrement"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_survey__survey_record_creation_ids
msgid "Records creation"
msgstr "Création d'enregistrements"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__created_record_id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question_answer__record_id
msgid "Referenced record"
msgstr "Enregistrement référencé"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__field_relation
msgid "Related Model"
msgstr "Modèle relatif"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_record_creation.py:0
#: code:addons/survey_record_generation/models/survey_record_creation.py:0
#, python-format
msgid "Some required fields are not set : %s"
msgstr "Certains champs requis ne sont pas remplis : %s"
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_survey
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__survey_id
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__survey_id
msgid "Survey"
msgstr "Sondage"
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_question_answer
msgid "Survey Label"
msgstr "Étiquette du sondage"
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_question
msgid "Survey Question"
msgstr "Question du sondage"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__survey_record_creation_id
msgid "Survey Record Creation"
msgstr "Sondage Création d'enregistrements"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values_x2m__survey_record_creation_field_values_id
msgid "Survey Record Creation Field Values"
msgstr "Sondage Création d'enregistrement Valeur des champs"
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_user_input
msgid "Survey User Input"
msgstr "Saisie utilisateur du sondage"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__survey_record_creation_id
msgid "Survey record creation"
msgstr "Génération d'enregistrement depuis la participation"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_user_input.py:0
#, python-format
msgid ""
"The field %(field)s is mandatory for model %(model)s. In Record Creation "
"tab, drag %(record)s on top of the model %(model)s."
msgstr ""
"Le champs %(field)s est obligatoire pour le modèle %(model)s. Dans l'onglet "
"Création d'un enregistrement, placez la ligne %(record)s au dessus de la "
"ligne du modèle %(model)s."
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_user_input.py:0
#, python-format
msgid ""
"The field %s is mandatory. In Record Creation tab, drag %s at the top of the"
" table"
msgstr ""
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__unicity_check
msgid "Unicity constraint"
msgstr "Contrainte d'unicité"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__update_existing_records
msgid "Update existing records"
msgstr "Mettre à jour les enregistrements existants"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_question_answer__value_char
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__displayed_value
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_boolean
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_char
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_date
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_datetime
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_float
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_html
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_integer
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_selection
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__fixed_value_text
#: model:ir.model.fields.selection,name:survey_record_generation.selection__survey_question__answer_values_type__value
msgid "Value"
msgstr "Valeur"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation_field_values__value_origin
msgid "Value origin"
msgstr "Origine de la valeur"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__warning_message
msgid "Warning message"
msgstr "Message d'erreur"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#, python-format
msgid "You should append at least one record in %s"
msgstr "Vous devez au moins ajouter un enregistrement dans %s"
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_user_input.py:0
#, python-format
msgid ""
"[Survey record generation] The answer values type '%(type)s' is not "
"supported (for question %(question)s). Use 'record' or 'value' instead."
msgstr ""
"[Survey record generation] La valeur '%(type)s' pour le champs "
"'answer_values_type' n'est pas supportée (pour la question %(question)s). "
"Veuillez utiliser 'Enregistrement' ou 'Valeur' à la place."
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_user_input.py:0
#, python-format
msgid ""
"[Survey record generation] The boolean value %s(value)s is not supported "
"(for question %(question)s)."
msgstr ""
"[Survey record generation] La valeur booléenne %s(value)s n'est pas "
"supportée (pour la question %(question)s)."
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_user_input.py:0
#, python-format
msgid ""
"[Survey record generation] The question type %(type)s is not recognized (for"
" question %(question)s)."
msgstr ""
"[Survey record generation] Le type de question %(type)s n'est pas reconnu "
"(pour la question %(question)s)."
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_user_input.py:0
#, python-format
msgid ""
"[Survey record generation] The question type %(type)s is not supported yet."
msgstr ""
"[Survey record generation] Le type de question %(type)s n'est pas encore "
"supporté."
#. module: survey_record_generation
#. odoo-python
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#: code:addons/survey_record_generation/models/survey_record_creation_field_values.py:0
#, python-format
msgid "possible values are %s"
msgstr "les valeurs possibles sont %s"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_record_creation__sequence
msgid "sequence"
msgstr ""
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_generated_record
msgid "survey.generated.record"
msgstr ""
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_record_creation
msgid "survey.record.creation"
msgstr ""
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_record_creation_field_values
msgid "survey.record.creation.field.values"
msgstr ""
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_record_creation_field_values_x2m
msgid "survey.record.creation.field.values.x2m"
msgstr ""

View File

@@ -0,0 +1,7 @@
from . import survey_question_answer
from . import survey_question
from . import survey_record_creation_field_values
from . import survey_record_creation
from . import survey_survey
from . import survey_user_input
from . import survey_generated_record

View File

@@ -0,0 +1,15 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
class SurveyGeneratedRecord(models.Model):
_name = "survey.generated.record"
survey_record_creation_name = fields.Char('Name', readonly=True)
survey_record_creation_id = fields.Many2one('survey.record.creation', 'Survey record creation', readonly=True)
user_input_id = fields.Many2one('survey.user_input', 'Participation', readonly=True)
created_record_id = fields.Reference(string="Referenced record", selection='_selection_target_model', readonly=True)
@api.model
def _selection_target_model(self):
return [(model.model, model.name) for model in self.env['ir.model'].sudo().search([])]

View File

@@ -0,0 +1,49 @@
import logging
import ast
from typing import Literal
from odoo import api, fields, models, _, Command
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
AnswerValuesType: Literal["no", "value", "record"]
class SurveyQuestion(models.Model):
_inherit = 'survey.question'
model_id = fields.Many2one('ir.model', string="Model")
model_name = fields.Char(related="model_id.model")
fill_domain = fields.Char("Domain", default="[]")
answer_values_type = fields.Selection([('no', 'No values'),('value','Value'),('record','Record')], string="Associate value to answer", default="no", required=True)
@api.onchange('model_id')
def onchange_model_id(self):
self.fill_domain = "[]"
if self.model_id:
rec = self.env[self.model_id.model].search([], limit=1)
if not rec:
raise UserError(_('No record found in %s',self.model_id.name))
else:
for answer in self.suggested_answer_ids:
answer.record_id = f"{self.model_id.model},{rec.id}"
def fill(self):
for question in self:
if question.model_id:
new_suggested_answer_ids = [Command.clear()]
record_model = question.model_id.model
if question.fill_domain:
domain = ast.literal_eval(question.fill_domain)
else:
domain = []
records = self.env[record_model].search(domain)
new_suggested_answer_ids += [Command.create({'value':record.display_name, 'record_id':f"{record_model},{record.id}"
}) for record in records]
question.suggested_answer_ids = new_suggested_answer_ids

View File

@@ -0,0 +1,42 @@
import logging
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
class SurveyQuestionAnswer(models.Model):
_inherit = 'survey.question.answer'
record_id = fields.Reference(string="Referenced record", selection='_selection_target_model')
model_id = fields.Many2one('ir.model', related="question_id.model_id")
answer_values_type = fields.Selection(related="question_id.answer_values_type")
value_char = fields.Char('Value')
@api.model
def _selection_target_model(self):
return [(model.model, model.name) for model in self.env['ir.model'].sudo().search([])]
@api.onchange('record_id')
def onchange_record_id(self):
if self.record_id:
self.value = self.record_id.display_name
@api.model
def default_get(self, fields):
result = super().default_get(fields)
if (
not result.get("model_id")
or "record_id" not in fields
):
return result
model = self.env['ir.model'].browse(result.get("model_id")).model
res = self.env[model].search([], limit=1)
if res:
result["record_id"] = "%s,%s" % (
model,
res.id,
)
return result

View File

@@ -0,0 +1,66 @@
import logging
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
class SurveyRecordCreation(models.Model):
"""Configure list of models for wich record will be created on survey submission
"""
_name = 'survey.record.creation'
name = fields.Char('Name')
survey_id = fields.Many2one('survey.survey', string="Survey")
model_id = fields.Many2one('ir.model', "Model", help="Model of generated record")
field_values_ids = fields.One2many('survey.record.creation.field.values', 'survey_record_creation_id', string="Field values")
warning_message = fields.Html('Warning message', compute="_compute_warning_message")
sequence = fields.Integer("sequence")
update_existing_records = fields.Boolean(
string="Update existing records",
)
field_to_retrieve_existing_records = fields.Many2one(
"ir.model.fields",
domain="[('id', 'in', allowed_field_ids)]",
ondelete="cascade",
help="Choose the field you want to use to retrieve the existing record. "
"WARNING: We update only the first record found.",
)
allowed_field_ids = fields.Many2many(
"ir.model.fields",
compute="_compute_allowed_field_ids",
store=True,
string="Allowed Fields",
)
ignore_if_mandatory_field_is_missing = fields.Boolean(
string="Ignore creation if a mandatory field is missing",
help="If a mandatory field is missing when trying to create the record, "
"an error is raised when the survey is submitted. "
"If this option is activated, the error is ignored."
)
@api.depends("field_values_ids.field_id")
def _compute_allowed_field_ids(self):
for record in self:
record.allowed_field_ids = record.field_values_ids.mapped("field_id")
@api.onchange("model_id")
def clear_field_values_ids(self):
self.field_values_ids = None
@api.depends("model_id","field_values_ids")
def _compute_warning_message(self):
for record_creation in self:
# check if all mandatory fields set
if record_creation.model_id:
required_field_ids = self.model_id.field_id.filtered(lambda f:f.required and "property_" not in f.name)
set_field_ids = self.field_values_ids.field_id
missing_fields = required_field_ids - set_field_ids
if missing_fields:
record_creation.warning_message = _("Some required fields are not set : %s",', '.join([f"<b>{f.field_description}</b> (<i>{f.name}</i>)" for f in missing_fields]))
else:
record_creation.warning_message = None
else:
record_creation.warning_message = None

View File

@@ -0,0 +1,197 @@
import logging
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.tools.misc import format_date
_logger = logging.getLogger(__name__)
type_mapping = { #field types on the left, question types on the right. TODO : what about booleans ?
"char": ["char_box", "numerical_box", "date", "datetime", "simple_choice", "multiple_choice"],
"text": ["char_box", "date", "simple_choice"],
"html": ["text_box", "numerical_box", "datetime", "simple_choice"],
"integer": ["numerical_box"],
"float": ["numerical_box"],
"date": ["date"],
"datetime": ["datetime"],
"many2one": ["simple_choice"],
"many2many": ["multiple_choice"],
"selection": ["char_box", "simple_choice"]
}
class SurveyRecordCreationFieldValues(models.Model):
"""Configure default values of records created on survey submission
"""
_name = 'survey.record.creation.field.values'
survey_record_creation_id = fields.Many2one('survey.record.creation')
survey_id = fields.Many2one('survey.survey', related="survey_record_creation_id.survey_id")
model_id = fields.Many2one('ir.model', related="survey_record_creation_id.model_id")
field_id = fields.Many2one(
'ir.model.fields',
domain="[('model_id','=',model_id),('ttype','in',['char','selection','text','html','integer','float','date','datetime','many2one','many2many', 'boolean'])]",
ondelete="cascade")
field_relation = fields.Char(related='field_id.relation')
field_type = fields.Selection(related="field_id.ttype")
field_help = fields.Html('Help', compute="_compute_field_help")
value_origin = fields.Selection(
[('fixed','Fixed'),('question','Question'),('other_record','From other created record')],
string="Value origin",
required=True,
default='fixed',
help="""* Fixed: you can set the value in value field
* Question: Response of the question will set the value. If you do not see your question, maybe the type of question do not match the type of field
* From other created record: You can set other record creation to link several created records. Can only be used with many2one fields.""")
fixed_value_many2one = fields.Reference(string='Record', selection='_selection_target_model')
fixed_value_many2many = fields.One2many('survey.record.creation.field.values.x2m', "survey_record_creation_field_values_id")
fixed_value_char = fields.Char("Value")
fixed_value_selection = fields.Char("Value")
fixed_value_text = fields.Text("Value")
fixed_value_html = fields.Html("Value")
fixed_value_integer = fields.Integer("Value")
fixed_value_float = fields.Float("Value")
fixed_value_date = fields.Date("Value")
fixed_value_datetime = fields.Datetime("Value")
fixed_value_boolean = fields.Boolean("Value")
displayed_value = fields.Char("Value", compute="_compute_displayed_value")
other_created_record_id = fields.Many2one("survey.record.creation", string="Other record", domain="[('survey_id','=',survey_id),('model_id.model','=',field_relation)]")
allowed_question_ids = fields.Many2many('survey.question', compute='_compute_allowed_question_ids')
question_id = fields.Many2one('survey.question', string="Question", domain="[('id','in',allowed_question_ids)]")
unicity_check = fields.Boolean('Unicity constraint', help="On record creation, if another record exists with same value, record will not be created.")
@api.depends("field_id")
def _compute_field_help(self):
for record in self:
field_help = _("Field type is : <b>%s</b>",record.field_type)
if record.field_type == "selection":
field_help += "<br />"+_("possible values are %s",', '.join([f"<b>{s.value}</b> <i>({s.name})</i>" for s in record.field_id.selection_ids]))
record.field_help = field_help
@api.depends('field_id')
def _compute_allowed_question_ids(self):
for record_creation_field_values in self:
if not record_creation_field_values.survey_id or not record_creation_field_values.field_id:
record_creation_field_values.allowed_question_ids = None
continue
question_domain = [('survey_id','=',record_creation_field_values.survey_id.id)]
if record_creation_field_values.field_id.ttype in ['many2one','many2many']:
question_domain.extend(['|','&',('answer_values_type','=','record'),('model_id','=',record_creation_field_values.field_id.relation),('answer_values_type','=','value')])
if record_creation_field_values.field_id.ttype in type_mapping:
question_domain.append(('question_type','in',type_mapping[record_creation_field_values.field_id.ttype]))
record_creation_field_values.allowed_question_ids = self.env['survey.question'].search(question_domain)
@api.model
def _selection_target_model(self):
return [(model.model, model.name) for model in self.env['ir.model'].sudo().search([])]
def clean_values(self):
# clean values
self.fixed_value_many2many = None
self.fixed_value_many2one = None
self.fixed_value_char = None
self.fixed_value_selection = None
self.fixed_value_text = None
self.fixed_value_html = None
self.fixed_value_integer = None
self.fixed_value_float = None
self.fixed_value_date = None
self.fixed_value_datetime = None
self.other_created_record_id = None
self.question_id = None
@api.onchange('field_id')
def _onchange_field_id(self):
# clean values
self.clean_values()
# Set reference field model and select first record
if self.field_id and self.field_id.ttype == 'many2one' and self.field_id.relation:
rec = self.env[self.field_id.relation].search([], limit=1)
if rec:
self.fixed_value_many2one = f"{self.field_id.relation},{rec.id}"
else:
model_name = self.env['ir.model'].search([('model','=',self.field_id.relation)]).name
self.fixed_value_many2one = None
raise UserError(_('You should append at least one record in %s',model_name))
else:
self.fixed_value_many2one = None
def get_fixed_value_for_record_creation(self):
"""return val used in create() method
"""
if self.value_origin == 'fixed':
if self.field_type == 'many2one':
if self.fixed_value_many2one:
return self.fixed_value_many2one.id
elif self.field_type == 'many2many':
return [m2m.value_reference.id for m2m in self.fixed_value_many2many if m2m.value_reference]
else:
return self["fixed_value_"+self.field_type]
@api.onchange("fixed_value_char","fixed_value_selection","fixed_value_text","fixed_value_html","fixed_value_integer","fixed_value_float","fixed_value_date","fixed_value_datetime",'fixed_value_many2one', "fixed_value_many2many","other_created_record_id","question_id")
def _compute_displayed_value(self):
for record in self:
if record.field_id:
if record.value_origin == 'other_record' and record.other_created_record_id:
record.displayed_value = _("Other created record: ")+record.other_created_record_id.name
elif record.value_origin == 'fixed':
if record.field_id.ttype == "many2one":
if record.fixed_value_many2one:
record.displayed_value = record.fixed_value_many2one.display_name
else:
record.displayed_value = None
elif record.field_id.ttype == "many2many":
if record.fixed_value_many2many:
record.displayed_value = ", ".join([r.value_reference.display_name for r in record.fixed_value_many2many if r.value_reference])
else:
record.displayed_value = None
elif record.field_id.ttype == "date":
record.displayed_value = format_date(self.env, record.fixed_value_date)
elif record.field_id.ttype == "datetime":
record.displayed_value = format_date(self.env, record.fixed_value_datetime)
else:
record.displayed_value = str(record['fixed_value_'+record.field_id.ttype])
else: #value_origin = question
record.displayed_value = _('Answer to question: %s',record.question_id.title)
else:
record.displayed_value = ""
class SurveyRecordCreationFieldValuesX2m(models.Model):
"""O2m an M2m default values
"""
_name = 'survey.record.creation.field.values.x2m'
survey_record_creation_field_values_id = fields.Many2one('survey.record.creation.field.values')
value_reference = fields.Reference(string='Record', selection='_selection_target_model')
@api.model
def _selection_target_model(self):
return [(model.model, model.name) for model in self.env['ir.model'].sudo().search([])]
@api.onchange('survey_record_creation_field_values_id')
def _onchange_model_name(self):
# Set reference field model and select first record
field = self.survey_record_creation_field_values_id.field_id
if field and "2many" in field.ttype and field.relation:
rec = self.env[field.relation].search([], limit=1)
if rec:
self.value_reference = f"{field.relation},{rec.id}"
else:
model_name = self.env['ir.model'].search([('model','=',field.relation)]).name
raise ValueError(_('You should append at least one record in %s',(model_name,)))

View File

@@ -0,0 +1,13 @@
import logging
from odoo import api, fields, models, _
_logger = logging.getLogger(__name__)
class SurveySurvey(models.Model):
_inherit = 'survey.survey'
survey_record_creation_ids = fields.One2many('survey.record.creation', 'survey_id', 'Records creation', help="List of records created when survey submitted")

View File

@@ -0,0 +1,339 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from typing import TYPE_CHECKING, Any, Literal
from odoo import _, fields, models
from odoo.exceptions import UserError
from odoo.fields import Command
if TYPE_CHECKING:
from .survey_question import AnswerValuesType
from .survey_record_creation import SurveyRecordCreation
from .survey_record_creation_field_values import SurveyRecordCreationFieldValues
class SurveyUserInput(models.Model):
_inherit = "survey.user_input"
generated_record_ids = fields.One2many(
"survey.generated.record", "user_input_id", "Generated records"
)
generated_records_count = fields.Integer(
"Attempts Count", compute="_compute_generated_records_count"
)
def _compute_generated_records_count(self):
for user_input in self:
user_input.generated_records_count = len(user_input.generated_record_ids)
def action_redirect_to_generated_records(self):
self.ensure_one()
action = self.env["ir.actions.act_window"]._for_xml_id(
"survey_record_generation.survey_generated_record_action"
)
action['domain'] = [('user_input_id', '=', self.id)]
return action
def _mark_done(self):
# generate records
for user_input in self:
created_records = {}
other_record_fields_to_update: list[SurveyRecordCreationFieldValues] = []
record_creation: SurveyRecordCreation
for (
record_creation
) in user_input.survey_id.survey_record_creation_ids.sorted("sequence"):
model: str = record_creation.model_id.model
vals: dict = {}
field_value: SurveyRecordCreationFieldValues
for field_value in record_creation.field_values_ids:
value, other_record_fields_to_update = (
self.get_value_based_on_value_origin(
field_value=field_value,
user_input=user_input,
created_records=created_records,
model=model,
other_record_fields_to_update=other_record_fields_to_update,
)
)
field_name: str = field_value.field_id.name
vals[field_name] = value
existing_record = self.find_existing_record(record_creation, vals)
duplicate = self.find_duplicate_if_there_are_fields_with_unicity_check(
model, record_creation, vals
)
if duplicate:
record = duplicate
elif existing_record:
vals_with_keys_not_in_record = {
k: v
for k, v in vals.items()
if not getattr(existing_record, k, False)
}
existing_record.write(vals_with_keys_not_in_record)
record = existing_record
else:
try:
with self.env.cr.savepoint():
record = self.env[model].create(vals)
if model == "res.partner" and not self.partner_id:
self.partner_id = record.id
except Exception:
# This a broad exception because it could be IntegrityError,
# EmptyNamesError in case partner_firstname is installed etc...
if record_creation.ignore_if_mandatory_field_is_missing:
continue
raise
# Link generated records to user input
self.env["survey.generated.record"].create(
{
"survey_record_creation_name": record_creation.name,
"survey_record_creation_id": record_creation.id,
"user_input_id": user_input.id,
"created_record_id": f"{model},{record.id}",
}
)
created_records[record_creation.id] = record
# update linked record
for field_to_update in other_record_fields_to_update:
record_to_update = created_records.get(
field_to_update.survey_record_creation_id.id
)
if record_to_update:
linked_record = created_records[
field_to_update.other_created_record_id.id
]
value = self.get_value_for_relational_field(
field_to_update, linked_record
)
record_to_update.write({field_to_update.field_id.name: value})
return super()._mark_done()
def find_existing_record(
self, record_creation: "SurveyRecordCreation", vals: dict
) -> Any:
if record_creation.update_existing_records:
model = record_creation.model_id.model
search_field = record_creation.field_to_retrieve_existing_records
user_answer_value = vals.get(search_field.name)
if user_answer_value:
return self.env[model].search(
[(search_field.name, "=", user_answer_value)], limit=1
)
return None
def get_value_based_on_value_origin(
self,
field_value: "SurveyRecordCreationFieldValues",
user_input: "SurveyUserInput",
created_records: dict[Any, Any],
model: str,
other_record_fields_to_update: list["SurveyRecordCreationFieldValues"],
) -> tuple[Any, list["SurveyRecordCreationFieldValues"]]:
value: Any = None
if field_value.value_origin == "fixed":
value = field_value.get_fixed_value_for_record_creation()
elif field_value.value_origin == "question":
value = self.get_value_from_user_answer(field_value, user_input)
elif field_value.value_origin == "other_record":
# if the other_record value is a required field, get it or raise
value = self.get_required_value_from_other_record(
model, created_records, field_value
)
# otherwise, we update the record later (out of this for loop)
if not value:
other_record_fields_to_update.append(field_value)
return value, other_record_fields_to_update
@staticmethod
def get_value_for_relational_field(
field_to_update: "SurveyRecordCreationFieldValues", linked_record
) -> Any:
field_type = field_to_update.field_id.ttype
if field_type == "many2one":
return linked_record.id
else:
# many2many or one2many
return [Command.set(linked_record.ids)]
def find_duplicate_if_there_are_fields_with_unicity_check(
self, model: str, record_creation: "SurveyRecordCreation", vals: dict[Any, Any]
) -> Any:
# check duplicates
unique_fields = [
field_value.field_id.name
for field_value in record_creation.field_values_ids.filtered(
lambda r: r.unicity_check
)
]
duplicate = None
if unique_fields:
uniq_domain = []
for uniq_field in unique_fields:
uniq_domain.append((uniq_field, "=", vals[uniq_field]))
duplicate = self.env[model].search(uniq_domain, limit=1)
return duplicate
def get_required_value_from_other_record(
self,
model: str,
created_records: dict[Any, Any],
field_value: "SurveyRecordCreationFieldValues",
) -> Any:
model_class = self.env[model]
if model_class._fields[field_value.field_id.name].required:
# check if the other record is already created,
# if yes add it to vals, else raise
if (
len(created_records) > 0
and created_records[field_value.other_created_record_id.id]
):
linked_record = created_records[field_value.other_created_record_id.id]
return self.get_value_for_relational_field(field_value, linked_record)
else:
raise UserError(
_(
"The field %(field)s is mandatory for model %(model)s. "
"In Record Creation tab, drag %(record)s "
"on top of the model %(model)s."
)
% {
"field": field_value.field_id.display_name,
"model": model,
"record": field_value.other_created_record_id.name,
}
)
def get_value_from_user_answer(
self,
field_value: "SurveyRecordCreationFieldValues",
user_input: "SurveyUserInput",
) -> Any:
# find user_input_lines (which are user's answers) for the question
user_input_lines = [
user_input_line
for user_input_line in user_input.user_input_line_ids
if user_input_line.question_id == field_value.question_id
]
if not user_input_lines:
# If the question has not been displayed to the user,
# there are no user_input_lines
return None
if user_input_lines[0].skipped:
# The question has been ignored by the user
return None
question_type = field_value.question_id.question_type
if question_type in [
"char_box",
"text_box",
"numerical_box",
"date",
"datetime",
]:
return user_input_lines[0][f"value_{user_input_lines[0].answer_type}"]
elif question_type in ["simple_choice", "multiple_choice", "matrix"]:
answer_values_type = field_value.question_id.answer_values_type
return self.get_value_based_on_answer_values_type(
answer_values_type, field_value, question_type, user_input_lines
)
else:
raise UserError(
_(
"[Survey record generation] The question type %(type)s is not "
"recognized (for question %(question)s)."
)
% {"type": question_type, "question": field_value.question_id.title}
)
def get_value_based_on_answer_values_type(
self,
answer_values_type: "AnswerValuesType",
field_value: "SurveyRecordCreationFieldValues",
question_type: Literal["simple_choice", "multiple_choice", "matrix"],
user_input_lines: list[Any],
) -> Any:
if answer_values_type == "record":
answered_record_ids = []
for user_input_line in user_input_lines:
if (
user_input_line.suggested_answer_id
and user_input_line.suggested_answer_id.record_id
):
answered_record_ids.append(
user_input_line.suggested_answer_id.record_id.id
)
if not answered_record_ids:
return None
if question_type == "simple_choice":
return answered_record_ids[0]
elif question_type == "multiple_choice":
return answered_record_ids
else:
raise UserError(
_(
"[Survey record generation] The question type"
" %(type)s is not supported yet."
)
% {"type": question_type}
)
elif answer_values_type == "value":
answer_value_char = user_input_lines[0].suggested_answer_id.value_char
if field_value.field_id.ttype != "boolean":
return answer_value_char
else:
return self.get_boolean_value(
answer_value_char=answer_value_char,
question_title=field_value.question_id.title,
)
else:
raise UserError(
_(
"[Survey record generation] The answer values type '%(type)s' "
"is not supported (for question %(question)s). Use 'record' or "
"'value' instead."
)
% {
"type": answer_values_type,
"question": field_value.question_id.title,
}
)
@staticmethod
def get_boolean_value(answer_value_char: str, question_title: str) -> bool:
# Below code is a trick to be able to use "simple_choice" question
# with values 'yes' and 'no' and transform it to boolean.
if boolean_value := answer_value_char in [
"1",
"True",
"true",
"Oui",
"oui",
"Yes",
"yes",
]:
return boolean_value
else:
raise UserError(
_(
"[Survey record generation] The boolean value %s(value)s "
"is not supported (for question %(question)s)."
)
% {
"value": answer_value_char,
"question": question_title,
}
)

View File

@@ -0,0 +1,40 @@
Record generation configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. |Image of record creation list| image:: ../static/description/record-creations.png
#. Go to the the survey
#. In *Record creation* tab add a new line
#. Set a name for created record, select the Model of the record (eg: Prospect)
#. Add a field configuration. So for each field :
.. |Image of record creation fields| image:: ../static/description/record-creation-customer.png
#. You can check "unicity constraint" to prevent duplicates.
In case of duplicates and if other record use this record to fill a m2o field, the founded record will be used
#. You can configure explicitly where Odoo should retrieve the value of field :
* **fixed**: To set explicit value
* **question**: If value come from user's answer
For m2o or m2m links, question should be configured before. See Question answers configuration section below.
* **other created record**: If value come from other created record (m2o case only)
Question answers configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In a survey question : to configure value of choices
.. |Question answers| image:: ../static/description/question-answers.png
#. Configure a multiple choice question
#. Select value type associated to answer:
* **Value** > eg: to fill a selection field
* **Record** > to fill m2o or m2m field
#. In case of *record* type:
#. Select referenced model
#. You can directly fill answers (eventualy with help of the domain field)
#. Or create new answers and set associated record
#. In case of *value* type:
#. Add new selectable answers and set associated value

View File

@@ -0,0 +1,10 @@
In several cases we want to use survey application to create records.
Website form application give the same possibility but the design is integrated on the web site.
Futhermore this module add other functionnalities like "duplicate" check, or creation of several interconnected records
Typical use case : Information request form
#. Submitting the form will create a contact (partner) only if it does not already exist in the database.
#. Submitting the form will also create a crm opportunity linked to previously created partner

View File

@@ -0,0 +1,4 @@
* `Elabore <https://www.elabore.coop>`_
* Clément Thomas
* Quentin Mondot

View File

@@ -0,0 +1 @@
This module allows to generate any record from surveys answers.

View File

@@ -0,0 +1,6 @@
When a survey is properly configured, once it is submited by a user, records should be created.
To find records generated from a participation go to:
#. Survey > participation
#. Click on **# Generated records** button (top-right)

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_survey_record_creation,survey.record.creation,survey_record_generation.model_survey_record_creation,survey.group_survey_user,1,1,1,1
access_survey_record_creation_field_values,survey.record.creation.field.values,survey_record_generation.model_survey_record_creation_field_values,survey.group_survey_user,1,1,1,1
access_survey_record_creation_field_values_x2m,survey.record.creation.field.values.x2m,survey_record_generation.model_survey_record_creation_field_values_x2m,survey.group_survey_user,1,1,1,1
access_survey_generated_record,survey.generated.record,survey_record_generation.model_survey_generated_record,survey.group_survey_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_survey_record_creation survey.record.creation survey_record_generation.model_survey_record_creation survey.group_survey_user 1 1 1 1
3 access_survey_record_creation_field_values survey.record.creation.field.values survey_record_generation.model_survey_record_creation_field_values survey.group_survey_user 1 1 1 1
4 access_survey_record_creation_field_values_x2m survey.record.creation.field.values.x2m survey_record_generation.model_survey_record_creation_field_values_x2m survey.group_survey_user 1 1 1 1
5 access_survey_generated_record survey.generated.record survey_record_generation.model_survey_generated_record survey.group_survey_user 1 1 1 1

View File

@@ -0,0 +1,526 @@
<!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 record generation</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="survey-record-generation">
<h1 class="title">Survey record generation</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:03eeb5c11b7d8330051c416331c84103b2641351fdc1d0a1bad43acd09501a33
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/16.0/survey_record_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-16-0/survey-16-0-survey_record_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=16.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 any record 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="#use-cases-context" id="toc-entry-1">Use Cases / Context</a></li>
<li><a class="reference internal" href="#configuration" id="toc-entry-2">Configuration</a><ul>
<li><a class="reference internal" href="#record-generation-configuration" id="toc-entry-3">Record generation configuration</a></li>
<li><a class="reference internal" href="#question-answers-configuration" id="toc-entry-4">Question answers configuration</a></li>
</ul>
</li>
<li><a class="reference internal" href="#usage" id="toc-entry-5">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-6">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-7">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-8">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-9">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-10">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="use-cases-context">
<h1><a class="toc-backref" href="#toc-entry-1">Use Cases / Context</a></h1>
<p>In several cases we want to use survey application to create records.
Website form application give the same possibility but the design is integrated on the web site.
Futhermore this module add other functionnalities like “duplicate” check, or creation of several interconnected records</p>
<p>Typical use case : Information request form</p>
<ol class="arabic simple">
<li>Submitting the form will create a contact (partner) only if it does not already exist in the database.</li>
<li>Submitting the form will also create a crm opportunity linked to previously created partner</li>
</ol>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-2">Configuration</a></h1>
<div class="section" id="record-generation-configuration">
<h2><a class="toc-backref" href="#toc-entry-3">Record generation configuration</a></h2>
<ol class="arabic">
<li><p class="first">Go to the the survey</p>
</li>
<li><p class="first">In <em>Record creation</em> tab add a new line</p>
</li>
<li><p class="first">Set a name for created record, select the Model of the record (eg: Prospect)</p>
</li>
<li><p class="first">Add a field configuration. So for each field :</p>
<blockquote>
<ol class="arabic simple">
<li><dl class="first docutils">
<dt>You can check “unicity constraint” to prevent duplicates.</dt>
<dd>In case of duplicates and if other record use this record to fill a m2o field, the founded record will be used</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>You can configure explicitly where Odoo should retrieve the value of field :</dt>
<dd><ul class="first last">
<li><strong>fixed</strong>: To set explicit value</li>
<li><dl class="first docutils">
<dt><strong>question</strong>: If value come from users answer</dt>
<dd>For m2o or m2m links, question should be configured before. See Question answers configuration section below.</dd>
</dl>
</li>
<li><strong>other created record</strong>: If value come from other created record (m2o case only)</li>
</ul>
</dd>
</dl>
</li>
</ol>
</blockquote>
</li>
</ol>
</div>
<div class="section" id="question-answers-configuration">
<h2><a class="toc-backref" href="#toc-entry-4">Question answers configuration</a></h2>
<p>In a survey question : to configure value of choices</p>
<ol class="arabic simple">
<li>Configure a multiple choice question</li>
<li><dl class="first docutils">
<dt>Select value type associated to answer:</dt>
<dd><ul class="first last">
<li><strong>Value</strong> &gt; eg: to fill a selection field</li>
<li><strong>Record</strong> &gt; to fill m2o or m2m field</li>
</ul>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>In case of <em>record</em> type:</dt>
<dd><ol class="first last arabic">
<li>Select referenced model</li>
<li>You can directly fill answers (eventualy with help of the domain field)</li>
<li>Or create new answers and set associated record</li>
</ol>
</dd>
</dl>
</li>
<li><dl class="first docutils">
<dt>In case of <em>value</em> type:</dt>
<dd><ol class="first last arabic">
<li>Add new selectable answers and set associated value</li>
</ol>
</dd>
</dl>
</li>
</ol>
</div>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-5">Usage</a></h1>
<p>When a survey is properly configured, once it is submited by a user, records should be created.</p>
<p>To find records generated from a participation go to:</p>
<ol class="arabic simple">
<li>Survey &gt; participation</li>
<li>Click on <strong># Generated records</strong> button (top-right)</li>
</ol>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-6">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_record_generation%0Aversion:%2016.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-7">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-8">Authors</a></h2>
<ul class="simple">
<li>Elabore</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-9">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.elabore.coop">Elabore</a><ul>
<li>Clément Thomas</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-10">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>This module is part of the <a class="reference external" href="https://github.com/OCA/survey/tree/16.0/survey_record_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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

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

View File

@@ -0,0 +1,872 @@
from psycopg2 import IntegrityError
from odoo.addons.survey.tests.common import SurveyCase
from odoo.tests.common import mute_logger
class TestSurveyRecordCreation(SurveyCase):
def setUp(self):
super().setUp()
# We create a survey
self.survey = self.env["survey.survey"].create(
{
"title": "Test Survey",
}
)
# With a single question "name"
self.question_name = self._add_question(
page=None,
name="Name",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.res_partner_model = self.env["ir.model"]._get("res.partner")
# The administrator has set up a record creation on res.partner model
self.survey_record_creation = self.env["survey.record.creation"].create(
{
"name": "Contact",
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
}
)
self.name_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "name")]
)
# And linked the res_partner field "name" to the answer of question_name
self.name_survey_record_creation_field_values = self.env[
"survey.record.creation.field.values"
].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": self.name_field.id,
"value_origin": "question",
"question_id": self.question_name.id,
}
)
def test_record_is_created(self):
# Easy test to become familiar with the subject (check comments of setUp)
# We simulate that jean@test.fr has answered the survey
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
# He answered "Jean" to the question_name
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
# And validate the survey
self.answer._mark_done()
# Thus, the res.partner with the name "Jean" has been created
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
def test_all_types_of_question(self):
# Still todo : "datetime" and "text_box"
# Also todo : "simple_choice" with answer_values_type "no"
# Also todo : "multiple_choice" with answer_values_type "no" and "value"
# Note : matrix question type is never proposed in the allowed_question_ids
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
### "char_box" type of question, tested with field CHAR "name" ###
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
### "numerical_box" type of question,
# tested with FLOAT field "partner_latitude" ###
self.question_partner_latitude = self._add_question(
page=None,
name="Partner latitude",
qtype="numerical_box",
survey_id=self.survey.id,
sequence=1,
)
partner_latitude_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "partner_latitude")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": partner_latitude_field.id,
"value_origin": "question",
"question_id": self.question_partner_latitude.id,
}
)
self._add_answer_line(
question=self.question_partner_latitude,
answer=self.answer,
answer_value=44.73333,
)
# NOTE: "date" question type not tested here — res.partner has no writable Date field in v18.
### "simple_choice" type of question, tested with SELECTION field "type" ###
### Here we also test answer_values_type "value"
self.question_type = self._add_question(
page=None,
name="Type",
qtype="simple_choice",
labels=[
{"value": "contact"},
{"value": "other"},
],
survey_id=self.survey.id,
sequence=1,
answer_values_type="value",
)
self.question_type.suggested_answer_ids[0].value_char = (
self.question_type.suggested_answer_ids[0].value
)
self.question_type.suggested_answer_ids[1].value_char = (
self.question_type.suggested_answer_ids[1].value
)
type_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "type")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": type_field.id,
"value_origin": "question",
"question_id": self.question_type.id,
}
)
self._add_answer_line(
question=self.question_type,
answer=self.answer,
answer_value=self.question_type.suggested_answer_ids[0].id,
)
### "simple_choice" type of question, tested with MANY2ONE field "title" ###
### Here we also test answer_values_type "record"
mister_title = self.env["res.partner.title"].create({"name": "Mister"})
madam_title = self.env["res.partner.title"].create({"name": "Madam"})
self.question_title = self._add_question(
page=None,
name="Title",
qtype="simple_choice",
labels=[
{"value": mister_title.display_name},
{"value": madam_title.display_name},
],
survey_id=self.survey.id,
sequence=1,
answer_values_type="record",
)
self.question_title.suggested_answer_ids[0].record_id = (
f"res.partner.title,{mister_title.id}"
)
self.question_title.suggested_answer_ids[1].record_id = (
f"res.partner.title,{madam_title.id}"
)
title_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "title")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": title_field.id,
"value_origin": "question",
"question_id": self.question_title.id,
}
)
self._add_answer_line(
question=self.question_title,
answer=self.answer,
answer_value=self.question_title.suggested_answer_ids[0].id,
)
### "multiple_choice" type of question,
# tested with MANY2MANY field "category" ###
adult_category = self.env["res.partner.category"].create({"name": "Adult"})
teenager_category = self.env["res.partner.category"].create(
{"name": "Teenager"}
)
child_category = self.env["res.partner.category"].create({"name": "Child"})
self.question_category = self._add_question(
page=None,
name="Category",
qtype="multiple_choice",
labels=[
{"value": adult_category.display_name},
{"value": teenager_category.display_name},
{"value": child_category.display_name},
],
survey_id=self.survey.id,
sequence=1,
answer_values_type="record",
)
self.question_category.suggested_answer_ids[0].record_id = (
f"res.partner.category,{adult_category.id}"
)
self.question_category.suggested_answer_ids[1].record_id = (
f"res.partner.category,{teenager_category.id}"
)
self.question_category.suggested_answer_ids[2].record_id = (
f"res.partner.category,{child_category.id}"
)
category_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "category_id")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": category_field.id,
"value_origin": "question",
"question_id": self.question_category.id,
}
)
self._add_answer_line(
question=self.question_category,
answer=self.answer,
answer_value=self.question_category.suggested_answer_ids[0].id,
)
self._add_answer_line(
question=self.question_category,
answer=self.answer,
answer_value=self.question_category.suggested_answer_ids[1].id,
)
self.answer._mark_done()
partner = self.env["res.partner"].search(
[
("name", "=", "Jean"),
("partner_latitude", "=", 44.73333),
("type", "=", "contact"),
("title", "=", mister_title.id),
("category_id", "=", adult_category.id),
("category_id", "=", teenager_category.id),
("category_id", "!=", child_category.id),
]
)
self.assertTrue(partner.name == "Jean")
def test_records_are_created(self):
# we test that several records can be created at the end of the same survey
# concurrently, we test the value_origin "other_record" and "fixed"
res_partner_bank_model = self.env["ir.model"]._get("res.partner.bank")
self.bank_survey_record_creation = self.env["survey.record.creation"].create(
{
"name": "Bank",
"survey_id": self.survey.id,
"model_id": res_partner_bank_model.id,
}
)
# Below we test "value_origin": "other_record" with a required field
partner_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner.bank"), ("name", "=", "partner_id")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.bank_survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": res_partner_bank_model.id,
"field_id": partner_field.id,
"value_origin": "other_record",
"other_created_record_id": self.survey_record_creation.id,
}
)
# Below we test "value_origin": "fixed"
acc_number_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner.bank"), ("name", "=", "acc_number")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.bank_survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": res_partner_bank_model.id,
"field_id": acc_number_field.id,
"value_origin": "fixed",
"fixed_value_char": "FR76 1444 5004 0004 0000 0000 000",
}
)
# Below we test "value_origin": "other_record" with a NOT required field
bank_ids_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "bank_ids")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": bank_ids_field.id,
"value_origin": "other_record",
"other_created_record_id": self.bank_survey_record_creation.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
bank_account = self.env["res.partner.bank"].search(
[("partner_id", "=", partner.id)]
)
self.assertTrue(bank_account.acc_number == "FR76 1444 5004 0004 0000 0000 000")
def test_records_of_same_model_are_created(self):
# When we have 2 survey.record.creation on res.partner,
# we check that 2 contacts are created
self.second_question_name = self._add_question(
page=None,
name="Name of second person",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.second_contact_creation = self.env["survey.record.creation"].create(
{
"name": "Contact 2",
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
}
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.second_contact_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": self.name_field.id,
"value_origin": "question",
"question_id": self.second_question_name.id,
}
)
self.first_answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name,
answer=self.first_answer,
answer_value="Jean",
)
self._add_answer_line(
question=self.second_question_name,
answer=self.first_answer,
answer_value="Jeanne",
)
self.first_answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
partner = self.env["res.partner"].search([("name", "=", "Jeanne")])
self.assertTrue(partner.name == "Jeanne")
def test_survey_submitted_twice_by_same_user(self):
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self.answer._mark_done()
partners = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(len(partners) == 2)
def test_unicity_check(self):
# In this test, we check the behavior of unicity_check
self.name_survey_record_creation_field_values.unicity_check = True
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
self.second_answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.second_answer, answer_value="Jean"
)
self.second_answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
self.assertTrue(len(partner) == 1)
def test_some_questions_are_not_answered(self):
self.question_email = self._add_question(
page=None,
name="Email",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
mister_title = self.env["res.partner.title"].create({"name": "Mister"})
madam_title = self.env["res.partner.title"].create({"name": "Madam"})
self.question_title = self._add_question(
page=None,
name="Title",
qtype="simple_choice",
labels=[
{"value": mister_title.display_name},
{"value": madam_title.display_name},
],
survey_id=self.survey.id,
sequence=1,
answer_values_type="record",
)
self.question_title.suggested_answer_ids[0].record_id = (
f"res.partner.title,{mister_title.id}"
)
self.question_title.suggested_answer_ids[1].record_id = (
f"res.partner.title,{madam_title.id}"
)
self.question_street = self._add_question(
page=None,
name="Street",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
email_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "email")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": email_field.id,
"value_origin": "question",
"question_id": self.question_email.id,
}
)
title_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "title")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": title_field.id,
"value_origin": "question",
"question_id": self.question_title.id,
}
)
street_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "street")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": street_field.id,
"value_origin": "question",
"question_id": self.question_street.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self._add_answer_line(
question=self.question_email,
answer=self.answer,
answer_value=False,
skipped=True,
answer_type=False,
)
self._add_answer_line(
question=self.question_title,
answer=self.answer,
answer_value=False,
skipped=True,
answer_type=False,
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.name == "Jean")
self.assertFalse(partner.email)
self.assertFalse(partner.title)
self.assertFalse(partner.street)
def test_boolean_field(self):
self.question_employee = self._add_question(
page=None,
name="Employee",
qtype="simple_choice",
labels=[
{"value": "yes"},
{"value": "no"},
],
survey_id=self.survey.id,
sequence=1,
answer_values_type="value",
)
self.question_employee.suggested_answer_ids[0].value_char = (
self.question_employee.suggested_answer_ids[0].value
)
self.question_employee.suggested_answer_ids[1].value_char = (
self.question_employee.suggested_answer_ids[1].value
)
employee_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "employee")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": employee_field.id,
"value_origin": "question",
"question_id": self.question_employee.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self._add_answer_line(
question=self.question_employee,
answer=self.answer,
answer_value=self.question_employee.suggested_answer_ids[0].id,
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(partner.employee)
def test_update_existing_record(self):
# A contact with name 'Jean' already exists.
# We'll update the email of this partner (and not create a new one)
self.env["res.partner"].create({"name": "Jean"})
self.question_email = self._add_question(
page=None,
name="Email",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.survey_record_creation.write(
{
"update_existing_records": True,
"field_to_retrieve_existing_records": self.name_field.id,
}
)
email_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "email")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": email_field.id,
"value_origin": "question",
"question_id": self.question_email.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self._add_answer_line(
question=self.question_email,
answer=self.answer,
answer_value="jean@test.fr",
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(len(partner) == 1)
self.assertTrue(partner.email == "jean@test.fr")
def test_update_only_empty_fields_when_updating_records(self):
# A contact with name 'Jean' and email 'jean@test.fr' already exists.
# We'll update the field 'function' of this partner and won't update the email
# because it's already filled up
self.env["res.partner"].create(
{
"name": "Jean",
"email": "jean@test.fr",
# when the survey is submitted, email should not be updated
}
)
self.question_email = self._add_question(
page=None,
name="Email",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.question_function = self._add_question(
page=None,
name="Function",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.survey_record_creation.write(
{
"update_existing_records": True,
"field_to_retrieve_existing_records": self.name_field.id,
}
)
email_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "email")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": email_field.id,
"value_origin": "question",
"question_id": self.question_email.id,
}
)
function_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "function")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": function_field.id,
"value_origin": "question",
"question_id": self.question_function.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self._add_answer_line(
question=self.question_email,
answer=self.answer,
answer_value="ThisEmailShouldNotBeUpdated@test.fr",
)
self._add_answer_line(
question=self.question_function,
answer=self.answer,
answer_value="happiness office manager",
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(len(partner) == 1)
self.assertTrue(partner.email == "jean@test.fr")
self.assertTrue(partner.function == "happiness office manager")
def test_unicity_check_has_priority_over_update(self):
# In this test, we verify that if a field is set up with unicity_check
# it has priority over updating the existing record
self.name_survey_record_creation_field_values.unicity_check = True
self.env["res.partner"].create({"name": "Jean"})
self.question_email = self._add_question(
page=None,
name="Email",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.survey_record_creation.write(
{
"update_existing_records": True,
"field_to_retrieve_existing_records": self.name_field.id,
}
)
email_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "email")]
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.survey_record_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": email_field.id,
"value_origin": "question",
"question_id": self.question_email.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self._add_answer_line(
question=self.question_email,
answer=self.answer,
answer_value="jean@test.fr",
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertTrue(len(partner) == 1)
self.assertTrue(getattr(partner, "Email", None) is None)
def test_required_fields_are_not_filled_up(self):
# In this test, we check the behavior when a required field (name) is missing
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
with self.assertRaises(IntegrityError):
# TODO : propose a better user experience than IntegrityError when
# a mandatory field is missing
with mute_logger("odoo.sql_db"):
self.answer._mark_done()
def test_ignore_if_mandatory_field_is_missing(self):
# In this test, we check the behavior of ignore_if_mandatory_field_is_missing
self.survey_record_creation.write(
{"ignore_if_mandatory_field_is_missing": True}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
with mute_logger("odoo.sql_db"):
self.answer._mark_done()
# No partner has been created, and no IntegrityError has been raised
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertEqual(len(partner), 0)
def test_fill_up_partner_id_in_survey_input(self):
# In this test, we check that the field partner_id is filled up
# when we create a res.partner
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name, answer=self.answer, answer_value="Jean"
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertEqual(self.answer.partner_id, partner)
def test_partner_id_in_survey_input_is_filled_up_by_first_contact_record_creation(self):
# In this test, we verify that when creating several contacts with the same survey,
# the 1st created contact is used to fill up survey_input.partner_id
self.second_question_name = self._add_question(
page=None,
name="Name of second person",
qtype="char_box",
survey_id=self.survey.id,
sequence=1,
)
self.second_contact_creation = self.env["survey.record.creation"].create(
{
"name": "Contact 2",
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
}
)
self.env["survey.record.creation.field.values"].create(
{
"survey_record_creation_id": self.second_contact_creation.id,
"survey_id": self.survey.id,
"model_id": self.res_partner_model.id,
"field_id": self.name_field.id,
"value_origin": "question",
"question_id": self.second_question_name.id,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_name,
answer=self.answer,
answer_value="Jean",
)
self._add_answer_line(
question=self.second_question_name,
answer=self.answer,
answer_value="Jeanne",
)
self.answer._mark_done()
partner = self.env["res.partner"].search([("name", "=", "Jean")])
self.assertEqual(self.answer.partner_id, partner)

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="survey_generated_record_view_tree" model="ir.ui.view">
<field name="name">survey.generated.record.view.tree</field>
<field name="model">survey.generated.record</field>
<field name="arch" type="xml">
<list>
<field name="created_record_id" string="Link to generated record" widget="reference"/>
<field name="survey_record_creation_name" string="Generated record type"/>
<field name="user_input_id" string="Generated from survey input"/>
</list>
</field>
</record>
<record id="survey_generated_record_view_search" model="ir.ui.view">
<field name="name">survey.generated.record.view.search</field>
<field name="model">survey.generated.record</field>
<field name="arch" type="xml">
<search>
<field name="survey_record_creation_name" />
<field name="survey_record_creation_id" />
<field name="user_input_id" />
<field name="created_record_id"/>
</search>
</field>
</record>
<record id="survey_generated_record_action" model="ir.actions.act_window">
<field name="name">Generated records</field>
<field name="res_model">survey.generated.record</field>
<field name="view_mode">list</field>
<field name="search_view_id" ref="survey_generated_record_view_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
No generated records found
</p>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="survey_question_form" model="ir.ui.view">
<field name="name">survey.question.view.form.inherit.record.generation</field>
<field name="model">survey.question</field>
<field name="inherit_id" ref="survey.survey_question_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='suggested_answer_ids']" position="before">
<field name="answer_values_type" />
<field name="model_id" invisible="answer_values_type != 'record'" />
<field name="model_name" invisible="1" />
<field name="fill_domain" widget="domain" options="{'model': 'model_name'}" invisible="answer_values_type != 'record' or not model_id" />
<button name="fill" string="Empty and fill"
type="object"
colspan="2"
help="Empty the list and fill it with all items of selected model matching domain"
invisible="answer_values_type != 'record'" />
</xpath>
<xpath expr="//field[@name='suggested_answer_ids']" position="attributes">
<attribute
name="context"
>{'default_question_id': id, 'default_model_id': model_id}</attribute>
</xpath>
<xpath expr="//field[@name='suggested_answer_ids']/list" position="inside">
<field name="answer_values_type" invisible="1" />
<field name="model_id" invisible="1" />
<field name="record_id"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
invisible="answer_values_type != 'record'"/>
<field name="value_char"
invisible="answer_values_type != 'value'" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="survey_survey_view_form" model="ir.ui.view">
<field name="name">survey.survey.view.form.inherit.record.generation</field>
<field name="model">survey.survey</field>
<field name="inherit_id" ref="survey.survey_survey_view_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page name="record_creation" string="Record creation">
<field name="survey_record_creation_ids">
<list>
<field name="sequence" widget="handle"/>
<field name="name" />
<field name="model_id" />
</list>
<form>
<group>
<field name="name" />
<field name="model_id" />
<field name="ignore_if_mandatory_field_is_missing" />
<field name="update_existing_records" />
<field name="allowed_field_ids" invisible="1"/>
<field name="field_to_retrieve_existing_records" invisible="not update_existing_records"/>
<div colspan="2" style="width:100%;">
<div class="alert alert-warning"
invisible="not update_existing_records">
Only the first matched record will be updated.
Also to be noticed, the unicity check feature has priority over updating the existing record.
</div>
</div>
<field name="field_values_ids">
<list>
<field name="field_id" />
<field name="displayed_value" />
<field name="unicity_check" />
</list>
<form>
<group>
<field name="model_id" invisible="1" />
<field name="field_id" />
<field name="unicity_check" />
<field name="field_relation" invisible="1" />
<field name="field_type" invisible="1" />
<div colspan="2">
<field name="field_help" />
</div>
<field name="survey_id" invisible="1" />
<field name="value_origin" />
<field name="allowed_question_ids" invisible="1" />
</group>
<div invisible="value_origin != 'fixed' or not field_id">
<group>
<field name="displayed_value" invisible="1" />
<field
name="fixed_value_char"
invisible="field_type != 'char'"
/>
<field
name="fixed_value_selection"
invisible="field_type != 'selection'"
/>
<field
name="fixed_value_text"
invisible="field_type != 'text'"
/>
<field
name="fixed_value_html"
invisible="field_type != 'html'"
/>
<field
name="fixed_value_integer"
invisible="field_type != 'integer'"
/>
<field
name="fixed_value_float"
invisible="field_type != 'float'"
/>
<field
name="fixed_value_date"
invisible="field_type != 'date'"
/>
<field
name="fixed_value_datetime"
invisible="field_type != 'datetime'"
/>
<field
name="fixed_value_boolean"
invisible="field_type != 'boolean'"
/>
<field
name="fixed_value_many2one"
invisible="field_type != 'many2one'"
readonly="False"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
/>
<field name="fixed_value_many2many"
invisible="field_type not in ['one2many', 'many2many']">
<list editable="bottom">
<field name="value_reference"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
/>
</list>
</field>
</group>
</div>
<div invisible="value_origin != 'question' or not field_id">
<group>
<field name="question_id" />
</group>
</div>
<div invisible="value_origin != 'other_record' or not field_id or field_type != 'many2one'">
<group>
<field name="other_created_record_id" />
</group>
</div>
</form>
</field>
<div colspan="2">
<field name="warning_message" />
</div>
</group>
</form>
</field>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- USER INPUTS -->
<record id="survey_user_input_view_form_survey_record_generation" model="ir.ui.view">
<field name="name">survey.user_input.view.form.survey.record.generation</field>
<field name="model">survey.user_input</field>
<field name="inherit_id" ref="survey.survey_user_input_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_redirect_to_generated_records"
type="object"
class="oe_stat_button"
icon="fa-files-o">
<field string="Generated records" name="generated_records_count" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
</data>
</odoo>