[NEW] survey_record_generation

This commit is contained in:
clementthomas
2025-02-20 19:30:36 +01:00
parent cbf71900ce
commit bf78651b31
12 changed files with 542 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,26 @@
# 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": "16.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",
],
"installable": True,
}

View File

@@ -0,0 +1,6 @@
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

View File

@@ -0,0 +1,25 @@
import logging
from odoo import api, fields, models, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class SurveyQuestion(models.Model):
_inherit = 'survey.question'
model_id = fields.Many2one('ir.model', string="Model")
answer_values_type = fields.Selection([('no', 'No values'),('value','Value'),('record','Record')])
@api.onchange('model_id')
def onchange_model_id(self):
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}"

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,42 @@
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")
@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,189 @@
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 = {
"char": ["char_box", "numerical_box", "date", "datetime", "simple_choice", "multiple_choice"],
"text": ["char_box", "date"],
"html": ["text_box", "numerical_box", "datetime", "simple_choice", "multiple_choice"],
"integer": ["numerical_box"],
"float": ["numerical_box"],
"date": ["date"],
"datetime": ["datetime"],
"many2one": ["simple_choice"],
"many2many": ["multiple_choice"],
"selection": ["char_box"]
}
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),('readonly','=',False),('ttype','in',['char','selection','text','html','integer','float','date','datetime','many2one','many2many'])]")
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")
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)]")
@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
return
question_domain = [('survey_id','=',record_creation_field_values.survey_id.id),'|','&',('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([])]
@api.onchange('field_id','origin')
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):
# 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,53 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class SurveyUserInput(models.Model):
_inherit = "survey.user_input"
def _mark_done(self):
# generate records
for user_input in self:
created_records = {}
fields_to_update = []
for record_creation in user_input.survey_id.survey_record_creation_ids:
model = record_creation.model_id.model
vals = {}
for field_value in record_creation.field_values_ids:
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]
if field_value.question_id.question_type in ['simple_choice', 'multiple_choice','matrix']:
if field_value.question_id.answer_value_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_value_type == 'value':
vals[field_value.field_id.name] = user_input_line.suggested_answer_id.value_char
else:
user_input_line = user_input_lines[0]
vals[field_value.field_id.name] = user_input_line[0][f"value_{user_input_line.answer_type}"]
elif field_value.value_origin == 'other_record':
fields_to_update.append(field_value)
# Create record
record = self.env[model].create(vals)
created_records[record_creation.id] = record
# update linked records
for field_to_update in fields_to_update:
record_to_update = created_records[field_to_update.survey_record_creation_id.id]
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()

View File

@@ -0,0 +1,4 @@
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
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

View File

@@ -0,0 +1,28 @@
<?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" attrs="{'invisible':[('answer_values_type','!=','record')]}" />
</xpath>
<xpath expr="//field[@name='suggested_answer_ids']" position="attributes">
<attribute
name="context"
>{'default_question_id': active_id, 'default_model_id': model_id}</attribute>
</xpath>
<xpath expr="//field[@name='suggested_answer_ids']/tree" 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}"
attrs="{'invisible':[('answer_values_type','!=','record')]}"/>
<field name="value_char"
attrs="{'invisible':[('answer_values_type','!=','value')]}" />
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,113 @@
<?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">
<tree>
<field name="name" />
<field name="model_id" />
</tree>
<form>
<group>
<field name="name" />
<field name="model_id" />
<field name="field_values_ids">
<tree>
<field name="field_id" />
<field name="displayed_value" />
</tree>
<form>
<group>
<field name="model_id" invisible="1" />
<field name="field_id" />
<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 attrs="{'invisible':['|',('value_origin','!=','fixed'),('field_id','=',False)]}">
<group>
<field name="displayed_value" invisible="1" />
<field
name="fixed_value_char"
attrs="{'invisible':[('field_type','in',['selection','text','html','integer','float','date','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_selection"
attrs="{'invisible':[('field_type','in',['char','text','html','integer','float','date','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_text"
attrs="{'invisible':[('field_type','in',['char','selection','html','integer','float','date','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_html"
attrs="{'invisible':[('field_type','in',['char','selection','text','integer','float','date','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_integer"
attrs="{'invisible':[('field_type','in',['char','selection','text','html','float','date','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_float"
attrs="{'invisible':[('field_type','in',['char','selection','text','html','integer','date','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_date"
attrs="{'invisible':[('field_type','in',['char','selection','text','html','integer','float','datetime','many2one','many2many'])]}"
/>
<field
name="fixed_value_datetime"
attrs="{'invisible':[('field_type','in',['char','selection','text','html','integer','float','date','many2one','many2many'])]}"
/>
<field
name="fixed_value_many2one"
attrs="{'invisible':[('field_type','!=','many2one')]}"
readonly="False"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
/>
<field name="fixed_value_many2many"
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"
options="{'hide_model': True, 'no_create': True, 'no_edit': True, 'no_open': True}"
/>
</tree>
</field>
</group>
</div>
<div attrs="{'invisible':['|',('value_origin','!=','question'),('field_id','=',False)]}">
<group>
<field name="question_id" />
</group>
</div>
<div attrs="{'invisible':['|','|',('value_origin','!=','other_record'),('field_id','=',False),('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>