[IMP] survey : add datetime question type
to be able to choose between asking for a date or a date with time, a datetime question type is now added. Task ID : 1911291 Closes PR #29054
This commit is contained in:
@@ -236,7 +236,9 @@ class Survey(http.Controller):
|
||||
elif answer.answer_type == 'number':
|
||||
answer_value = str(answer.value_number)
|
||||
elif answer.answer_type == 'date':
|
||||
answer_value = fields.Date.to_string(answer.value_date)
|
||||
answer_value = fields.Datetime.to_string(answer.value_date)
|
||||
elif answer.answer_type == 'datetime':
|
||||
answer_value = fields.Datetime.to_string(answer.value_datetime)
|
||||
elif answer.answer_type == 'suggestion' and not answer.value_suggested_row:
|
||||
answer_value = answer.value_suggested.id
|
||||
elif answer.answer_type == 'suggestion' and answer.value_suggested_row:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import logging
|
||||
import re
|
||||
import datetime
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import ValidationError
|
||||
@@ -41,6 +42,7 @@ class SurveyQuestion(models.Model):
|
||||
('textbox', 'Single Line Text Box'),
|
||||
('numerical_box', 'Numerical Value'),
|
||||
('date', 'Date'),
|
||||
('datetime', 'Datetime'),
|
||||
('simple_choice', 'Multiple choice: only one answer'),
|
||||
('multiple_choice', 'Multiple choice: multiple answers allowed'),
|
||||
('matrix', 'Matrix')], string='Type of Question',
|
||||
@@ -77,6 +79,8 @@ class SurveyQuestion(models.Model):
|
||||
validation_max_float_value = fields.Float('Maximum value')
|
||||
validation_min_date = fields.Date('Minimum Date')
|
||||
validation_max_date = fields.Date('Maximum Date')
|
||||
validation_min_datetime = fields.Datetime('Minimum Datetime')
|
||||
validation_max_datetime = fields.Datetime('Maximum Datetime')
|
||||
validation_error_msg = fields.Char('Validation Error message', translate=True, default=lambda self: _("The answer you entered is not valid."))
|
||||
# Constraints on number of answers (matrices)
|
||||
constr_mandatory = fields.Boolean('Mandatory Answer')
|
||||
@@ -91,7 +95,8 @@ class SurveyQuestion(models.Model):
|
||||
('positive_len_max', 'CHECK (validation_length_max >= 0)', 'A length must be positive!'),
|
||||
('validation_length', 'CHECK (validation_length_min <= validation_length_max)', 'Max length cannot be smaller than min length!'),
|
||||
('validation_float', 'CHECK (validation_min_float_value <= validation_max_float_value)', 'Max value cannot be smaller than min value!'),
|
||||
('validation_date', 'CHECK (validation_min_date <= validation_max_date)', 'Max date cannot be smaller than min date!')
|
||||
('validation_date', 'CHECK (validation_min_date <= validation_max_date)', 'Max date cannot be smaller than min date!'),
|
||||
('validation_datetime', 'CHECK (validation_min_datetime <= validation_max_datetime)','Max datetime cannot be smaller than min datetime!')
|
||||
]
|
||||
|
||||
@api.onchange('validation_email')
|
||||
@@ -169,10 +174,11 @@ class SurveyQuestion(models.Model):
|
||||
errors.update({answer_tag: self.validation_error_msg})
|
||||
return errors
|
||||
|
||||
@api.multi
|
||||
def validate_date(self, post, answer_tag):
|
||||
def date_validation(self, date_type, post, answer_tag, min_value, max_value):
|
||||
self.ensure_one()
|
||||
errors = {}
|
||||
if date_type not in ('date', 'datetime'):
|
||||
raise ValueError("Unexpected date type value")
|
||||
answer = post[answer_tag].strip()
|
||||
# Empty answer to mandatory question
|
||||
if self.constr_mandatory and not answer:
|
||||
@@ -180,7 +186,10 @@ class SurveyQuestion(models.Model):
|
||||
# Checks if user input is a date
|
||||
if answer:
|
||||
try:
|
||||
dateanswer = fields.Date.from_string(answer)
|
||||
if date_type == 'datetime':
|
||||
dateanswer = fields.Datetime.from_string(answer)
|
||||
else:
|
||||
dateanswer = fields.Date.from_string(answer)
|
||||
except ValueError:
|
||||
errors.update({answer_tag: _('This is not a date')})
|
||||
return errors
|
||||
@@ -188,10 +197,13 @@ class SurveyQuestion(models.Model):
|
||||
if answer and self.validation_required:
|
||||
# Answer is not in the right range
|
||||
try:
|
||||
date_from_string = fields.Date.from_string
|
||||
if date_type == 'datetime':
|
||||
date_from_string = fields.Datetime.from_string
|
||||
else:
|
||||
date_from_string = fields.Date.from_string
|
||||
dateanswer = date_from_string(answer)
|
||||
min_date = date_from_string(self.validation_min_date)
|
||||
max_date = date_from_string(self.validation_max_date)
|
||||
min_date = date_from_string(min_value)
|
||||
max_date = date_from_string(max_value)
|
||||
|
||||
if min_date and max_date and not (min_date <= dateanswer <= max_date):
|
||||
# If Minimum and Maximum Date are entered
|
||||
@@ -206,6 +218,14 @@ class SurveyQuestion(models.Model):
|
||||
pass
|
||||
return errors
|
||||
|
||||
@api.multi
|
||||
def validate_date(self, post, answer_tag):
|
||||
return self.date_validation('date', post, answer_tag, self.validation_min_date, self.validation_max_date)
|
||||
|
||||
@api.multi
|
||||
def validate_datetime(self, post, answer_tag):
|
||||
return self.date_validation('datetime', post, answer_tag, self.validation_min_datetime, self.validation_max_datetime)
|
||||
|
||||
@api.multi
|
||||
def validate_simple_choice(self, post, answer_tag):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -110,11 +110,13 @@ class SurveyUserInputLine(models.Model):
|
||||
('text', 'Text'),
|
||||
('number', 'Number'),
|
||||
('date', 'Date'),
|
||||
('datetime', 'Datetime'),
|
||||
('free_text', 'Free Text'),
|
||||
('suggestion', 'Suggestion')], string='Answer Type')
|
||||
value_text = fields.Char('Text answer')
|
||||
value_number = fields.Float('Numerical answer')
|
||||
value_date = fields.Date('Date answer')
|
||||
value_datetime = fields.Datetime('Datetime answer')
|
||||
value_free_text = fields.Text('Free Text answer')
|
||||
value_suggested = fields.Many2one('survey.label', string="Suggested answer")
|
||||
value_suggested_row = fields.Many2one('survey.label', string="Row answer")
|
||||
@@ -266,6 +268,29 @@ class SurveyUserInputLine(models.Model):
|
||||
old_uil.create(vals)
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def save_line_datetime(self, user_input_id, question, post, answer_tag):
|
||||
vals = {
|
||||
'user_input_id': user_input_id,
|
||||
'question_id': question.id,
|
||||
'survey_id': question.survey_id.id,
|
||||
'skipped': False
|
||||
}
|
||||
if answer_tag in post and post[answer_tag].strip():
|
||||
vals.update({'answer_type': 'datetime', 'value_datetime': post[answer_tag]})
|
||||
else:
|
||||
vals.update({'answer_type': None, 'skipped': True})
|
||||
old_uil = self.search([
|
||||
('user_input_id', '=', user_input_id),
|
||||
('survey_id', '=', question.survey_id.id),
|
||||
('question_id', '=', question.id)
|
||||
])
|
||||
if old_uil:
|
||||
old_uil.write(vals)
|
||||
else:
|
||||
old_uil.create(vals)
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def save_line_simple_choice(self, user_input_id, question, post, answer_tag):
|
||||
vals = {
|
||||
|
||||
@@ -79,10 +79,15 @@ if(!the_form.length) {
|
||||
|
||||
// prefill of text/number/date boxes
|
||||
var input = the_form.find(".form-control[name=" + key + "]");
|
||||
if (input.attr('date')) {
|
||||
if (input.hasClass('datetimepicker-input')) {
|
||||
// display dates in user timezone
|
||||
var moment_date = field_utils.parse.date(value[0]);
|
||||
value = field_utils.format.date(moment_date, null, {timezone: true});
|
||||
if ($(input).closest('.input-group.date').data('questiontype') === 'datetime') {
|
||||
value = field_utils.format.datetime(moment_date, null, {timezone: true});
|
||||
}
|
||||
else {
|
||||
value = field_utils.format.date(moment_date, null, {timezone: true});
|
||||
}
|
||||
}
|
||||
input.val(value);
|
||||
|
||||
@@ -133,7 +138,12 @@ if(!the_form.length) {
|
||||
var moment_date = moment(el.value);
|
||||
if (moment_date) {
|
||||
moment_date.toJSON = function () {
|
||||
return this.clone().locale('en').format('YYYY-MM-DD');
|
||||
if ($(el).closest('.input-group.date').data('questiontype') === 'datetime') {
|
||||
return this.clone().utc().format('YYYY-MM-DD hh:mm:ss');
|
||||
}
|
||||
else {
|
||||
return this.clone().format('YYYY-MM-DD');
|
||||
}
|
||||
};
|
||||
}
|
||||
var field_obj = _.findWhere(formData, {'name': el.name});
|
||||
@@ -177,13 +187,33 @@ if(!the_form.length) {
|
||||
// frontend does not load moment locale at all.
|
||||
// so wait until DOM ready with locale then init datetimepicker
|
||||
load_locale().then(function(){
|
||||
_.each($('.input-group.date'), function(date_field){
|
||||
var minDate = $(date_field).data('mindate') || moment({ y: 1900 });
|
||||
var maxDate = $(date_field).data('maxdate') || moment().add(200, "y");
|
||||
_.each($('.input-group.date'), function(date_field){
|
||||
var disabledDates = []
|
||||
if ($(date_field).data('mindate')) {
|
||||
var minDate = field_utils.format.datetime(moment($(date_field).data('mindate')), null, {timezone: true});
|
||||
}
|
||||
if ($(date_field).data('maxdate')) {
|
||||
var maxDate = field_utils.format.datetime(moment($(date_field).data('maxdate')), null, {timezone: true});
|
||||
}
|
||||
else {
|
||||
var minDate = $(date_field).data('mindate') || moment({ y: 1900 });
|
||||
var maxDate = $(date_field).data('maxdate') || moment().add(200, "y");
|
||||
}
|
||||
var datetimepickerFormat = time.getLangDateFormat()
|
||||
if ($(date_field).data('questiontype') === 'datetime') {
|
||||
datetimepickerFormat = time.getLangDatetimeFormat()
|
||||
}
|
||||
else{
|
||||
maxDate = moment(maxDate).add(1, "d");
|
||||
disabledDates = [maxDate]
|
||||
}
|
||||
$('#' + date_field.id).datetimepicker({
|
||||
format : time.getLangDateFormat(),
|
||||
format : datetimepickerFormat,
|
||||
minDate: minDate,
|
||||
maxDate: maxDate,
|
||||
disabledDates: disabledDates,
|
||||
useCurrent: false,
|
||||
viewDate: moment(new Date()).hours(0).minutes(0).seconds(0).milliseconds(0),
|
||||
calendarWeeks: true,
|
||||
icons: {
|
||||
time: 'fa fa-clock-o',
|
||||
|
||||
@@ -31,12 +31,18 @@
|
||||
<div attrs="{'invisible': [('question_type', '!=', 'numerical_box')]}">
|
||||
<i class="fa fa-2x" role="img" aria-label="Numeric" title="Numeric">123..</i>
|
||||
</div>
|
||||
<!-- Date and Time -->
|
||||
<!-- Date -->
|
||||
<div attrs="{'invisible': [('question_type', '!=', 'date')]}">
|
||||
<p class="o_datetime">YYYY-MM-DD
|
||||
<i class="fa fa-calendar fa-2x" role="img" aria-label="Calendar" title="Calendar"/>
|
||||
</p>
|
||||
</div>
|
||||
<!-- Date and Time -->
|
||||
<div attrs="{'invisible': [('question_type', '!=', 'datetime')]}">
|
||||
<p class="o_datetime">YYYY-MM-DD hh:mm:ss
|
||||
<i class="fa fa-calendar fa-2x" role="img" aria-label="Calendar" title="Calendar"/>
|
||||
</p>
|
||||
</div>
|
||||
<!-- Multiple choice: only one answer -->
|
||||
<div attrs="{'invisible': [('question_type', '!=', 'simple_choice')]}" role="img" aria-label="Multiple choice with one answer" title="Multiple choice with one answer">
|
||||
<div class="row"><i class="fa fa-circle-o fa-lg"/> answer</div>
|
||||
@@ -106,7 +112,7 @@
|
||||
<field name="constr_mandatory" string="Mandatory Answer"/>
|
||||
<field name="constr_error_msg" attrs="{'invisible': [('constr_mandatory', '=', False)]}"/>
|
||||
</group>
|
||||
<div colspan="2" attrs="{'invisible': [('question_type', 'not in', ['textbox', 'numerical_box', 'date'])]}">
|
||||
<div colspan="2" attrs="{'invisible': [('question_type', 'not in', ['textbox', 'numerical_box', 'date', 'datetime'])]}">
|
||||
<group>
|
||||
<field name="validation_required" attrs="{'invisible': [('validation_email', '=', True), ('question_type', '=', 'textbox')]}"/>
|
||||
</group>
|
||||
@@ -117,6 +123,8 @@
|
||||
<field name="validation_max_float_value" attrs="{'invisible': [('question_type', '!=', 'numerical_box')]}"/>
|
||||
<field name="validation_min_date" attrs="{'invisible': [('question_type', '!=', 'date')]}"/>
|
||||
<field name="validation_max_date" attrs="{'invisible': [('question_type', '!=', 'date')]}"/>
|
||||
<field name="validation_min_datetime" widget="datetime" attrs="{'invisible': [('question_type', '!=', 'datetime')]}"/>
|
||||
<field name="validation_max_datetime" widget="datetime" attrs="{'invisible': [('question_type', '!=', 'datetime')]}"/>
|
||||
<field name="validation_error_msg" colspan="4"/>
|
||||
</group>
|
||||
</div>
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
<t t-if="question.question_type == 'textbox'"><t t-call="survey.textbox"/></t>
|
||||
<t t-if="question.question_type == 'numerical_box'"><t t-call="survey.numerical_box"/></t>
|
||||
<t t-if="question.question_type == 'date'"><t t-call="survey.date"/></t>
|
||||
<t t-if="question.question_type == 'datetime'"><t t-call="survey.datetime"/></t>
|
||||
<t t-if="question.question_type == 'simple_choice'"><t t-call="survey.simple_choice"/></t>
|
||||
<t t-if="question.question_type == 'multiple_choice'"><t t-call="survey.multiple_choice"/></t>
|
||||
<t t-if="question.question_type == 'matrix'"><t t-call="survey.matrix"/></t>
|
||||
@@ -257,6 +258,20 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="datetime" name="DateTime box">
|
||||
<div class="form-group">
|
||||
<div class="input-group date" t-attf-id="datetimepicker_#{question.id}" data-target-input="nearest"
|
||||
t-att-data-mindate="question.validation_min_datetime"
|
||||
t-att-data-maxdate="question.validation_max_datetime"
|
||||
t-att-data-questiontype="question.question_type">
|
||||
<input type="text" class="form-control datetimepicker-input" t-attf-data-target="#datetimepicker_#{question.id}" t-att-name="prefix"/>
|
||||
<div class="input-group-append" t-attf-data-target="#datetimepicker_#{question.id}" data-toggle="datetimepicker">
|
||||
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="simple_choice" name="Simple choice">
|
||||
<div t-if="question.display_mode == 'dropdown' and token" class="js_drop row">
|
||||
<div class="col-lg-12">
|
||||
@@ -377,6 +392,7 @@
|
||||
<t t-if="question.question_type == 'textbox'"><t t-call="survey.textbox"/></t>
|
||||
<t t-if="question.question_type == 'numerical_box'"><t t-call="survey.numerical_box"/></t>
|
||||
<t t-if="question.question_type == 'date'"><t t-call="survey.date"/></t>
|
||||
<t t-if="question.question_type == 'datetime'"><t t-call="survey.datetime"/></t>
|
||||
<t t-if="question.question_type == 'simple_choice'"><t t-call="survey.simple_choice"/></t>
|
||||
<t t-if="question.question_type == 'multiple_choice'"><t t-call="survey.multiple_choice"/></t>
|
||||
<t t-if="question.question_type == 'matrix'"><t t-call="survey.matrix"/></t>
|
||||
|
||||
Reference in New Issue
Block a user