1 Commits

Author SHA1 Message Date
853ed418bb wip 2025-08-21 12:31:41 +02:00
19 changed files with 156 additions and 1992 deletions

View File

@@ -1,20 +0,0 @@
# Configuration for known file extensions
[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.{json,yml,yaml,rst,md}]
indent_size = 2
# Do not configure editor for libs and autogenerated content
[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}]
charset = unset
end_of_line = unset
indent_size = unset
indent_style = unset
insert_final_newline = false
trim_trailing_whitespace = false

View File

@@ -1,188 +0,0 @@
env:
browser: true
es6: true
# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449
parserOptions:
ecmaVersion: 2019
overrides:
- files:
- "**/*.esm.js"
parserOptions:
sourceType: module
# Globals available in Odoo that shouldn't produce errorings
globals:
_: readonly
$: readonly
fuzzy: readonly
jQuery: readonly
moment: readonly
odoo: readonly
openerp: readonly
owl: readonly
luxon: readonly
# Styling is handled by Prettier, so we only need to enable AST rules;
# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890
rules:
accessor-pairs: warn
array-callback-return: warn
callback-return: warn
capitalized-comments:
- warn
- always
- ignoreConsecutiveComments: true
ignoreInlineComments: true
complexity:
- warn
- 15
constructor-super: warn
dot-notation: warn
eqeqeq: warn
global-require: warn
handle-callback-err: warn
id-blacklist: warn
id-match: warn
init-declarations: error
max-depth: warn
max-nested-callbacks: warn
max-statements-per-line: warn
no-alert: warn
no-array-constructor: warn
no-caller: warn
no-case-declarations: warn
no-class-assign: warn
no-cond-assign: error
no-const-assign: error
no-constant-condition: warn
no-control-regex: warn
no-debugger: error
no-delete-var: warn
no-div-regex: warn
no-dupe-args: error
no-dupe-class-members: error
no-dupe-keys: error
no-duplicate-case: error
no-duplicate-imports: error
no-else-return: warn
no-empty-character-class: warn
no-empty-function: error
no-empty-pattern: error
no-empty: warn
no-eq-null: error
no-eval: error
no-ex-assign: error
no-extend-native: warn
no-extra-bind: warn
no-extra-boolean-cast: warn
no-extra-label: warn
no-fallthrough: warn
no-func-assign: error
no-global-assign: error
no-implicit-coercion:
- warn
- allow: ["~"]
no-implicit-globals: warn
no-implied-eval: warn
no-inline-comments: warn
no-inner-declarations: warn
no-invalid-regexp: warn
no-irregular-whitespace: warn
no-iterator: warn
no-label-var: warn
no-labels: warn
no-lone-blocks: warn
no-lonely-if: error
no-mixed-requires: error
no-multi-str: warn
no-native-reassign: error
no-negated-condition: warn
no-negated-in-lhs: error
no-new-func: warn
no-new-object: warn
no-new-require: warn
no-new-symbol: warn
no-new-wrappers: warn
no-new: warn
no-obj-calls: warn
no-octal-escape: warn
no-octal: warn
no-param-reassign: warn
no-path-concat: warn
no-process-env: warn
no-process-exit: warn
no-proto: warn
no-prototype-builtins: warn
no-redeclare: warn
no-regex-spaces: warn
no-restricted-globals: warn
no-restricted-imports: warn
no-restricted-modules: warn
no-restricted-syntax: warn
no-return-assign: error
no-script-url: warn
no-self-assign: warn
no-self-compare: warn
no-sequences: warn
no-shadow-restricted-names: warn
no-shadow: warn
no-sparse-arrays: warn
no-sync: warn
no-this-before-super: warn
no-throw-literal: warn
no-undef-init: warn
no-undef: error
no-unmodified-loop-condition: warn
no-unneeded-ternary: error
no-unreachable: error
no-unsafe-finally: error
no-unused-expressions: error
no-unused-labels: error
no-unused-vars: error
no-use-before-define: error
no-useless-call: warn
no-useless-computed-key: warn
no-useless-concat: warn
no-useless-constructor: warn
no-useless-escape: warn
no-useless-rename: warn
no-void: warn
no-with: warn
operator-assignment: [error, always]
prefer-const: warn
radix: warn
require-yield: warn
sort-imports: warn
spaced-comment: [error, always]
strict: [error, function]
use-isnan: error
valid-jsdoc:
- warn
- prefer:
arg: param
argument: param
augments: extends
constructor: class
exception: throws
func: function
method: function
prop: property
return: returns
virtual: abstract
yield: yields
preferType:
array: Array
bool: Boolean
boolean: Boolean
number: Number
object: Object
str: String
string: String
requireParamDescription: false
requireReturn: false
requireReturnDescription: false
requireReturnType: false
valid-typeof: warn
yoda: warn

View File

@@ -1,42 +0,0 @@
name: pre-commit
on:
pull_request:
branches:
- "16.0*"
jobs:
pre-commit:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Get python version
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
# - uses: actions/cache@v4
# with:
# path: ~/.cache/pre-commit
# key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install pre-commit
run: pip install pre-commit
- name: Run pre-commit
run: pre-commit run --all-files --show-diff-on-failure --color=always
env:
# Consider valid a PR that changes README fragments but doesn't
# change the README.rst file itself. It's not really a problem
# because the bot will update it anyway after merge. This way, we
# lower the barrier for functional contributors that want to fix the
# readme fragments, while still letting developers get README
# auto-generated (which also helps functionals when using runboat).
# DOCS https://pre-commit.com/#temporarily-disabling-hooks
SKIP: oca-gen-addon-readme
- name: Check that all files generated by pre-commit are in git
run: |
newfiles="$(git ls-files --others --exclude-from=.gitignore)"
if [ "$newfiles" != "" ] ; then
echo "Please check-in the following files:"
echo "$newfiles"
exit 1
fi

View File

@@ -1,150 +0,0 @@
exclude: |
(?x)
# NOT INSTALLABLE ADDONS
# END NOT INSTALLABLE ADDONS
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
# We don't want to mess with tool-generated files
.svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs|
# Maybe reactivate this when all README files include prettier ignore tags?
^README\.md$|
# Library files can have extraneous formatting (even minimized)
/static/(src/)?lib/|
# Repos using Sphinx to generate docs don't need prettying
^docs/_templates/.*\.html$|
# Don't bother non-technical authors with formatting issues in docs
readme/.*\.(rst|md)$|
# Ignore build and dist directories in addons
/build/|/dist/|
# Ignore test files in addons
/tests/samples/.*|
# You don't usually want a bot to modify your legal texts
(LICENSE.*|COPYING.*)
default_language_version:
python: python3
node: "16.17.0"
repos:
- repo: local
hooks:
# These files are most likely copier diff rejection junks; if found,
# review them manually, fix the problem (if needed) and remove them
- id: forbidden-files
name: forbidden files
entry: found forbidden files; remove them
language: fail
files: "\\.rej$"
- id: en-po-files
name: en.po files cannot exist
entry: found a en.po file
language: fail
files: '[a-zA-Z0-9_]*/i18n/en\.po$'
- repo: https://github.com/oca/maintainer-tools
rev: f9b919b9868143135a9c9cb03021089cabba8223
hooks:
# update the NOT INSTALLABLE ADDONS section above
- id: oca-update-pre-commit-excluded-addons
- id: oca-fix-manifest-website
entry:
bash -c 'oca-fix-manifest-website "https://git.elabore.coop/elabore/$(basename
$(git rev-parse --show-toplevel))"'
- id: oca-gen-addon-readme
entry:
bash -c 'oca-gen-addon-readme
--addons-dir=.
--branch=$(git symbolic-ref
refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@")
--repo-name=$(basename $(git rev-parse --show-toplevel))
--org-name="Elabore"
--if-source-changed --keep-source-digest'
- repo: https://github.com/OCA/odoo-pre-commit-hooks
rev: v0.1.4
hooks:
- id: oca-checks-odoo-module
- id: oca-checks-po
args:
- --disable=po-pretty-format
- repo: local
hooks:
- id: prettier
name: prettier (with plugin-xml)
entry: prettier
args:
- --write
- --list-different
- --ignore-unknown
types: [text]
files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$
language: node
additional_dependencies:
- "prettier@2.7.1"
- "@prettier/plugin-xml@2.2.0"
- repo: local
hooks:
- id: eslint
name: eslint
entry: eslint
args:
- --color
- --fix
verbose: true
types: [javascript]
language: node
additional_dependencies:
- "eslint@8.24.0"
- "eslint-plugin-jsdoc@"
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- id: end-of-file-fixer
# exclude autogenerated files
exclude: /README\.rst$|\.pot?$
- id: debug-statements
- id: fix-encoding-pragma
args: ["--remove"]
- id: check-case-conflict
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-merge-conflict
# exclude files where underlines are not distinguishable from merge conflicts
exclude: /README\.rst$|^docs/.*\.rst$
- id: check-symlinks
- id: check-xml
- id: mixed-line-ending
args: ["--fix=lf"]
- repo: https://github.com/PyCQA/docformatter
rev: v1.7.7
hooks:
- id: docformatter
args: [
"--in-place", # modify the files
"--recursive", # run on all the files
"--wrap-summaries",
"88", # max length of 1st line
"--wrap-descriptions",
"88", # max length of other lines
"--pre-summary-newline", # new line before a long summary
"--make-summary-multi-line", # force summary on multilines
]
additional_dependencies: ["tomli"] # if Python <3.11
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/OCA/pylint-odoo
rev: v9.1.3
hooks:
- id: pylint_odoo
name: pylint with optional checks
args:
- --rcfile=.pylintrc
- --exit-zero
verbose: true
- id: pylint_odoo
args:
- --rcfile=.pylintrc-mandatory

View File

@@ -1,8 +0,0 @@
# Defaults for all prettier-supported languages.
# Prettier will complete this with settings from .editorconfig file.
bracketSpacing: false
printWidth: 88
proseWrap: always
semi: true
trailingComma: "es5"
xmlWhitespaceSensitivity: "strict"

123
.pylintrc
View File

@@ -1,123 +0,0 @@
[MASTER]
load-plugins=pylint_odoo
score=n
[ODOOLINT]
readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
manifest-required-authors=Elabore
manifest-required-keys=license
manifest-deprecated-keys=description,active
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
valid-odoo-versions=16.0
[MESSAGES CONTROL]
disable=all
# This .pylintrc contains optional AND mandatory checks and is meant to be
# loaded in an IDE to have it check everything, in the hope this will make
# optional checks more visible to contributors who otherwise never look at a
# green travis to see optional checks that failed.
# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
# config as a blocking check.
enable=anomalous-backslash-in-string,
api-one-deprecated,
api-one-multi-together,
assignment-from-none,
attribute-deprecated,
class-camelcase,
dangerous-default-value,
dangerous-view-replace-wo-priority,
development-status-allowed,
duplicate-id-csv,
duplicate-key,
duplicate-xml-fields,
duplicate-xml-record-id,
eval-referenced,
eval-used,
incoherent-interpreter-exec-perm,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
manifest-required-author,
manifest-required-key,
manifest-version-format,
method-compute,
method-inverse,
method-required-super,
method-search,
openerp-exception-warning,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
redundant-modulename-xml,
reimported,
relative-import,
return-in-init,
rst-syntax-error,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
wrong-tabs-instead-of-spaces,
xml-syntax-error,
attribute-string-redundant,
character-not-valid-in-resource-link,
consider-merging-classes-inherited,
context-overridden,
create-user-wo-reset-password,
dangerous-filter-wo-user,
dangerous-qweb-replace-wo-priority,
deprecated-data-xml-node,
deprecated-openerp-xml-node,
duplicate-po-message-definition,
except-pass,
file-not-used,
invalid-commit,
manifest-maintainers-list,
missing-newline-extrafiles,
missing-readme,
missing-return,
odoo-addons-relative-import,
old-api7-method-defined,
po-msgstr-variables,
po-syntax-error,
renamed-field-parameter,
resource-not-exist,
str-format-used,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
unnecessary-utf8-coding-comment,
website-manifest-key-not-valid-uri,
xml-attribute-translatable,
xml-deprecated-qweb-directive,
xml-deprecated-tree-attribute,
external-request-timeout,
# messages that do not cause the lint step to fail
consider-merging-classes-inherited,
create-user-wo-reset-password,
dangerous-filter-wo-user,
deprecated-module,
file-not-used,
invalid-commit,
missing-manifest-dependency,
missing-newline-extrafiles,
missing-readme,
no-utf8-coding-comment,
odoo-addons-relative-import,
old-api7-method-defined,
redefined-builtin,
too-complex,
unnecessary-utf8-coding-comment
[REPORTS]
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=colorized
reports=no

View File

@@ -1,98 +0,0 @@
[MASTER]
load-plugins=pylint_odoo
score=n
[ODOOLINT]
readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst"
manifest-required-authors=Elabore
manifest-required-keys=license
manifest-deprecated-keys=description,active
license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3
valid-odoo-versions=16.0
[MESSAGES CONTROL]
disable=all
enable=anomalous-backslash-in-string,
api-one-deprecated,
api-one-multi-together,
assignment-from-none,
attribute-deprecated,
class-camelcase,
dangerous-default-value,
dangerous-view-replace-wo-priority,
development-status-allowed,
duplicate-id-csv,
duplicate-key,
duplicate-xml-fields,
duplicate-xml-record-id,
eval-referenced,
eval-used,
incoherent-interpreter-exec-perm,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
manifest-required-author,
manifest-required-key,
manifest-version-format,
method-compute,
method-inverse,
method-required-super,
method-search,
openerp-exception-warning,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
redundant-modulename-xml,
reimported,
relative-import,
return-in-init,
rst-syntax-error,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
wrong-tabs-instead-of-spaces,
xml-syntax-error,
attribute-string-redundant,
character-not-valid-in-resource-link,
consider-merging-classes-inherited,
context-overridden,
create-user-wo-reset-password,
dangerous-filter-wo-user,
dangerous-qweb-replace-wo-priority,
deprecated-data-xml-node,
deprecated-openerp-xml-node,
duplicate-po-message-definition,
except-pass,
file-not-used,
invalid-commit,
manifest-maintainers-list,
missing-newline-extrafiles,
missing-readme,
missing-return,
odoo-addons-relative-import,
old-api7-method-defined,
po-msgstr-variables,
po-syntax-error,
renamed-field-parameter,
resource-not-exist,
str-format-used,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
unnecessary-utf8-coding-comment,
website-manifest-key-not-valid-uri,
xml-attribute-translatable,
xml-deprecated-qweb-directive,
xml-deprecated-tree-attribute,
external-request-timeout
[REPORTS]
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
output-format=colorized
reports=no

View File

@@ -1,31 +0,0 @@
target-version = "py310"
fix = true
[lint]
extend-select = [
"B",
"C90",
"E501", # line too long (default 88)
"I", # isort
"UP", # pyupgrade
]
extend-safe-fixes = ["UP008"]
exclude = ["setup/*"]
[format]
exclude = ["setup/*"]
[lint.per-file-ignores]
"__init__.py" = ["F401", "I001"] # ignore unused and unsorted imports in __init__.py
"__manifest__.py" = ["B018"] # useless expression
[lint.isort]
section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"]
[lint.isort.sections]
"odoo" = ["odoo"]
"odoo-addons" = ["odoo.addons"]
[lint.mccabe]
max-complexity = 16

View File

@@ -11,7 +11,7 @@ Allow to create record of any model when sending the form :
* Associate question with fields
* For x2m fields : Associate values to questions
""",
"version": "16.0.1.0.2",
"version": "16.0.1.0.1",
"license": "AGPL-3",
"author": "Elabore",
"website": "https://www.elabore.coop",

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-03 16:35+0000\n"
"PO-Revision-Date: 2025-11-03 16:35+0000\n"
"POT-Creation-Date: 2025-04-15 10:34+0000\n"
"PO-Revision-Date: 2025-04-15 10:34+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -31,11 +31,6 @@ msgstr ""
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"
@@ -60,16 +55,6 @@ msgstr "Associer une valeur à la réponse"
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
@@ -117,11 +102,6 @@ msgstr ""
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"
@@ -190,6 +170,11 @@ msgstr "Aide"
msgid "ID"
msgstr ""
#. module: survey_record_generation
#: model:ir.model,name:survey_record_generation.model_survey_question
msgid "Inherit Survey Question for extra fields"
msgstr "Question du sondage"
#. 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
@@ -272,15 +257,6 @@ 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
@@ -353,11 +329,6 @@ msgstr "Sondage"
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"
@@ -370,8 +341,8 @@ 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"
msgid "Survey User Input for custom matrix"
msgstr "Entrée utilisateur du sondage"
#. module: survey_record_generation
#: model:ir.model.fields,field_description:survey_record_generation.field_survey_generated_record__survey_record_creation_id
@@ -383,36 +354,20 @@ msgstr "Génération d'enregistrement depuis la participation"
#: 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"
"The field %s is mandatory. In Record Creation tab, drag "
"%s at the top of the table"
msgstr ""
"Le champ %s est obligatoire. Dans l'onglet Création d'un enregistrement, glissez "
"%s sur la première ligne du tableau"
#. 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
@@ -445,50 +400,6 @@ msgstr "Message d'erreur"
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
@@ -520,4 +431,4 @@ 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 ""
msgstr ""

View File

@@ -1,14 +1,12 @@
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'
@@ -16,8 +14,8 @@ class SurveyQuestion(models.Model):
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)
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 = "[]"

View File

@@ -9,15 +9,15 @@ _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")
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:
@@ -31,7 +31,7 @@ class SurveyQuestionAnswer(models.Model):
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:
@@ -39,4 +39,4 @@ class SurveyQuestionAnswer(models.Model):
model,
res.id,
)
return result
return result

View File

@@ -1,3 +1,4 @@
import logging
from odoo import api, fields, models, _
@@ -12,38 +13,16 @@ class SurveyRecordCreation(models.Model):
name = fields.Char('Name')
survey_id = fields.Many2one('survey.survey', string="Survey")
model_id = fields.Many2one('ir.model', "Model", help="Model of generated record")
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), ('readonly', '=', False)]",
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",
)
@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")
@api.onchange('model_id')
def clear_field_values_ids(self):
self.field_values_ids = None
@api.depends("model_id","field_values_ids")
@api.depends('model_id','field_values_ids')
def _compute_warning_message(self):
for record_creation in self:
# check if all mandatory fields set
@@ -51,10 +30,14 @@ class SurveyRecordCreation(models.Model):
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

@@ -8,7 +8,7 @@ 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 ?
type_mapping = {
"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"],
@@ -17,7 +17,7 @@ type_mapping = { #field types on the left, question types on the right. TODO :
"date": ["date"],
"datetime": ["datetime"],
"many2one": ["simple_choice"],
"many2many": ["multiple_choice"],
"many2many": ["multiple_choice"],
"selection": ["char_box", "simple_choice"]
}
@@ -32,7 +32,7 @@ class SurveyRecordCreationFieldValues(models.Model):
model_id = fields.Many2one('ir.model', related="survey_record_creation_id.model_id")
field_id = fields.Many2one(
'ir.model.fields',
'ir.model.fields',
domain="[('model_id','=',model_id),('readonly','=',False),('ttype','in',['char','selection','text','html','integer','float','date','datetime','many2one','many2many', 'boolean'])]",
ondelete="cascade")
field_relation = fields.Char(related='field_id.relation')
@@ -40,10 +40,10 @@ class SurveyRecordCreationFieldValues(models.Model):
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',
[('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.""")
@@ -57,7 +57,7 @@ class SurveyRecordCreationFieldValues(models.Model):
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_datetime = fields.Datetime("Value")
fixed_value_boolean = fields.Boolean("Value")
displayed_value = fields.Char("Value", compute="_compute_displayed_value")
@@ -84,7 +84,7 @@ class SurveyRecordCreationFieldValues(models.Model):
record_creation_field_values.allowed_question_ids = None
return
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:
@@ -96,7 +96,7 @@ class SurveyRecordCreationFieldValues(models.Model):
@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
@@ -119,7 +119,7 @@ class SurveyRecordCreationFieldValues(models.Model):
# 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:
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
@@ -136,12 +136,12 @@ class SurveyRecordCreationFieldValues(models.Model):
if self.value_origin == 'fixed':
if self.field_type == 'many2one':
if self.fixed_value_many2one:
return self.fixed_value_many2one.id
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):
@@ -172,7 +172,7 @@ class SurveyRecordCreationFieldValues(models.Model):
record.displayed_value = ""
class SurveyRecordCreationFieldValuesX2m(models.Model):
"""O2m an M2m default values
"""O2m an M2m default values
"""
_name = 'survey.record.creation.field.values.x2m'
@@ -182,16 +182,16 @@ class SurveyRecordCreationFieldValuesX2m(models.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
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:
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,)))
raise ValueError(_('You should append at least one record in %s',(model_name,)))

View File

@@ -1,25 +1,13 @@
# 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 import models, fields, _
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"
)
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:
@@ -28,313 +16,109 @@ class SurveyUserInput(models.Model):
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 = self.env['ir.actions.act_window']._for_xml_id('survey_record_generation.survey_generated_record_action')
""" context = dict(self.env.context or {})
context['create'] = False
context['search_default_survey_id'] = self.survey_id.id
context['search_default_group_by_survey'] = False
if self.partner_id:
context['search_default_partner_id'] = self.partner_id.id
elif self.email:
context['search_default_email'] = self.email
action['context'] = context """
return action
def _mark_done(
self, ignore_when_res_partner_mandatory_fields_are_missing: bool = False
):
def _mark_done(self, ignore_when_res_partner_mandatory_fields_are_missing = False):
# generate records
for user_input in self:
created_records = {}
other_record_fields_to_update: list[SurveyRecordCreationFieldValues] = []
fields_to_update = []
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 record_creation in user_input.survey_id.survey_record_creation_ids.sorted('sequence'):
model = record_creation.model_id.model
vals = {}
ModelClass = self.env[model]
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
if field_value.value_origin == 'fixed':
vals[field_value.field_id.name] = field_value.get_fixed_value_for_record_creation()
elif field_value.value_origin == 'question':
# find user_input_lines of 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]
vals[field_name] = value
if not user_input_lines:
continue
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 field_value.question_id.question_type in ['simple_choice', 'multiple_choice','matrix']:
if field_value.question_id.answer_values_type == 'record':
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:
record_ids.append(user_input_line.suggested_answer_id.record_id.id)
if field_value.question_id.question_type == 'simple_choice':
vals[field_value.field_id.name] = record_ids[0]
else:
vals[field_value.field_id.name] = record_ids
if field_value.question_id.answer_values_type == 'value':
if field_value.field_id.ttype == "boolean":
boolean_value = user_input_lines[0].suggested_answer_id.value_char in [True, 1, "1", "True", "true", "Oui", "oui"]
vals[field_value.field_id.name] = boolean_value
else:
vals[field_value.field_id.name] = user_input_lines[0].suggested_answer_id.value_char
elif user_input_lines[0].answer_type: # if value not filled by user, answer_type not set
vals[field_value.field_id.name] = user_input_lines[0][f"value_{user_input_lines[0].answer_type}"]
else:
vals[field_value.field_id.name] = None
elif field_value.value_origin == 'other_record':
fields_to_update.append(field_value)
# check if the field to update is mandatory
if ModelClass._fields[field_value.field_id.name].required:
# check if the other record is already created, if yes add it to vals
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]
vals[field_value.field_id.name] = linked_record.id
else:
raise UserError(
_("The field %s is mandatory. In Record Creation tab, drag %s at the top of the table")
% (field_value.field_id.display_name, field_value.other_created_record_id.name)
)
# check duplicates
uniq_fields = [field_value.field_id.name for field_value in record_creation.field_values_ids.filtered(lambda r:r.unicity_check)]
duplicate = None
if uniq_fields:
uniq_domain = []
for uniq_field in uniq_fields:
uniq_domain.append((uniq_field,'=',vals[uniq_field]))
duplicate = self.env[model].search(uniq_domain, limit=1)
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)
}
record = existing_record.write(vals_with_keys_not_in_record)
else:
if (
model == "res.partner"
and ignore_when_res_partner_mandatory_fields_are_missing
):
# this part has been developed for Calim specific needs :
# being able to ignore some Contacts creation
# TODO : find a way to make it generic for all models
if model == "res.partner" and ignore_when_res_partner_mandatory_fields_are_missing:
# this part has been developed for Calim specific needs : being able to create several Contacts with the same survey
# TODO : find a way to make it generic for all models ?
if not vals.get("lastname") and not vals.get("firstname"):
continue
# Create record
record = self.env[model].create(vals)
# 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}",
}
)
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":"%s,%s" % (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
)
# update linked records
for field_to_update in 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})
linked_record = created_records[field_to_update.other_created_record_id.id]
record_to_update.write({field_to_update.field_id.name:linked_record.id})
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

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

View File

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

View File

@@ -1,840 +0,0 @@
from datetime import date
from psycopg2 import IntegrityError
from odoo.addons.survey.tests.common import SurveyCase
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,
)
### "date" type of question, tested with DATE field "date" ###
self.question_date = self._add_question(
page=None, name="Date", qtype="date", survey_id=self.survey.id, sequence=1
)
date_field = self.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "date")]
)
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": date_field.id,
"value_origin": "question",
"question_id": self.question_date.id,
}
)
self._add_answer_line(
question=self.question_date, answer=self.answer, answer_value=date.today()
)
### "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),
("date", "=", date.today()),
("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": "Second contact",
"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_required_fields_are_not_filled_up(self):
# In this test, we check the behavior when a required field is missing
self.question_email = self._add_question(
page=None,
name="Email",
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,
}
)
self.answer = self._add_answer(
survey=self.survey, partner=False, email="jean@test.fr"
)
self._add_answer_line(
question=self.question_email,
answer=self.answer,
answer_value="jean@test.fr",
)
with self.assertRaises(IntegrityError):
# TODO : propose a better user experience than IntegrityError when
# a mandatory field is missing
self.answer._mark_done()
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)

View File

@@ -18,26 +18,16 @@
<group>
<field name="name" />
<field name="model_id" />
<field name="update_existing_records" />
<field name="allowed_field_ids" attrs="{'invisible': True}"/>
<field name="field_to_retrieve_existing_records" attrs="{'invisible': [('update_existing_records', '=', False)]}"/>
<div colspan="2" style="width:100%;">
<div class="alert alert-warning"
attrs="{'invisible': [('update_existing_records', '=', False)]}">
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">
<tree>
<field name="field_id" />
<field name="displayed_value" />
<field name="unicity_check" />
</tree>
</tree>
<form>
<group>
<field name="model_id" invisible="1" />
<field name="field_id" />
<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" />
@@ -50,37 +40,37 @@
</group>
<div attrs="{'invisible':['|',('value_origin','!=','fixed'),('field_id','=',False)]}">
<group>
<field name="displayed_value" invisible="1" />
<field
name="fixed_value_char"
<field name="displayed_value" invisible="1" />
<field
name="fixed_value_char"
attrs="{'invisible':[('field_type','!=','char')]}"
/>
<field
name="fixed_value_selection"
<field
name="fixed_value_selection"
attrs="{'invisible':[('field_type','!=','selection')]}"
/>
<field
name="fixed_value_text"
<field
name="fixed_value_text"
attrs="{'invisible':[('field_type','!=','text')]}"
/>
<field
name="fixed_value_html"
<field
name="fixed_value_html"
attrs="{'invisible':[('field_type','!=', 'html')]}"
/>
<field
name="fixed_value_integer"
<field
name="fixed_value_integer"
attrs="{'invisible':[('field_type','!=', 'integer')]}"
/>
<field
name="fixed_value_float"
<field
name="fixed_value_float"
attrs="{'invisible':[('field_type','!=', 'float')]}"
/>
<field
name="fixed_value_date"
<field
name="fixed_value_date"
attrs="{'invisible':[('field_type','!=', 'date')]}"
/>
<field
name="fixed_value_datetime"
<field
name="fixed_value_datetime"
attrs="{'invisible':[('field_type','!=', 'datetime')]}"
/>
<field
@@ -97,7 +87,7 @@
attrs="{'invisible':[('field_type','not in',['one2many','many2many'])]}">
<tree editable="bottom">
<field name="survey_record_creation_field_values_id" invisible="1" />
<field name="value_reference"
<field name="value_reference"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
/>
</tree>
@@ -114,7 +104,7 @@
<field name="other_created_record_id" />
</group>
</div>
</form>
</form>
</field>
<div colspan="2">
<field name="warning_message" />