From 0020f35399dd49464b001bef29fc9f785476f226 Mon Sep 17 00:00:00 2001 From: Quentin Mondot Date: Wed, 21 Oct 2026 12:22:01 +0200 Subject: [PATCH] [FIX] survey_record_generation: questions without answer no longer empty fields When a question was not displayed (conditional question) or skipped, its field was still given to create() with a None value. The field was then emptied instead of keeping its default or computed value. Since the link to an already created record is given at creation, this also made sale.subscription.line price_unit equal to 0 when the "free amount" question was not displayed, instead of the product price. Fields without value are now left out of the values, which also allows several conditional questions to fill up the same field (e.g. one list of amounts per periodicity). Assisted-By: Claude Opus 5.5 --- .../models/survey_user_input.py | 7 +- .../tests/test_computed_fields.py | 8 ++ .../tests/test_survey_record_creation.py | 79 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/survey_record_generation/models/survey_user_input.py b/survey_record_generation/models/survey_user_input.py index 8cccae0..68b2192 100644 --- a/survey_record_generation/models/survey_user_input.py +++ b/survey_record_generation/models/survey_user_input.py @@ -61,6 +61,11 @@ class SurveyUserInput(models.Model): ) field_name: str = field_value.field_id.name + if value is None: + # No value (e.g. question not displayed or skipped): + # keep the default/computed value of the field, and + # let another question fill up the same field. + continue vals[field_name] = value existing_record = self.find_existing_record(record_creation, vals) @@ -195,7 +200,7 @@ class SurveyUserInput(models.Model): if unique_fields: uniq_domain = [] for uniq_field in unique_fields: - uniq_domain.append((uniq_field, "=", vals[uniq_field])) + uniq_domain.append((uniq_field, "=", vals.get(uniq_field, False))) duplicate = self.env[model].search(uniq_domain, limit=1) return duplicate diff --git a/survey_record_generation/tests/test_computed_fields.py b/survey_record_generation/tests/test_computed_fields.py index 79f14ce..4ae40b2 100644 --- a/survey_record_generation/tests/test_computed_fields.py +++ b/survey_record_generation/tests/test_computed_fields.py @@ -110,3 +110,11 @@ class TestComputedFields(SurveyCase): self.assertEqual(len(line), 1) self.assertEqual(line.price_unit, 33.0) + + def test_computed_field_applies_when_question_is_not_displayed(self): + # E.g. the "free amount" question is only displayed when the user + # chose "Other amount": otherwise the line keeps the computed price. + line = self._submit() + + self.assertEqual(len(line), 1) + self.assertEqual(line.price_unit, 20.0) diff --git a/survey_record_generation/tests/test_survey_record_creation.py b/survey_record_generation/tests/test_survey_record_creation.py index 79e5e75..17f007e 100644 --- a/survey_record_generation/tests/test_survey_record_creation.py +++ b/survey_record_generation/tests/test_survey_record_creation.py @@ -1072,3 +1072,82 @@ class TestSurveyRecordCreation(SurveyCase): self.assertEqual(self.answer.partner_id, jean) self.assertNotEqual(self.answer.partner_id, jeanne) self.assertEqual(self.answer.email, jean.email) + + def test_question_not_displayed_does_not_override_default_value(self): + # When a question is not displayed to the user (e.g. a conditional + # question), the field must keep its default (or computed) value + # instead of being emptied. + question_type = self._add_question( + page=None, + name="Type", + qtype="simple_choice", + labels=[{"value": "contact"}, {"value": "other"}], + survey_id=self.survey.id, + sequence=2, + answer_values_type="value", + ) + for suggested_answer in question_type.suggested_answer_ids: + suggested_answer.value_char = suggested_answer.value + type_field = self.env["ir.model.fields"]._get("res.partner", "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": question_type.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.assertEqual(partner.type, "contact") + + def test_several_conditional_questions_on_the_same_field(self): + # Several questions can fill up the same field (e.g. one list of + # amounts per periodicity, displayed conditionally): the answered + # question wins, the questions not displayed are ignored. + street_field = self.env["ir.model.fields"]._get("res.partner", "street") + questions = self.env["survey.question"] + for sequence, name in enumerate(["Street (France)", "Street (abroad)"], 2): + question = self._add_question( + page=None, + name=name, + qtype="char_box", + survey_id=self.survey.id, + sequence=sequence, + ) + 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": question.id, + } + ) + questions |= question + + 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" + ) + # Only the first question is displayed and answered + self._add_answer_line( + question=questions[0], answer=self.answer, answer_value="1 rue de Paris" + ) + self.answer._mark_done() + + partner = self.env["res.partner"].search([("name", "=", "Jean")]) + self.assertEqual(partner.street, "1 rue de Paris")