I have written an addon that creates a new dropdown question type (Vehicle Registration) in Survey. When the user runs the survey, they are presented with a dropdown to select a vehicle from a dynamically created list of the currently registered vehicles in the Fleet module. This bit works. However, when the user submits the survey, it crashes with the error:
File "/opt/odoo17/odoo17/addons/survey/controllers/main.py", line 536, in survey_submit
answer_sudo._save_lines(question, answer, comment, overwrite_existing=survey_sudo.users_can_go_back or question.save_as_nickname or question.save_as_email)
File "/opt/odoo17/odoo17/addons/survey/models/survey_user_input.py", line 301, in _save_lines
raise AttributeError(question.question_type + ": This type of question has no saving function")
AttributeError: vehicle_dropdown: This type of question has no saving functionThis is my survey_question.py:
from odoo import models, fields, apiimport logging
_logger = logging.getLogger(__name__)
class SurveyQuestion(models.Model):
_inherit = 'survey.question'
question_type = fields.Selection(selection_add=[('vehicle_dropdown', 'Vehicle Dropdown')])
fleet_vehicles = fields.Many2many('fleet.vehicle', string='Fleet Vehicles')
class SurveyUserInputLine(models.Model):
_inherit = 'survey.user_input.line'
@api.model
def get_options(self):
self.ensure_one()
if self.question_id.question_type == 'vehicle_dropdown':
return [(vehicle.id, vehicle.license_plate) for vehicle in self.question_id.fleet_vehicles]
return super().get_options()
def _save_lines(self, question, answer, comment=None, overwrite_existing=False):
_logger.info(f"Saving lines for question: {question.id} ({question.question_type})")
_logger.info(f"Answer: {answer}, Type: {type(answer)}")
if question.question_type == 'vehicle_dropdown':
if answer:
try:
answer = int(answer)
_logger.info(f"Answer after int conversion: {answer}, Type: {type(answer)}")
existing_line = self.search([
('user_input_id', '=', self.user_input_id.id),
('question_id', '=', question.id)
], limit=1)
if existing_line and overwrite_existing:
existing_line.write({'value_reference': answer})
_logger.info(f"Updated existing line {existing_line.id} with value_reference {answer}")
else:
self.create({
'user_input_id': self.user_input_id.id,
'question_id': question.id,
'answer_type': 'selection',
'value_reference': answer,
})
_logger.info(f"Created new line with value_reference {answer}")
return True
except ValueError as e:
_logger.error(f"Could not convert answer to integer: {e}")
except Exception as e:
_logger.exception(f"Error in _save_lines: {e}")
else:
_logger.info("No answer provided for vehicle dropdown")
return super()._save_lines(question, answer, comment, overwrite_existing)
And this is my survey_question_views.xml:
<?xml version="1.0" encoding="UTF-8"?><odoo>
<!-- Inherit the survey.question form view -->
<record id="view_survey_question_form_inherit" model="ir.ui.view">
<field name="name">survey.question.form.inherit</field>
<field name="model">survey.question</field>
<field name="inherit_id" ref="survey.survey_question_form"/>
<field name="arch" type="xml">
<!-- Add the Fleet Vehicles field after the question type field -->
<xpath expr="//field[@name='question_type']" position="after">
<field name="fleet_vehicles" widget="many2many_tags" options="{'no_create': True}" placeholder="Select vehicles"/>
</xpath>
</field>
</record>
<!-- Inherit the survey.question_container template -->
<template id="survey_vehicle_dropdown" inherit_id="survey.question_container">
<!-- Target the inner div after the <h3> question title -->
<xpath expr="//div[h3]" position="after">
<t t-if="question.question_type == 'vehicle_dropdown'">
<div class="form-group">
<label t-esc="question.title" class="form-label"/>
<select name="answer" class="form-control" required="1">
<option value="">Select a Vehicle</option>
<!-- Dynamically load only vehicles with 'Registered' status -->
<t t-foreach="env['fleet.vehicle'].search([('state_id', '=', 3)])" t-as="vehicle">
<option t-att-value="vehicle.id" t-esc="vehicle.license_plate"/>
</t>
</select>
</div>
</t>
</xpath>
</template>
</odoo>
Can anyone help where I'm going wrong?