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 @@ -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() 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") @@ -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 = { 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 @@
YYYY-MM-DD
YYYY-MM-DD hh:mm:ss + +
+