summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Beguin <dbe@odoo.com>2018-11-26 13:51:41 +0000
committerDavid Beguin <dbe@odoo.com>2019-01-23 13:28:30 +0000
commitd2ed4ba93d0a6c2e9e6982acb45b761f9f89e7a9 (patch)
treeaf0741f15eea33b669e9ff69ce6b5a5ae16479e9
parentf2e2b6260b3436cf894a9ea8343fb888844ed632 (diff)
[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
-rw-r--r--addons/survey/controllers/main.py4
-rw-r--r--addons/survey/models/survey_question.py34
-rw-r--r--addons/survey/models/survey_user.py25
-rw-r--r--addons/survey/static/src/js/survey.js44
-rw-r--r--addons/survey/views/survey_question_views.xml12
-rw-r--r--addons/survey/views/survey_templates.xml16
6 files changed, 118 insertions, 17 deletions
diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py
index 7fdd9d6661f..5b085d8181a 100644
--- a/addons/survey/controllers/main.py
+++ b/addons/survey/controllers/main.py
@@ -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:
diff --git a/addons/survey/models/survey_question.py b/addons/survey/models/survey_question.py
index 56647bb2830..565f04fec5a 100644
--- a/addons/survey/models/survey_question.py
+++ b/addons/survey/models/survey_question.py
@@ -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
@@ -207,6 +219,14 @@ class SurveyQuestion(models.Model):
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()
errors = {}
diff --git a/addons/survey/models/survey_user.py b/addons/survey/models/survey_user.py
index d034098692b..1bdcba0c1c2 100644
--- a/addons/survey/models/survey_user.py
+++ b/addons/survey/models/survey_user.py
@@ -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")
@@ -267,6 +269,29 @@ class SurveyUserInputLine(models.Model):
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 = {
'user_input_id': user_input_id,
diff --git a/addons/survey/static/src/js/survey.js b/addons/survey/static/src/js/survey.js
index 2b9094e37b1..9cae86adefd 100644
--- a/addons/survey/static/src/js/survey.js
+++ b/addons/survey/static/src/js/survey.js
@@ -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',
diff --git a/addons/survey/views/survey_question_views.xml b/addons/survey/views/survey_question_views.xml
index b66fc391558..b51bab4de08 100644
--- a/addons/survey/views/survey_question_views.xml
+++ b/addons/survey/views/survey_question_views.xml
@@ -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>
diff --git a/addons/survey/views/survey_templates.xml b/addons/survey/views/survey_templates.xml
index f789cfe28a9..1aca736513a 100644
--- a/addons/survey/views/survey_templates.xml
+++ b/addons/survey/views/survey_templates.xml
@@ -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>