@ -0,0 +1,48 @@ | 
				
			|||||
 | 
					.. image:: https://img.shields.io/badge/licence-OPL--1-red.svg | 
				
			||||
 | 
					    :target: https://www.odoo.com/documentation/16.0/legal/licenses.html#odoo-apps | 
				
			||||
 | 
					    :alt: License: OPL-1 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Enhanced Survey Management | 
				
			||||
 | 
					========================== | 
				
			||||
 | 
					* Enhance your survey management with this module. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Installation | 
				
			||||
 | 
					============ | 
				
			||||
 | 
					    - www.odoo.com/documentation/16.0/setup/install.html | 
				
			||||
 | 
					    - Install our custom addon | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					License | 
				
			||||
 | 
					------- | 
				
			||||
 | 
					Odoo Proprietary License v1.0 (OPL-1) | 
				
			||||
 | 
					(https://www.odoo.com/documentation/16.0/legal/licenses.html#odoo-apps) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Company | 
				
			||||
 | 
					------- | 
				
			||||
 | 
					* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Credits | 
				
			||||
 | 
					------- | 
				
			||||
 | 
					* Developers: (V16) Mohammed Savad, Ahammed Harshad, Gee Paul Joby, Nihala Jebin, | 
				
			||||
 | 
					              (V17) Nihala KP | 
				
			||||
 | 
					Contact: odoo@cybrosys.com | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Contacts | 
				
			||||
 | 
					-------- | 
				
			||||
 | 
					* Mail Contact : odoo@cybrosys.com | 
				
			||||
 | 
					* Website : https://cybrosys.com | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Bug Tracker | 
				
			||||
 | 
					----------- | 
				
			||||
 | 
					Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Maintainer | 
				
			||||
 | 
					========== | 
				
			||||
 | 
					.. image:: https://cybrosys.com/images/logo.png | 
				
			||||
 | 
					   :target: https://cybrosys.com | 
				
			||||
 | 
					This module is maintained by Cybrosys Technologies. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					For support and more information, please visit https://www.cybrosys.com | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Further information | 
				
			||||
 | 
					=================== | 
				
			||||
 | 
					HTML Description: `<static/description/index.html>`__ | 
				
			||||
@ -0,0 +1,23 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from . import controllers | 
				
			||||
 | 
					from . import models | 
				
			||||
@ -0,0 +1,68 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################# | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    You can modify it under the terms of the GNU LESSER | 
				
			||||
 | 
					#    GENERAL PUBLIC LICENSE (LGPL v3), Version 3. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is distributed in the hope that it will be useful, | 
				
			||||
 | 
					#    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
				
			||||
 | 
					#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
				
			||||
 | 
					#    GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE | 
				
			||||
 | 
					#    (LGPL v3) along with this program. | 
				
			||||
 | 
					#    If not, see <http://www.gnu.org/licenses/>. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################# | 
				
			||||
 | 
					{ | 
				
			||||
 | 
					    'name': 'Enhanced Survey Management', | 
				
			||||
 | 
					    'version': '17.0.1.0.0', | 
				
			||||
 | 
					    'category': 'Extra Tools', | 
				
			||||
 | 
					    'summary': 'Enhance your survey management with new question kinds and more', | 
				
			||||
 | 
					    'description': """Upgrade your survey management capabilities with the ' | 
				
			||||
 | 
					                   'addition of versatile question types. ' | 
				
			||||
 | 
					                   'Capture specific timeframes by incorporating questions ' | 
				
			||||
 | 
					                   'about the month, week, or range, ' | 
				
			||||
 | 
					                   'enabling finer data analysis. ' | 
				
			||||
 | 
					                   'Furthermore, enhance data collection by allowing ' | 
				
			||||
 | 
					                   'respondents to upload files, ' | 
				
			||||
 | 
					                   'fostering a more comprehensive understanding of their ' | 
				
			||||
 | 
					                   'experiences.' | 
				
			||||
 | 
					                   'Explore these new question types and optimize your survey ' | 
				
			||||
 | 
					                   'strategy for enhanced insights.""", | 
				
			||||
 | 
					    'author': 'Cybrosys Techno Solutions', | 
				
			||||
 | 
					    'company': 'Cybrosys Techno Solutions', | 
				
			||||
 | 
					    'maintainer': 'Cybrosys Techno Solutions', | 
				
			||||
 | 
					    'website': "https://www.cybrosys.com", | 
				
			||||
 | 
					    'depends': ['base', 'survey', 'website'], | 
				
			||||
 | 
					    'data': [ | 
				
			||||
 | 
					        'data/enhanced_survey_management_data.xml', | 
				
			||||
 | 
					        'security/ir.model.access.csv', | 
				
			||||
 | 
					        'views/survey_templates.xml', | 
				
			||||
 | 
					        'views/survey_question_views.xml', | 
				
			||||
 | 
					        'views/survey_input_print_templates.xml', | 
				
			||||
 | 
					        'views/survey_portal_templates.xml', | 
				
			||||
 | 
					        'views/survey_user_views.xml', | 
				
			||||
 | 
					        'views/survey_survey_views.xml' | 
				
			||||
 | 
					    ], | 
				
			||||
 | 
					    'assets': { | 
				
			||||
 | 
					        'survey.survey_assets': [ | 
				
			||||
 | 
					            'https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css', | 
				
			||||
 | 
					            'https://cdn.jsdelivr.net/npm/flatpickr@4.6.3/dist/flatpickr.min.js', | 
				
			||||
 | 
					            'enhanced_survey_management/static/src/js/survey_form.js', | 
				
			||||
 | 
					            'enhanced_survey_management/static/src/js/survey_submit.js', | 
				
			||||
 | 
					        ], | 
				
			||||
 | 
					    }, | 
				
			||||
 | 
					    'images': [ | 
				
			||||
 | 
					        'static/description/banner.jpg', | 
				
			||||
 | 
					    ], | 
				
			||||
 | 
					    'license': 'LGPL-3', | 
				
			||||
 | 
					    'installable': True, | 
				
			||||
 | 
					    'auto_install': False, | 
				
			||||
 | 
					    'application': False, | 
				
			||||
 | 
					} | 
				
			||||
@ -0,0 +1,24 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from . import enhanced_survey_management | 
				
			||||
 | 
					from . import website_visibility | 
				
			||||
 | 
					from . import xlsx_main | 
				
			||||
@ -0,0 +1,121 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from odoo.addons.portal.controllers.portal import CustomerPortal | 
				
			||||
 | 
					from odoo import http | 
				
			||||
 | 
					from odoo.http import request | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyAnswerPortal(CustomerPortal): | 
				
			||||
 | 
					    """ Inherited CustomerPortal to add new portal view""" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def _prepare_home_portal_values(self, counters): | 
				
			||||
 | 
					        """ Function : Prepare portal values for attended survey count | 
				
			||||
 | 
					            @return : Survey counts | 
				
			||||
 | 
					        """ | 
				
			||||
 | 
					        values = super(SurveyAnswerPortal, self)._prepare_home_portal_values( | 
				
			||||
 | 
					            counters) | 
				
			||||
 | 
					        if 'survey_count' in counters: | 
				
			||||
 | 
					            values['survey_count'] = request.env[ | 
				
			||||
 | 
					                'survey.survey'].sudo().search_count( | 
				
			||||
 | 
					                [('user_id', '=', request.uid), ('visibility', '=', True)]) | 
				
			||||
 | 
					        return values | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyPortalView(http.Controller): | 
				
			||||
 | 
					    """ Survey portal controller """ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @http.route('/my/survey/ans', type='http', auth="public", | 
				
			||||
 | 
					                website=True) | 
				
			||||
 | 
					    def portal_my_survey(self): | 
				
			||||
 | 
					        """ Controller to send survey data to portal """ | 
				
			||||
 | 
					        survey = request.env['survey.survey'].sudo().search( | 
				
			||||
 | 
					            [('user_id', '=', request.uid)]) | 
				
			||||
 | 
					        values = { | 
				
			||||
 | 
					            'survey_list': [{'id': rec.id, | 
				
			||||
 | 
					                             'survey': rec.title, | 
				
			||||
 | 
					                             'user_id': rec.user_id.name, | 
				
			||||
 | 
					                             'email': rec.user_id.partner_id.email, | 
				
			||||
 | 
					                             'status': rec.session_state | 
				
			||||
 | 
					                             } for rec in survey if rec.visibility], | 
				
			||||
 | 
					            'page_name': 'survey' | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					        return request.render( | 
				
			||||
 | 
					            """enhanced_survey_management.portal_survey_result""", values) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @http.route('/my/survey/<int:survey_id>', type="http", auth="public", | 
				
			||||
 | 
					                website=True) | 
				
			||||
 | 
					    def survey_view(self, survey_id): | 
				
			||||
 | 
					        """ Controller function to send specific survey data """ | 
				
			||||
 | 
					        question_list = [] | 
				
			||||
 | 
					        survey_questions_line = request.env[ | 
				
			||||
 | 
					            'survey.user_input.line'].sudo().search([ | 
				
			||||
 | 
					            ('survey_id', '=', survey_id)]) | 
				
			||||
 | 
					        survey = request.env['survey.survey'].sudo().browse(survey_id) | 
				
			||||
 | 
					        for res in survey_questions_line: | 
				
			||||
 | 
					            question_list.append({ | 
				
			||||
 | 
					                'id': res.question_id.id, | 
				
			||||
 | 
					                'questions': res.question_id.title, | 
				
			||||
 | 
					                'section': res.create_date, | 
				
			||||
 | 
					                'question_type': res.question_id.question_type, | 
				
			||||
 | 
					                'score': res.answer_score, | 
				
			||||
 | 
					                'answer': res.display_name, | 
				
			||||
 | 
					            }) | 
				
			||||
 | 
					        values = { | 
				
			||||
 | 
					            'survey_questions': question_list, | 
				
			||||
 | 
					            'survey': survey.title, | 
				
			||||
 | 
					            'survey_date': survey.create_date, | 
				
			||||
 | 
					            'name': survey.user_id.partner_id.name, | 
				
			||||
 | 
					            'email': survey.user_id.partner_id.email, | 
				
			||||
 | 
					            'access_token': survey.access_token, | 
				
			||||
 | 
					            'page_name': 'survey', | 
				
			||||
 | 
					            'survey_boolean': True | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					        return request.render( | 
				
			||||
 | 
					            "enhanced_survey_management.portal_survey_result_view", values) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyLoadContent(http.Controller): | 
				
			||||
 | 
					    """Controller to load country and state to the survey""" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @http.route('/survey/load_country', type="json", auth="public", | 
				
			||||
 | 
					                website=True, csrf=False) | 
				
			||||
 | 
					    def load_country(self): | 
				
			||||
 | 
					        """Function to return country names for question type country""" | 
				
			||||
 | 
					        country_ids = request.env['res.country'].sudo().search([]) | 
				
			||||
 | 
					        return { | 
				
			||||
 | 
					            'id': country_ids.mapped('id'), | 
				
			||||
 | 
					            'name': country_ids.mapped('name'), | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @http.route('/survey/load_states', type="json", auth="public", | 
				
			||||
 | 
					                website=True, | 
				
			||||
 | 
					                csrf=False) | 
				
			||||
 | 
					    def load_states(self, **kwargs): | 
				
			||||
 | 
					        """Function to return state names based on country""" | 
				
			||||
 | 
					        country_id = request.env['res.country'].sudo().search([ | 
				
			||||
 | 
					            ('name', '=', kwargs['params']['country_id'])]) | 
				
			||||
 | 
					        state_ids = request.env['res.country.state'].sudo().search([ | 
				
			||||
 | 
					            ('country_id', '=', country_id.id)]) | 
				
			||||
 | 
					        return { | 
				
			||||
 | 
					            'id': state_ids.mapped('id'), | 
				
			||||
 | 
					            'name': state_ids.mapped('name'), | 
				
			||||
 | 
					        } | 
				
			||||
@ -0,0 +1,44 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from odoo import http | 
				
			||||
 | 
					from odoo.http import request | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class WebsiteView(http.Controller): | 
				
			||||
 | 
					    """Inherited http.Controller to add custom route""" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @http.route('/public/survey', type='http', auth="public", | 
				
			||||
 | 
					                website=True) | 
				
			||||
 | 
					    def public_user_access(self): | 
				
			||||
 | 
					        """ Controller function to access survey from website """ | 
				
			||||
 | 
					        survey = request.env['survey.survey'].sudo().search( | 
				
			||||
 | 
					            [('access_mode', '=', 'website')]) | 
				
			||||
 | 
					        values = { | 
				
			||||
 | 
					            'survey_list': [{ | 
				
			||||
 | 
					                'title': rec.title, | 
				
			||||
 | 
					                'attempts': rec.attempts_limit, | 
				
			||||
 | 
					                'date': rec.create_date, | 
				
			||||
 | 
					                'access_token': rec.access_token | 
				
			||||
 | 
					            } for rec in survey], | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					        return request.render("enhanced_survey_management.survey_visibility", | 
				
			||||
 | 
					                              values) | 
				
			||||
@ -0,0 +1,126 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					import io | 
				
			||||
 | 
					import json | 
				
			||||
 | 
					from datetime import datetime | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					from odoo import http | 
				
			||||
 | 
					from odoo.http import content_disposition, request | 
				
			||||
 | 
					from odoo.tools.misc import xlsxwriter | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class XlsxReportController(http.Controller): | 
				
			||||
 | 
					    """Inherited http.Controller to add custom route""" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @http.route('/xlsx_report/<model("survey.survey"):survey_id>', | 
				
			||||
 | 
					                type='http', auth='user', csrf=False) | 
				
			||||
 | 
					    def get_report_xlsx(self, survey_id=None, **args): | 
				
			||||
 | 
					        """ Function to create and print XLSX report""" | 
				
			||||
 | 
					        user_input_ids = request.env['survey.user_input'].search( | 
				
			||||
 | 
					            [("survey_id", '=', survey_id.id)]) | 
				
			||||
 | 
					        answers, title = [], '' | 
				
			||||
 | 
					        for rec in user_input_ids: | 
				
			||||
 | 
					            for res in rec: | 
				
			||||
 | 
					                answer='' | 
				
			||||
 | 
					                name = '' | 
				
			||||
 | 
					                for new in res.user_input_line_ids: | 
				
			||||
 | 
					                    print(new.question_id) | 
				
			||||
 | 
					                    if new.question_id.question_type in ['barcode', 'qr']: | 
				
			||||
 | 
					                        continue | 
				
			||||
 | 
					                    new_dt = str(rec.create_date).split('.') | 
				
			||||
 | 
					                    if new.question_id.question_type == 'address': | 
				
			||||
 | 
					                        data=json.loads(new.display_name) | 
				
			||||
 | 
					                        for key in data: | 
				
			||||
 | 
					                            if data[key]: | 
				
			||||
 | 
					                                answer+=f'{data[key]},' | 
				
			||||
 | 
					                        answers.append([rec.id, rec.nickname, new_dt[0], | 
				
			||||
 | 
					                                        new.question_id.title, answer]) | 
				
			||||
 | 
					                    elif new.question_id.question_type == 'name': | 
				
			||||
 | 
					                        data = json.loads(new.display_name) | 
				
			||||
 | 
					                        for key in data: | 
				
			||||
 | 
					                            if data[key]: | 
				
			||||
 | 
					                                name += f'{data[key]} ' | 
				
			||||
 | 
					                        answers.append([rec.id, rec.nickname, new_dt[0], | 
				
			||||
 | 
					                                        new.question_id.title, name]) | 
				
			||||
 | 
					                    elif new.question_id.question_type == 'time': | 
				
			||||
 | 
					                        # Convert time to 12-hour format | 
				
			||||
 | 
					                        time_str = new.display_name | 
				
			||||
 | 
					                        time_parts = time_str.split('.') | 
				
			||||
 | 
					                        hours = int(time_parts[0]) | 
				
			||||
 | 
					                        minutes = int(time_parts[1]) if len( | 
				
			||||
 | 
					                                time_parts) > 1 else 0 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					                            # Handle 24-hour to 12-hour conversion | 
				
			||||
 | 
					                        time_obj = datetime.strptime(f'{hours}:{minutes}', | 
				
			||||
 | 
					                                                         '%H:%M') | 
				
			||||
 | 
					                        time_12hr = time_obj.strftime('%I:%M %p') | 
				
			||||
 | 
					                        answers.append([rec.id, rec.nickname, new_dt[0], | 
				
			||||
 | 
					                                        new.question_id.title, time_12hr]) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					                    else: | 
				
			||||
 | 
					                        answers.append([rec.id, rec.nickname, new_dt[0], | 
				
			||||
 | 
					                                    new.question_id.title, new.display_name]) | 
				
			||||
 | 
					        answers.reverse() | 
				
			||||
 | 
					        response = request.make_response( | 
				
			||||
 | 
					            None, | 
				
			||||
 | 
					            headers=[ | 
				
			||||
 | 
					                ('Content-Type', 'application/vnd.ms-excel'), | 
				
			||||
 | 
					                ('Content-Disposition', | 
				
			||||
 | 
					                 content_disposition(survey_id.title + '.xlsx')) | 
				
			||||
 | 
					            ] | 
				
			||||
 | 
					        ) | 
				
			||||
 | 
					        output = io.BytesIO() | 
				
			||||
 | 
					        workbook = xlsxwriter.Workbook(output, {'in_memory': True}) | 
				
			||||
 | 
					        sheet = workbook.add_worksheet() | 
				
			||||
 | 
					        sheet.set_column(1, 0, 25) | 
				
			||||
 | 
					        sheet.set_column(2, 0, 25) | 
				
			||||
 | 
					        sheet.set_column(3, 3, 50) | 
				
			||||
 | 
					        cell_format = workbook.add_format( | 
				
			||||
 | 
					            {'font_size': '12px', 'align': 'center'}) | 
				
			||||
 | 
					        head = workbook.add_format( | 
				
			||||
 | 
					            {'align': 'center', 'bold': True, 'font_size': '20px'}) | 
				
			||||
 | 
					        sub_title = workbook.add_format( | 
				
			||||
 | 
					            {'align': 'right', 'bold': True, 'font_size': '12px'}) | 
				
			||||
 | 
					        sub_title_content = workbook.add_format( | 
				
			||||
 | 
					            {'align': 'left', 'bold': True, 'font_size': '12px'}) | 
				
			||||
 | 
					        txt = workbook.add_format({'font_size': '10px'}) | 
				
			||||
 | 
					        sheet.merge_range('A2:D3', 'SURVEY MANAGEMENT REPORT', head) | 
				
			||||
 | 
					        sheet.write('A4', 'Title :', sub_title) | 
				
			||||
 | 
					        sheet.merge_range('B4:C4', survey_id.title, sub_title_content) | 
				
			||||
 | 
					        sheet.merge_range('A6:A7', 'Partner', cell_format) | 
				
			||||
 | 
					        sheet.merge_range('B6:B7', 'Submission Date & Time', cell_format) | 
				
			||||
 | 
					        sheet.merge_range('C6:C7', 'Question', cell_format) | 
				
			||||
 | 
					        sheet.merge_range('D6:D7', 'Answer', cell_format) | 
				
			||||
 | 
					        row = 7 | 
				
			||||
 | 
					        column = 0 | 
				
			||||
 | 
					        for rec in answers: | 
				
			||||
 | 
					            sheet.write(row, column, rec[1], txt) | 
				
			||||
 | 
					            sheet.write(row, column + 1, rec[2], txt) | 
				
			||||
 | 
					            sheet.write(row, column + 2, rec[3], txt) | 
				
			||||
 | 
					            sheet.write(row, column + 3, rec[4], txt) | 
				
			||||
 | 
					            row = row + 1 | 
				
			||||
 | 
					        workbook.close() | 
				
			||||
 | 
					        output.seek(0) | 
				
			||||
 | 
					        response.stream.write(output.read()) | 
				
			||||
 | 
					        output.close() | 
				
			||||
 | 
					        return response | 
				
			||||
@ -0,0 +1,10 @@ | 
				
			|||||
 | 
					<?xml version="1.0" encoding="UTF-8" ?> | 
				
			||||
 | 
					<odoo noupdate="1"> | 
				
			||||
 | 
					    <!-- Survey Website menu data --> | 
				
			||||
 | 
					    <record id="menu_survey" model="website.menu"> | 
				
			||||
 | 
					        <field name="name">Survey</field> | 
				
			||||
 | 
					        <field name="url">/public/survey</field> | 
				
			||||
 | 
					        <field name="parent_id" ref="website.main_menu"/> | 
				
			||||
 | 
					        <field name="sequence" type="int">80</field> | 
				
			||||
 | 
					    </record> | 
				
			||||
 | 
					</odoo> | 
				
			||||
@ -0,0 +1,6 @@ | 
				
			|||||
 | 
					## Module <enhanced_survey_management> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#### 17.05.2024 | 
				
			||||
 | 
					#### Version 17.0.1.0.0 | 
				
			||||
 | 
					#### ADD | 
				
			||||
 | 
					 - Initial Commit for Enhanced Survey Management | 
				
			||||
@ -0,0 +1,27 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from . import question_selection | 
				
			||||
 | 
					from . import survey_question | 
				
			||||
 | 
					from . import survey_question_answer | 
				
			||||
 | 
					from . import survey_survey | 
				
			||||
 | 
					from . import survey_user_input | 
				
			||||
 | 
					from . import survey_user_input_line | 
				
			||||
@ -0,0 +1,34 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from odoo import fields, models | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class QuestionSelection(models.Model): | 
				
			||||
 | 
					    """Model to store options for selection type question""" | 
				
			||||
 | 
					    _name = 'question.selection' | 
				
			||||
 | 
					    _description = 'Selection Question' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    name = fields.Char(string='Name', help="Selection value.") | 
				
			||||
 | 
					    question_id = fields.Many2one('survey.question', | 
				
			||||
 | 
					                                  string="Question", | 
				
			||||
 | 
					                                  help="Field to store " | 
				
			||||
 | 
					                                       "question id in selection type.") | 
				
			||||
@ -0,0 +1,94 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from odoo import api, fields, models, _ | 
				
			||||
 | 
					from odoo.exceptions import ValidationError | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyQuestion(models.Model): | 
				
			||||
 | 
					    """ Inherited survey.question to add custom question type""" | 
				
			||||
 | 
					    _inherit = 'survey.question' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    selection_ids = fields.One2many('question.selection', | 
				
			||||
 | 
					                                    'question_id', | 
				
			||||
 | 
					                                    string='Select', | 
				
			||||
 | 
					                                    help="""Field used to create selection type | 
				
			||||
 | 
					                                     questions""") | 
				
			||||
 | 
					    question_type = fields.Selection(selection_add=[ | 
				
			||||
 | 
					        ('time', 'Time'), | 
				
			||||
 | 
					        ('month', 'Month'), | 
				
			||||
 | 
					        ('name', 'Name'), | 
				
			||||
 | 
					        ('address', 'Address'), | 
				
			||||
 | 
					        ('email', 'Email'), | 
				
			||||
 | 
					        ('password', 'Password'), | 
				
			||||
 | 
					        ('qr', 'Qrcode'), | 
				
			||||
 | 
					        ('url', 'URL'), | 
				
			||||
 | 
					        ('week', 'Week'), | 
				
			||||
 | 
					        ('color', 'Color'), | 
				
			||||
 | 
					        ('range', 'Range'), | 
				
			||||
 | 
					        ('many2one', 'Many2one'), | 
				
			||||
 | 
					        ('file', 'Upload File'), | 
				
			||||
 | 
					        ('many2many', 'Many2many'), | 
				
			||||
 | 
					        ('selection', 'Selection'), | 
				
			||||
 | 
					        ('barcode', 'Barcode') | 
				
			||||
 | 
					    ], help="Survey supported question types") | 
				
			||||
 | 
					    matrix_subtype = fields.Selection( | 
				
			||||
 | 
					        selection_add=[('custom', 'Custom Matrix')], | 
				
			||||
 | 
					        help="""Matrix selection type question""") | 
				
			||||
 | 
					    model_id = fields.Many2one('ir.model', string='Model', | 
				
			||||
 | 
					                               domain=[('transient', '=', False)], | 
				
			||||
 | 
					                               help="Many2one type question") | 
				
			||||
 | 
					    range_min = fields.Integer(string='Min', | 
				
			||||
 | 
					                               help='Minimum range,range type question') | 
				
			||||
 | 
					    range_max = fields.Integer(string='Max', | 
				
			||||
 | 
					                               help='Maximum range,range type question') | 
				
			||||
 | 
					    qrcode = fields.Text(string='Qrcode', | 
				
			||||
 | 
					                         help='Show qrcode in survey') | 
				
			||||
 | 
					    qrcode_png = fields.Binary(string='Qrcode PNG', help="Qrcode PNG") | 
				
			||||
 | 
					    barcode = fields.Char(string='Barcode', help="Barcode number") | 
				
			||||
 | 
					    barcode_png = fields.Binary(string='Barcode PNG', | 
				
			||||
 | 
					                                help="Saves barcode png file") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @api.onchange('barcode') | 
				
			||||
 | 
					    def _onchange_barcode(self): | 
				
			||||
 | 
					        """Onchange function to restrict barcode less-than 12 character """ | 
				
			||||
 | 
					        if self.barcode: | 
				
			||||
 | 
					            if len(self.barcode) < 12: | 
				
			||||
 | 
					                raise ValidationError( | 
				
			||||
 | 
					                    _("Make sure barcode is at least 12 letters")) | 
				
			||||
 | 
					            if len(self.barcode) > 12: | 
				
			||||
 | 
					                raise ValidationError( | 
				
			||||
 | 
					                    _("There should not be more than 12 " | 
				
			||||
 | 
					                      "characters in a barcode")) | 
				
			||||
 | 
					            if not self.barcode.isdigit(): | 
				
			||||
 | 
					                raise ValidationError(_("Only digits are allowed.")) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def get_selection_values(self): | 
				
			||||
 | 
					        """Function return options for selection type question""" | 
				
			||||
 | 
					        return self.selection_ids | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def prepare_model_id(self, model): | 
				
			||||
 | 
					        """Function to return options for many2one question""" | 
				
			||||
 | 
					        if model: | 
				
			||||
 | 
					            model_data = self.env[model.model].sudo().search([]) | 
				
			||||
 | 
					            return [rec.read([model_data._rec_name])[0] for rec in model_data] | 
				
			||||
 | 
					        model_data = self.env[self.model_id.model].sudo().search([]) | 
				
			||||
 | 
					        return [rec.read([model_data._rec_name])[0] for rec in model_data] | 
				
			||||
@ -0,0 +1,46 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from odoo import fields, models | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyQuestionAnswer(models.Model): | 
				
			||||
 | 
					    """Inherit question answer model to add new answer type and model field""" | 
				
			||||
 | 
					    _inherit = 'survey.question.answer' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    answer_type = fields.Selection([ | 
				
			||||
 | 
					        ('text_box', 'Multiple Lines Text Box'), | 
				
			||||
 | 
					        ('char_box', 'Single Line Text Box'), | 
				
			||||
 | 
					        ('numerical_box', 'Numerical Value'), | 
				
			||||
 | 
					        ('time', 'Time'), | 
				
			||||
 | 
					        ('email', 'Email'), | 
				
			||||
 | 
					        ('password', 'Password'), | 
				
			||||
 | 
					        ('range', 'Range'), | 
				
			||||
 | 
					        ('month', 'Month'), | 
				
			||||
 | 
					        ('url', 'URL'), | 
				
			||||
 | 
					        ('week', 'Week'), | 
				
			||||
 | 
					        ('color', 'Color'), | 
				
			||||
 | 
					        ('many2one', 'Many2one')], help="Answer type", | 
				
			||||
 | 
					        string='Answer Type', readonly=False, store=True) | 
				
			||||
 | 
					    model_id = fields.Many2one('ir.model', string='Model', | 
				
			||||
 | 
					                               domain=[('transient', '=', False)], | 
				
			||||
 | 
					                               help="Select model for getting it's" | 
				
			||||
 | 
					                                    " values in survey") | 
				
			||||
@ -0,0 +1,39 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP  (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					from odoo import fields, models | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class Survey(models.Model): | 
				
			||||
 | 
					    """Inherited model to change survey visibility""" | 
				
			||||
 | 
					    _inherit = 'survey.survey' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    access_mode = fields.Selection(selection_add=[ | 
				
			||||
 | 
					        ('website', 'website')], ondelete={'website': 'cascade'}) | 
				
			||||
 | 
					    visibility = fields.Boolean(string='Portal Visibility', | 
				
			||||
 | 
					                                help="""Portal visibility of this survey""") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def action_answer_report_download(self): | 
				
			||||
 | 
					        """Function to generate report values""" | 
				
			||||
 | 
					        return { | 
				
			||||
 | 
					            'type': 'ir.actions.act_url', | 
				
			||||
 | 
					            'url': f'/xlsx_report/{self.id}' | 
				
			||||
 | 
					        } | 
				
			||||
@ -0,0 +1,136 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Mohammed Savad, Ahammed Harshad (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					import json | 
				
			||||
 | 
					from odoo import models | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyUserInput(models.Model): | 
				
			||||
 | 
					    """Inherited user input to make acceptable for custom question""" | 
				
			||||
 | 
					    _inherit = "survey.user_input" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def _save_lines(self, question, answer, comment=None, overwrite_existing=True): | 
				
			||||
 | 
					        """Function to save custom answers""" | 
				
			||||
 | 
					        old_answers = self.env['survey.user_input.line'].search([ | 
				
			||||
 | 
					            ('user_input_id', '=', self.id), | 
				
			||||
 | 
					            ('question_id', '=', question.id), ]) | 
				
			||||
 | 
					        print('answer',answer) | 
				
			||||
 | 
					        if question.question_type in ['password', 'range', 'time', 'url', | 
				
			||||
 | 
					                                      'email', 'range', 'many2many', | 
				
			||||
 | 
					                                      'file', 'many2one', 'week', 'color', | 
				
			||||
 | 
					                                      'month', 'address', 'name', 'selection', | 
				
			||||
 | 
					                                      'file', 'qr', 'barcode', 'signature']: | 
				
			||||
 | 
					            res = self._save_user_answers(question, old_answers, answer) | 
				
			||||
 | 
					        elif question.matrix_subtype == 'custom': | 
				
			||||
 | 
					            res = self._save_user_answers(question, old_answers, answer) | 
				
			||||
 | 
					        else: | 
				
			||||
 | 
					            res = super()._save_lines(question, answer, comment) | 
				
			||||
 | 
					        return res | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def _save_user_answers(self, question, user_input_line, answer): | 
				
			||||
 | 
					        """Function to save custom answers""" | 
				
			||||
 | 
					        vals = (self._get_user_answers( | 
				
			||||
 | 
					            question, answer, question.question_type)) | 
				
			||||
 | 
					        if user_input_line: | 
				
			||||
 | 
					            user_input_line.write(vals) | 
				
			||||
 | 
					        else: | 
				
			||||
 | 
					            user_input_line = self.env['survey.user_input.line'].create(vals) | 
				
			||||
 | 
					        return user_input_line | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def _get_user_answers(self, question, answer, answer_type): | 
				
			||||
 | 
					        """Function to save custom answers""" | 
				
			||||
 | 
					        vals = {'user_input_id': self.id, 'question_id': question.id, | 
				
			||||
 | 
					                'skipped': False, 'answer_type': answer_type, | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					        if not answer or (isinstance(answer, str) and not answer.strip()): | 
				
			||||
 | 
					            vals.update(answer_type=None, skipped=True) | 
				
			||||
 | 
					            return vals | 
				
			||||
 | 
					        if question.question_type == 'time': | 
				
			||||
 | 
					            vals['value_time'] = float(answer.replace(":", ".")) | 
				
			||||
 | 
					        elif question.question_type == 'url': | 
				
			||||
 | 
					            vals['value_url'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'password': | 
				
			||||
 | 
					            vals['value_password'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'email': | 
				
			||||
 | 
					            vals['value_email'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'range': | 
				
			||||
 | 
					            vals['value_range'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'many2one': | 
				
			||||
 | 
					            vals['value_many2one'] = answer[0] | 
				
			||||
 | 
					            vals['value_many2one_option'] = answer[1] | 
				
			||||
 | 
					        elif question.question_type == 'many2many': | 
				
			||||
 | 
					            vals['value_many2many'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'week': | 
				
			||||
 | 
					            vals['value_week'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'color': | 
				
			||||
 | 
					            vals['value_color'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'month': | 
				
			||||
 | 
					            vals['value_month'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'matrix' \ | 
				
			||||
 | 
					                and question.matrix_subtype == 'custom': | 
				
			||||
 | 
					            vals['value_matrix'] = json.dumps(answer) | 
				
			||||
 | 
					        elif question.question_type == 'address': | 
				
			||||
 | 
					            vals['value_address'] = json.dumps(answer) | 
				
			||||
 | 
					        elif question.question_type == 'qr': | 
				
			||||
 | 
					            vals['value_qr'] = json.dumps(answer) | 
				
			||||
 | 
					        elif question.question_type == 'barcode': | 
				
			||||
 | 
					            vals['value_barcode'] = json.dumps(answer) | 
				
			||||
 | 
					        elif question.question_type == 'name': | 
				
			||||
 | 
					            vals['value_name'] = json.dumps(answer) | 
				
			||||
 | 
					        elif question.question_type == 'selection': | 
				
			||||
 | 
					            vals['value_selection'] = answer | 
				
			||||
 | 
					        elif question.question_type == 'file': | 
				
			||||
 | 
					            attachment = self.env['ir.attachment'].create({ | 
				
			||||
 | 
					                'name': str(answer[1]), 'datas': answer[0], 'type': 'binary' | 
				
			||||
 | 
					            }) | 
				
			||||
 | 
					            vals['value_file'] = int(attachment.id if attachment else False) | 
				
			||||
 | 
					            vals['filename'] = attachment.name if attachment else False | 
				
			||||
 | 
					        return vals | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def _save_line_matrix(self, question, old_answers, answers, comment): | 
				
			||||
 | 
					        """Function to save custom matrix""" | 
				
			||||
 | 
					        if question.matrix_subtype == 'custom': | 
				
			||||
 | 
					            self._save_line(question, answers) | 
				
			||||
 | 
					        else: | 
				
			||||
 | 
					            vals_list = [] | 
				
			||||
 | 
					            if not answers and question.matrix_row_ids: | 
				
			||||
 | 
					                # add a False answer to force saving a skipped line | 
				
			||||
 | 
					                # this will make this question correctly considered as | 
				
			||||
 | 
					                # skipped in statistics | 
				
			||||
 | 
					                answers = {question.matrix_row_ids[0].id: [False]} | 
				
			||||
 | 
					            if answers: | 
				
			||||
 | 
					                for row_key, row_answer in answers.items(): | 
				
			||||
 | 
					                    for answer in row_answer: | 
				
			||||
 | 
					                        vals = self._get_line_answer_values(question, answer, | 
				
			||||
 | 
					                                                            'suggestion') | 
				
			||||
 | 
					                        vals['matrix_row_id'] = int(row_key) | 
				
			||||
 | 
					                        vals_list.append(vals.copy()) | 
				
			||||
 | 
					            if comment: | 
				
			||||
 | 
					                vals_list.append( | 
				
			||||
 | 
					                    self._get_line_comment_values(question, comment)) | 
				
			||||
 | 
					            old_answers.sudo().unlink() | 
				
			||||
 | 
					            return self.env['survey.user_input.line'].create(vals_list) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def _save_line_selection_answer(self, question, answer): | 
				
			||||
 | 
					        """Function to save selection type answers""" | 
				
			||||
 | 
					        vals = self._get_line_answer_values(question, answer, | 
				
			||||
 | 
					                                            question.question_type) | 
				
			||||
 | 
					        return self.env['survey.user_input.line'].create(vals) | 
				
			||||
@ -0,0 +1,189 @@ | 
				
			|||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Cybrosys Technologies Pvt. Ltd. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | 
				
			||||
 | 
					#    Author: Nihala KP (odoo@cybrosys.com) | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    This program is under the terms of the Odoo Proprietary License v1.0(OPL-1) | 
				
			||||
 | 
					#    It is forbidden to publish, distribute, sublicense, or sell copies of the | 
				
			||||
 | 
					#    Software or modified copies of the Software. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					#    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||
 | 
					#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||
 | 
					#    FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | 
				
			||||
 | 
					#    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,DAMAGES OR OTHER | 
				
			||||
 | 
					#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING | 
				
			||||
 | 
					#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
				
			||||
 | 
					#    DEALINGS IN THE SOFTWARE. | 
				
			||||
 | 
					# | 
				
			||||
 | 
					############################################################################### | 
				
			||||
 | 
					import json | 
				
			||||
 | 
					import textwrap | 
				
			||||
 | 
					from datetime import datetime | 
				
			||||
 | 
					from odoo import api, fields, models, _ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class SurveyUserInputLine(models.Model): | 
				
			||||
 | 
					    """Inherited user input line to add custom answer types """ | 
				
			||||
 | 
					    _inherit = 'survey.user_input.line' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    answer_type = fields.Selection( | 
				
			||||
 | 
					        selection_add=[('url', 'URL'), | 
				
			||||
 | 
					                       ('many2one', 'Many2one'), | 
				
			||||
 | 
					                       ('many2many', 'Many2many'), | 
				
			||||
 | 
					                       ('week', 'Week'), | 
				
			||||
 | 
					                       ('time', 'Time'), | 
				
			||||
 | 
					                       ('color', 'Color'), | 
				
			||||
 | 
					                       ('email', 'Email'), | 
				
			||||
 | 
					                       ('month', 'Month'), | 
				
			||||
 | 
					                       ('name', 'Name'), | 
				
			||||
 | 
					                       ('matrix', 'Matrix'), | 
				
			||||
 | 
					                       ('address', 'Address'), | 
				
			||||
 | 
					                       ('selection', 'Selection'), | 
				
			||||
 | 
					                       ('time', 'Time'), | 
				
			||||
 | 
					                       ('password', 'Password'), | 
				
			||||
 | 
					                       ('email', 'Email'), | 
				
			||||
 | 
					                       ('range', 'Range'), | 
				
			||||
 | 
					                       ('file', 'File'), | 
				
			||||
 | 
					                       ('qr', 'Qrcode'), | 
				
			||||
 | 
					                       ('barcode', 'Barcode')], help="Custom answer types") | 
				
			||||
 | 
					    value_url = fields.Char(string='User URL', | 
				
			||||
 | 
					                            help='Answer question type : URL') | 
				
			||||
 | 
					    value_email = fields.Char(string='User Email', | 
				
			||||
 | 
					                              help="Answer question type : Email") | 
				
			||||
 | 
					    value_week = fields.Char(string='User Week', | 
				
			||||
 | 
					                             help="Answer question type : Week") | 
				
			||||
 | 
					    value_color = fields.Char(string='User Color', | 
				
			||||
 | 
					                              help="Answer question type : Color") | 
				
			||||
 | 
					    value_many2one = fields.Char(string='Survey Many2one', | 
				
			||||
 | 
					                                 help="Answer question type : Many2one") | 
				
			||||
 | 
					    value_many2one_option = fields.Char(string='Many2one selected', | 
				
			||||
 | 
					                                        help="Answer question type : Selected") | 
				
			||||
 | 
					    value_many2many = fields.Char(string='Survey Many2many', | 
				
			||||
 | 
					                                  help="Answer question type : Many2many") | 
				
			||||
 | 
					    value_time = fields.Float(string='Time Value', | 
				
			||||
 | 
					                              help="Answer question type : Time") | 
				
			||||
 | 
					    value_matrix = fields.Text(string='Custom Matrix Values', | 
				
			||||
 | 
					                               help="Answer question type : Custom Matrix") | 
				
			||||
 | 
					    value_selection = fields.Char(string='User Selection', | 
				
			||||
 | 
					                                  help="Answer question type : Selection") | 
				
			||||
 | 
					    value_password = fields.Char(string='Password Value', | 
				
			||||
 | 
					                                 help="Answer question type : Password") | 
				
			||||
 | 
					    value_range = fields.Char(string='Range Value', | 
				
			||||
 | 
					                              help="Answer question type:Range") | 
				
			||||
 | 
					    value_file = fields.Many2one('ir.attachment', | 
				
			||||
 | 
					                                 string='Survey file', | 
				
			||||
 | 
					                                 help="Answer question type : Attachment") | 
				
			||||
 | 
					    filename = fields.Char(string='File', help="Attachment file name") | 
				
			||||
 | 
					    file_data = fields.Binary(string='File Data', | 
				
			||||
 | 
					                              help="Attachment file data", | 
				
			||||
 | 
					                              related="value_file.datas") | 
				
			||||
 | 
					    value_month = fields.Char(string='Month Value', | 
				
			||||
 | 
					                              help="Answer question type : Month") | 
				
			||||
 | 
					    value_address = fields.Text(string='Address Value', | 
				
			||||
 | 
					                                help="Answer question type : Address") | 
				
			||||
 | 
					    value_name = fields.Text(string='Name Values', | 
				
			||||
 | 
					                             help="Answer question type : Full name") | 
				
			||||
 | 
					    value_qr = fields.Char(string='Qr Values', | 
				
			||||
 | 
					                           help="Answer question type : QRcode") | 
				
			||||
 | 
					    value_barcode = fields.Char(string='Barcode Values', | 
				
			||||
 | 
					                                help="Answer question type : Barcode") | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def get_value_time(self): | 
				
			||||
 | 
					        """Function to return answer for question type time """ | 
				
			||||
 | 
					        if self.value_time: | 
				
			||||
 | 
					            return datetime.strptime(str(self.value_time).replace('.', ':'), | 
				
			||||
 | 
					                                     "%H:%M").strftime("%H:%M") | 
				
			||||
 | 
					        return None | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def get_value_address(self, field): | 
				
			||||
 | 
					        """Function to return answer for question type address""" | 
				
			||||
 | 
					        data = json.loads(self[0].value_address.replace("'", "\"")) | 
				
			||||
 | 
					        print("data",data) | 
				
			||||
 | 
					        if data: | 
				
			||||
 | 
					            question_id = self[0].question_id.id | 
				
			||||
 | 
					            return data.get(f'{question_id}-{field}', | 
				
			||||
 | 
					                            '') | 
				
			||||
 | 
					        return '' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def get_value_matrix(self, item): | 
				
			||||
 | 
					        """Function to return answer for custom matrix""" | 
				
			||||
 | 
					        data = json.loads(self.value_matrix) | 
				
			||||
 | 
					        if data: | 
				
			||||
 | 
					            question_id = self.question_id.id | 
				
			||||
 | 
					            return data[f'{item}-{question_id}'] | 
				
			||||
 | 
					        return None | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    def get_value_name(self, field): | 
				
			||||
 | 
					        """Function to return answer for Full name""" | 
				
			||||
 | 
					        data = json.loads(self.value_name) | 
				
			||||
 | 
					        if data: | 
				
			||||
 | 
					            question_id = self.question_id.id | 
				
			||||
 | 
					            return data[f'{question_id}-{field}'] | 
				
			||||
 | 
					        return '' | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    @api.depends('answer_type') | 
				
			||||
 | 
					    def _compute_display_name(self): | 
				
			||||
 | 
					        """Override compute function to add custom answer display""" | 
				
			||||
 | 
					        for line in self: | 
				
			||||
 | 
					            if line.answer_type == 'char_box': | 
				
			||||
 | 
					                line.display_name = line.value_char_box | 
				
			||||
 | 
					            elif line.answer_type == 'text_box' and line.value_text_box: | 
				
			||||
 | 
					                line.display_name = textwrap.shorten(line.value_text_box, | 
				
			||||
 | 
					                                                     width=50, | 
				
			||||
 | 
					                                                     placeholder=" [...]") | 
				
			||||
 | 
					            elif line.answer_type == 'numerical_box': | 
				
			||||
 | 
					                line.display_name = line.value_numerical_box | 
				
			||||
 | 
					            elif line.answer_type == 'date': | 
				
			||||
 | 
					                line.display_name = fields.Date.to_string(line.value_date) | 
				
			||||
 | 
					            elif line.answer_type == 'datetime': | 
				
			||||
 | 
					                line.display_name = fields.Datetime.to_string(line.value_datetime) | 
				
			||||
 | 
					            elif line.answer_type == 'time': | 
				
			||||
 | 
					                line.display_name = line.value_time | 
				
			||||
 | 
					            elif line.answer_type == 'month': | 
				
			||||
 | 
					                line.display_name = line.value_month | 
				
			||||
 | 
					            elif line.answer_type == 'address': | 
				
			||||
 | 
					                line.display_name = line.value_address | 
				
			||||
 | 
					            elif line.answer_type == 'name': | 
				
			||||
 | 
					                line.display_name = line.value_name | 
				
			||||
 | 
					            elif line.answer_type == 'url': | 
				
			||||
 | 
					                line.display_name = line.value_url | 
				
			||||
 | 
					            elif line.answer_type == 'many2one': | 
				
			||||
 | 
					                line.display_name = line.value_many2one_option | 
				
			||||
 | 
					            elif line.answer_type == 'many2many': | 
				
			||||
 | 
					                line.display_name = line.value_many2many | 
				
			||||
 | 
					            elif line.answer_type == 'week': | 
				
			||||
 | 
					                line.display_name = line.value_week | 
				
			||||
 | 
					            elif line.answer_type == 'time': | 
				
			||||
 | 
					                line.display_name = line.value_time | 
				
			||||
 | 
					            elif line.answer_type == 'email': | 
				
			||||
 | 
					                line.display_name = line.value_email | 
				
			||||
 | 
					            elif line.answer_type == 'range': | 
				
			||||
 | 
					                line.display_name = line.value_range | 
				
			||||
 | 
					            elif line.answer_type == 'matrix': | 
				
			||||
 | 
					                line.display_name = line.value_matrix | 
				
			||||
 | 
					            elif line.answer_type == 'password': | 
				
			||||
 | 
					                line.display_name = line.value_password | 
				
			||||
 | 
					            elif line.answer_type == 'signature': | 
				
			||||
 | 
					                line.display_name = line.value_signature | 
				
			||||
 | 
					            elif line.answer_type == 'color': | 
				
			||||
 | 
					                line.display_name = line.value_color | 
				
			||||
 | 
					            elif line.answer_type == 'selection': | 
				
			||||
 | 
					                line.display_name = line.value_selection | 
				
			||||
 | 
					            elif line.answer_type == 'barcode': | 
				
			||||
 | 
					                line.display_name = line.value_barcode | 
				
			||||
 | 
					            elif line.answer_type == 'qr': | 
				
			||||
 | 
					                line.display_name = line.value_qr | 
				
			||||
 | 
					            elif line.answer_type == 'file': | 
				
			||||
 | 
					                line.display_name = line.filename | 
				
			||||
 | 
					            elif line.answer_type == 'suggestion': | 
				
			||||
 | 
					                if line.matrix_row_id: | 
				
			||||
 | 
					                    line.display_name = '%s: %s' % ( | 
				
			||||
 | 
					                        line.suggested_answer_id.value, | 
				
			||||
 | 
					                        line.matrix_row_id.value) | 
				
			||||
 | 
					                else: | 
				
			||||
 | 
					                    line.display_name = line.suggested_answer_id.value | 
				
			||||
 | 
					            if not line.display_name: | 
				
			||||
 | 
					                line.display_name = _('Skipped') | 
				
			||||
		
		
			
  | 
| 
		 After Width: | Height: | Size: 36 KiB  | 
| 
		 After Width: | Height: | Size: 3.6 KiB  | 
| 
		 After Width: | Height: | Size: 310 B  | 
| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 After Width: | Height: | Size: 576 B  | 
| 
		 After Width: | Height: | Size: 733 B  | 
| 
		 After Width: | Height: | Size: 911 B  | 
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 673 B  | 
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 After Width: | Height: | Size: 878 B  | 
| 
		 After Width: | Height: | Size: 653 B  | 
| 
		 After Width: | Height: | Size: 905 B  | 
| 
		 After Width: | Height: | Size: 839 B  | 
| 
		 After Width: | Height: | Size: 427 B  | 
| 
		 After Width: | Height: | Size: 627 B  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 988 B  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 80 KiB  | 
| 
		 After Width: | Height: | Size: 3.2 KiB  | 
| 
		 After Width: | Height: | Size: 565 B  | 
| 
		 After Width: | Height: | Size: 26 KiB  | 
| 
		 After Width: | Height: | Size: 43 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 After Width: | Height: | Size: 4.0 KiB  | 
| 
		 After Width: | Height: | Size: 38 KiB  | 
| 
		 After Width: | Height: | Size: 4.3 KiB  | 
| 
		 After Width: | Height: | Size: 82 KiB  | 
| 
		 After Width: | Height: | Size: 81 KiB  | 
| 
		 After Width: | Height: | Size: 86 KiB  | 
| 
		 After Width: | Height: | Size: 94 KiB  | 
| 
		 After Width: | Height: | Size: 88 KiB  | 
| 
		 After Width: | Height: | Size: 81 KiB  | 
| 
		 After Width: | Height: | Size: 105 KiB  | 
| 
		 After Width: | Height: | Size: 19 KiB  | 
| 
		 After Width: | Height: | Size: 88 KiB  | 
| 
		 After Width: | Height: | Size: 27 KiB  | 
| 
		 After Width: | Height: | Size: 101 KiB  | 
| 
		 After Width: | Height: | Size: 20 KiB  | 
| 
		 After Width: | Height: | Size: 102 KiB  | 
| 
		 After Width: | Height: | Size: 18 KiB  | 
| 
		 After Width: | Height: | Size: 38 KiB  | 
| 
		 After Width: | Height: | Size: 88 KiB  | 
| 
		 After Width: | Height: | Size: 95 KiB  | 
| 
		 After Width: | Height: | Size: 40 KiB  | 
| 
		 After Width: | Height: | Size: 21 KiB  | 
| 
		 After Width: | Height: | Size: 90 KiB  | 
| 
		 After Width: | Height: | Size: 23 KiB  | 
| 
		 After Width: | Height: | Size: 91 KiB  | 
| 
		 After Width: | Height: | Size: 51 KiB  | 
| 
		 After Width: | Height: | Size: 90 KiB  | 
| 
		 After Width: | Height: | Size: 25 KiB  | 
| 
		 After Width: | Height: | Size: 91 KiB  | 
| 
		 After Width: | Height: | Size: 92 KiB  | 
| 
		 After Width: | Height: | Size: 37 KiB  | 
| 
		 After Width: | Height: | Size: 95 KiB  | 
| 
		 After Width: | Height: | Size: 106 KiB  | 
| 
		 After Width: | Height: | Size: 24 KiB  | 
| 
		 After Width: | Height: | Size: 94 KiB  | 
| 
		 After Width: | Height: | Size: 19 KiB  | 
| 
		 After Width: | Height: | Size: 116 KiB  | 
| 
		 After Width: | Height: | Size: 161 KiB  | 
| 
		 After Width: | Height: | Size: 154 KiB  | 
| 
		 After Width: | Height: | Size: 92 KiB  | 
| 
		 After Width: | Height: | Size: 32 KiB  | 
| 
		 After Width: | Height: | Size: 102 KiB  | 
| 
		 After Width: | Height: | Size: 23 KiB  | 
| 
		 After Width: | Height: | Size: 104 KiB  | 
| 
		 After Width: | Height: | Size: 26 KiB  | 
| 
		 After Width: | Height: | Size: 100 KiB  | 
| 
		 After Width: | Height: | Size: 264 KiB  | 
| 
		 After Width: | Height: | Size: 88 KiB  | 
| 
		 After Width: | Height: | Size: 11 KiB  | 
@ -0,0 +1,62 @@ | 
				
			|||||
 | 
					/** @odoo-module **/ | 
				
			||||
 | 
					import publicWidget from "@web/legacy/js/public/public_widget"; | 
				
			||||
 | 
					import { _t } from "@web/core/l10n/translation"; | 
				
			||||
 | 
					import { cookie } from "@web/core/browser/cookie"; | 
				
			||||
 | 
					import { utils as uiUtils } from "@web/core/ui/ui_service"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					import SurveyPreloadImageMixin from "@survey/js/survey_preload_image_mixin"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					publicWidget.registry.SurveyForm = publicWidget.Widget.extend(SurveyPreloadImageMixin, { | 
				
			||||
 | 
					        selector: '.o_survey_form', | 
				
			||||
 | 
					        events: { | 
				
			||||
 | 
					            'focus .o_select_Country': '_onSelectCountry', | 
				
			||||
 | 
					            'change .o_select_Country': '_onSelectState', | 
				
			||||
 | 
					            'change .o_select_many2many': '_onSelectMany2many', | 
				
			||||
 | 
					        }, | 
				
			||||
 | 
					        init(){ | 
				
			||||
 | 
					            this._super(...arguments); | 
				
			||||
 | 
					            this.rpc = this.bindService("rpc"); | 
				
			||||
 | 
					        }, | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        _onSelectCountry: function(ev){ | 
				
			||||
 | 
					            /* | 
				
			||||
 | 
					                * method to load country | 
				
			||||
 | 
					            */ | 
				
			||||
 | 
					            var self = this | 
				
			||||
 | 
					            this.rpc('/survey/load_country',{}) | 
				
			||||
 | 
					            .then(function (result){ | 
				
			||||
 | 
					                var count = 0; | 
				
			||||
 | 
					                self.$el.find(`#${ev.target.id}`).html('<option value="">Country</option>') | 
				
			||||
 | 
					                result['id'].forEach(element => { | 
				
			||||
 | 
					                    self.$el.find(`#${ev.target.id}`).append( | 
				
			||||
 | 
					                    `<option value='${result['name'][count]}'>${result['name'][count]}</option>` | 
				
			||||
 | 
					                    ) | 
				
			||||
 | 
					                    count += 1 | 
				
			||||
 | 
					                }) | 
				
			||||
 | 
					            }); | 
				
			||||
 | 
					        }, | 
				
			||||
 | 
					        _onSelectState: function(ev){ | 
				
			||||
 | 
					            /* | 
				
			||||
 | 
					                * method to load states | 
				
			||||
 | 
					            */ | 
				
			||||
 | 
					            var self = this | 
				
			||||
 | 
					            var country_id = ev.target.value | 
				
			||||
 | 
					            var question_id = ev.target.dataset.id | 
				
			||||
 | 
					            this.rpc('/survey/load_states',{ | 
				
			||||
 | 
					            params: { country_id }, | 
				
			||||
 | 
					            }).then(function (result){ | 
				
			||||
 | 
					                var count = 0; | 
				
			||||
 | 
					                self.$el.find(`#${question_id}-state`).html('<option value="">State</option>') | 
				
			||||
 | 
					                result['id'].forEach(element => { | 
				
			||||
 | 
					                    self.$el.find(`#${question_id}-state`).append( | 
				
			||||
 | 
					                    `<option value="${result['name'][count]}">${result['name'][count]}</option>` | 
				
			||||
 | 
					                    ) | 
				
			||||
 | 
					                    count += 1 | 
				
			||||
 | 
					                }) | 
				
			||||
 | 
					            }); | 
				
			||||
 | 
					        }, | 
				
			||||
 | 
					        _onSelectMany2many: function(ev){ | 
				
			||||
 | 
					         this.$el.find('.o_select_many2many_text').val(this.$el.find('.o_select_many2many').val()) | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					    }); | 
				
			||||
 | 
					export default publicWidget.registry.SurveyForm; | 
				
			||||
@ -0,0 +1,151 @@ | 
				
			|||||
 | 
					/** @odoo-module **/ | 
				
			||||
 | 
					import { | 
				
			||||
 | 
					    deserializeDate, | 
				
			||||
 | 
					    deserializeDateTime, | 
				
			||||
 | 
					    parseDateTime, | 
				
			||||
 | 
					    parseDate, | 
				
			||||
 | 
					    serializeDateTime, | 
				
			||||
 | 
					    serializeDate, | 
				
			||||
 | 
					} from "@web/core/l10n/dates"; | 
				
			||||
 | 
					import SurveyFormWidget from '@survey/js/survey_form'; | 
				
			||||
 | 
					    /* | 
				
			||||
 | 
					        * Including custom events to SurveyFormWidget | 
				
			||||
 | 
					    */ | 
				
			||||
 | 
					    SurveyFormWidget.include({ | 
				
			||||
 | 
					        events:{ | 
				
			||||
 | 
					                'change .o_file': '_onChangeFile', | 
				
			||||
 | 
					                'change .o_survey_form_choice_item': '_onChangeChoiceItem', | 
				
			||||
 | 
					                'click .o_survey_matrix_btn': '_onMatrixBtnClick', | 
				
			||||
 | 
					                'click input[type="radio"]': '_onRadioChoiceClick', | 
				
			||||
 | 
					                'click button[type="submit"]': '_onSubmit', | 
				
			||||
 | 
					                'click .o_survey_choice_img img': '_onChoiceImgClick', | 
				
			||||
 | 
					                'focusin .form-control': '_updateEnterButtonText', | 
				
			||||
 | 
					                'focusout .form-control': '_updateEnterButtonText' | 
				
			||||
 | 
					        }, | 
				
			||||
 | 
					        _prepareSubmitValues: function (formData, params) { | 
				
			||||
 | 
					        var self = this; | 
				
			||||
 | 
					        formData.forEach(function (value, key) { | 
				
			||||
 | 
					            switch (key) { | 
				
			||||
 | 
					                case 'csrf_token': | 
				
			||||
 | 
					                case 'token': | 
				
			||||
 | 
					                case 'page_id': | 
				
			||||
 | 
					                case 'question_id': | 
				
			||||
 | 
					                    params[key] = value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        }); | 
				
			||||
 | 
					        // Get all question answers by question type
 | 
				
			||||
 | 
					        var address = {} | 
				
			||||
 | 
					        var names = {} | 
				
			||||
 | 
					        var matrix = {} | 
				
			||||
 | 
					        var self = this; | 
				
			||||
 | 
					        this.$('[data-question-type]').each(function () { | 
				
			||||
 | 
					            switch ($(this).data('questionType')) { | 
				
			||||
 | 
					                case 'text_box': | 
				
			||||
 | 
					                case 'char_box': | 
				
			||||
 | 
					                case 'numerical_box': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'date': | 
				
			||||
 | 
					                case 'datetime': | 
				
			||||
 | 
					                    const [parse, serialize] = | 
				
			||||
 | 
					                        $(this).data("questionType") === "date" | 
				
			||||
 | 
					                            ? [parseDate, serializeDate] | 
				
			||||
 | 
					                            : [parseDateTime, serializeDateTime]; | 
				
			||||
 | 
					                    const date = parse(this.value); | 
				
			||||
 | 
					                    params[this.name] = date ? serialize(date) : ""; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'simple_choice_radio': | 
				
			||||
 | 
					                case 'multiple_choice': | 
				
			||||
 | 
					                    params = self._prepareSubmitChoices(params, $(this), $(this).data('name')); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'url': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'email': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'many2one': | 
				
			||||
 | 
					                    params[this.name] = [this.value, $(this).find("option:selected").attr('data-value')]; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'week': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'color': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'time': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'range': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'password': | 
				
			||||
 | 
					                    params[this.name] = this.value; | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'month': | 
				
			||||
 | 
					                    params[this.name] = this.value | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'address': | 
				
			||||
 | 
					                    address[this.name] = this.value | 
				
			||||
 | 
					                    if (this.name.endsWith('pin')){ | 
				
			||||
 | 
					                        address[this.name.split("-")[0]+'-country'] = self.$el.find(`#${this.name.split("-")[0]+'-country'}`).val(), | 
				
			||||
 | 
					                        address[this.name.split("-")[0]+'-state'] = self.$el.find(`#${this.name.split("-")[0]+'-state'}`).val() | 
				
			||||
 | 
					                        params[this.name.split("-")[0]] = address | 
				
			||||
 | 
					                        address = {} | 
				
			||||
 | 
					                        break; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					                case 'custom': | 
				
			||||
 | 
					                    if (this.name == 'matrix-end'){ | 
				
			||||
 | 
					                        params[this.id] = matrix} | 
				
			||||
 | 
					                    if ($(this).attr('id') === 'select' && this.name){ | 
				
			||||
 | 
					                       matrix[this.name] = $(this).find("option:selected").attr('data-value')} | 
				
			||||
 | 
					                    if ($(this).attr('id') !== 'select' && this.name){ | 
				
			||||
 | 
					                       matrix[this.name] = this.value | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                case 'matrix': | 
				
			||||
 | 
					                    params = self._prepareSubmitAnswersMatrix(params, $(this)); | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'name': | 
				
			||||
 | 
					                    names[this.name] = this.value | 
				
			||||
 | 
					                    if (this.name.endsWith('last')){ | 
				
			||||
 | 
					                        params[this.name.split("-")[0]] = names | 
				
			||||
 | 
					                        break; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'selection': | 
				
			||||
 | 
					                    params[this.name] = this.value | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'file': | 
				
			||||
 | 
					                    if ($(this)[0].files[0]){ | 
				
			||||
 | 
					                        params[this.name] = [$(this).data('file-name'), $(this)[0].files[0]['name']] | 
				
			||||
 | 
					                        break; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                case 'many2many': | 
				
			||||
 | 
					                    params[this.name] = self.$el.find(`.${this.name}`).val() | 
				
			||||
 | 
					                    break; | 
				
			||||
 | 
					                    } | 
				
			||||
 | 
					                }); | 
				
			||||
 | 
					             }, | 
				
			||||
 | 
					        _onChangeFile: function (ev) { | 
				
			||||
 | 
					            /* | 
				
			||||
 | 
					                *  method to save attachments | 
				
			||||
 | 
					            */ | 
				
			||||
 | 
					            const element = this.$(ev.target); | 
				
			||||
 | 
					            for (var i=0; i < element.length; i++){ | 
				
			||||
 | 
					                const elements = $(element[i]) | 
				
			||||
 | 
					                if (element[i].files[0] && elements.data('file') === parseInt(elements.attr('name'))) { | 
				
			||||
 | 
					                    var file_name = element[i].files[0]['name'] | 
				
			||||
 | 
					                    const reader = new FileReader(); | 
				
			||||
 | 
					                    reader.onloadend = () => { | 
				
			||||
 | 
					                        elements.attr('data-file-name', reader.result.split(',')[1]) | 
				
			||||
 | 
					                        elements.attr('data-file', file_name) | 
				
			||||
 | 
					                    }; | 
				
			||||
 | 
					                    reader.readAsDataURL(element[i].files[0]); | 
				
			||||
 | 
					                } | 
				
			||||
 | 
					            } | 
				
			||||
 | 
					        }, | 
				
			||||
 | 
					    }) | 
				
			||||
@ -0,0 +1,46 @@ | 
				
			|||||
 | 
					<?xml version="1.0" encoding="UTF-8" ?> | 
				
			||||
 | 
					<odoo> | 
				
			||||
 | 
					    <!-- Survey result view inherit to view input of custom question types --> | 
				
			||||
 | 
					    <template id="survey_page_print" name="Survey: print page(Inherit)" | 
				
			||||
 | 
					              inherit_id="survey.survey_page_print"> | 
				
			||||
 | 
					        <xpath expr="//div[@role='alert']" position="before"> | 
				
			||||
 | 
					            <t t-if="question.question_type in ['time','date', 'month', 'address', 'url', 'week', 'color', 'email', 'password', | 
				
			||||
 | 
					                     'many2one', 'file','range','many2many', 'name', 'matrix', 'selection', 'qr', 'barcode']"> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'time'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_time"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'month'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_month"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'url'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_url"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'week'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_week"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'color'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_color"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'address'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_address"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'email'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_email"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'range'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_range"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'selection'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_selection_choice"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'password'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_password"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'many2one'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_many2one"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'name'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_name"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'file'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_file"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'matrix'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_matrix_custom"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'many2many'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_many2many"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'barcode'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_barcode"/> | 
				
			||||
 | 
					                <t t-if="question.question_type == 'qr'" | 
				
			||||
 | 
					                   t-call="enhanced_survey_management.question_qr"/> | 
				
			||||
 | 
					            </t> | 
				
			||||
 | 
					        </xpath> | 
				
			||||
 | 
					    </template> | 
				
			||||
 | 
					</odoo> | 
				
			||||
@ -0,0 +1,132 @@ | 
				
			|||||
 | 
					<?xml version="1.0" encoding="UTF-8" ?> | 
				
			||||
 | 
					<odoo> | 
				
			||||
 | 
					    <!-- Survey portal Views --> | 
				
			||||
 | 
					    <template id="portal_my_home_survey" | 
				
			||||
 | 
					              name="survey" customize_show="True" | 
				
			||||
 | 
					              inherit_id="portal.portal_my_home" priority="20"> | 
				
			||||
 | 
					        <xpath expr="//div[hasclass('o_portal_docs')]" position="inside"> | 
				
			||||
 | 
					            <t t-call="portal.portal_docs_entry"> | 
				
			||||
 | 
					                <t t-set="title">Survey</t> | 
				
			||||
 | 
					                <t t-set="url" t-value="'/my/survey/ans'"/> | 
				
			||||
 | 
					                <t t-set="placeholder_count" t-value="'survey_count'"/> | 
				
			||||
 | 
					            </t> | 
				
			||||
 | 
					        </xpath> | 
				
			||||
 | 
					    </template> | 
				
			||||
 | 
					    <!-- Survey portal Breadcrumbs --> | 
				
			||||
 | 
					    <template id="portal_my_home_menu_survey" name="Portal layout : Survey menu" | 
				
			||||
 | 
					              inherit_id="portal.portal_breadcrumbs" | 
				
			||||
 | 
					              priority="20"> | 
				
			||||
 | 
					        <xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside"> | 
				
			||||
 | 
					            <li t-if="page_name == 'survey'"> | 
				
			||||
 | 
					                <a t-if="survey_boolean" t-attf-href="/my/survey/ans">/ | 
				
			||||
 | 
					                    Surveys | 
				
			||||
 | 
					                </a> | 
				
			||||
 | 
					                <t t-else="">/ Surveys</t> | 
				
			||||
 | 
					            </li> | 
				
			||||
 | 
					            <li t-if="survey_boolean" class="breadcrumb-item active"> | 
				
			||||
 | 
					                / | 
				
			||||
 | 
					                <t t-esc="survey"/> | 
				
			||||
 | 
					            </li> | 
				
			||||
 | 
					        </xpath> | 
				
			||||
 | 
					    </template> | 
				
			||||
 | 
					    <!-- Survey portal Views: Surveys --> | 
				
			||||
 | 
					    <template id="portal_survey_result" name="Survey"> | 
				
			||||
 | 
					        <t t-call="portal.portal_layout"> | 
				
			||||
 | 
					            <t t-set="breadcrumbs_searchbar" t-value="True"/> | 
				
			||||
 | 
					            <t t-call="portal.portal_searchbar"> | 
				
			||||
 | 
					                <t t-set="title">Survey</t> | 
				
			||||
 | 
					            </t> | 
				
			||||
 | 
					            <table class="table rounded mb-0 bg-white o_portal_my_doc_table"> | 
				
			||||
 | 
					                <thead> | 
				
			||||
 | 
					                    <tr class="active"> | 
				
			||||
 | 
					                        <th class="text-right">Survey</th> | 
				
			||||
 | 
					                        <th class="text-right">Partner</th> | 
				
			||||
 | 
					                        <th class="text-right">Email</th> | 
				
			||||
 | 
					                        <th class="text-right">Status</th> | 
				
			||||
 | 
					                        <th class="text-right"/> | 
				
			||||
 | 
					                    </tr> | 
				
			||||
 | 
					                </thead> | 
				
			||||
 | 
					                <t t-foreach="survey_list" t-as="survey"> | 
				
			||||
 | 
					                    <tr> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <a t-attf-href="/my/survey/{{survey['id']}}"> | 
				
			||||
 | 
					                                <span t-esc="survey['survey']"/> | 
				
			||||
 | 
					                            </a> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="survey['user_id']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="survey['email']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="survey['status']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                    </tr> | 
				
			||||
 | 
					                </t> | 
				
			||||
 | 
					            </table> | 
				
			||||
 | 
					        </t> | 
				
			||||
 | 
					    </template> | 
				
			||||
 | 
					    <!-- Survey portal views results --> | 
				
			||||
 | 
					    <template id="portal_survey_result_view" name="Survey Result"> | 
				
			||||
 | 
					        <t t-call="portal.portal_layout"> | 
				
			||||
 | 
					            <t t-set="breadcrumbs_searchbar" t-value="True"/> | 
				
			||||
 | 
					            <t t-call="portal.portal_searchbar"> | 
				
			||||
 | 
					                <t t-set="title">Survey Results</t> | 
				
			||||
 | 
					            </t> | 
				
			||||
 | 
					            <table class="table"> | 
				
			||||
 | 
					                <tr> | 
				
			||||
 | 
					                    <td class="text-right"> | 
				
			||||
 | 
					                        <strong>Survey:</strong> | 
				
			||||
 | 
					                        <span t-esc="survey"/> | 
				
			||||
 | 
					                    </td> | 
				
			||||
 | 
					                    <td> | 
				
			||||
 | 
					                        <strong>Created on:</strong> | 
				
			||||
 | 
					                        <span t-esc="survey_date"/> | 
				
			||||
 | 
					                    </td> | 
				
			||||
 | 
					                </tr> | 
				
			||||
 | 
					                <tr> | 
				
			||||
 | 
					                    <td> | 
				
			||||
 | 
					                        <strong>Partner:</strong> | 
				
			||||
 | 
					                        <span t-esc="name"/> | 
				
			||||
 | 
					                    </td> | 
				
			||||
 | 
					                    <td> | 
				
			||||
 | 
					                        <strong>Email:</strong> | 
				
			||||
 | 
					                        <span t-esc="email"/> | 
				
			||||
 | 
					                    </td> | 
				
			||||
 | 
					                </tr> | 
				
			||||
 | 
					            </table> | 
				
			||||
 | 
					            <table class="table rounded mb-0 bg-white o_portal_my_doc_table"> | 
				
			||||
 | 
					                <thead> | 
				
			||||
 | 
					                    <tr class="active"> | 
				
			||||
 | 
					                        <th class="text-right">Questions</th> | 
				
			||||
 | 
					                        <th class="text-right">Created Date</th> | 
				
			||||
 | 
					                        <th class="text-right">Questions_type</th> | 
				
			||||
 | 
					                        <th class="text-right">Answer</th> | 
				
			||||
 | 
					                        <th class="text-right">Score</th> | 
				
			||||
 | 
					                    </tr> | 
				
			||||
 | 
					                </thead> | 
				
			||||
 | 
					                <t t-foreach="survey_questions" t-as="qst"> | 
				
			||||
 | 
					                    <tr> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="qst['questions']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="qst['section']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="qst['question_type']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="qst['answer']" | 
				
			||||
 | 
					                                  style="white-space: normal !important;"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                        <td class="text-right"> | 
				
			||||
 | 
					                            <span t-esc="qst['score']"/> | 
				
			||||
 | 
					                        </td> | 
				
			||||
 | 
					                    </tr> | 
				
			||||
 | 
					                </t> | 
				
			||||
 | 
					            </table> | 
				
			||||
 | 
					        </t> | 
				
			||||
 | 
					    </template> | 
				
			||||
 | 
					</odoo> | 
				
			||||
@ -0,0 +1,199 @@ | 
				
			|||||
 | 
					<?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.enhanced.survey.management | 
				
			||||
 | 
					        </field> | 
				
			||||
 | 
					        <field name="model">survey.question</field> | 
				
			||||
 | 
					        <field name="inherit_id" ref="survey.survey_question_form"/> | 
				
			||||
 | 
					        <field name="arch" type="xml"> | 
				
			||||
 | 
					            <!-- inherit question view to add custom preview of new question type. --> | 
				
			||||
 | 
					            <xpath expr="//div[hasclass('o_preview_questions')]" | 
				
			||||
 | 
					                   position="inside"> | 
				
			||||
 | 
					                <!-- Time Zone --> | 
				
			||||
 | 
					                <div invisible="question_type != 'time' "> | 
				
			||||
 | 
					                    <span>Work Start Time</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i class="fa fa-clock-o" role="img" | 
				
			||||
 | 
					                       aria-label="Single Line" title="Single Line">  | 
				
			||||
 | 
					                          --:-- | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Email --> | 
				
			||||
 | 
					                <div invisible="question_type != 'email'"> | 
				
			||||
 | 
					                    <span>Email</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i class="fa fa-envelope" role="img" | 
				
			||||
 | 
					                       aria-label="Single Line" title="Single Line">  | 
				
			||||
 | 
					                          ____________ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Month Zone --> | 
				
			||||
 | 
					                <div invisible="question_type != 'month'"> | 
				
			||||
 | 
					                    <span>Name one Month</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i class="fa fa-calendar" role="img" | 
				
			||||
 | 
					                       aria-label="Single Line" title="Single Line">  | 
				
			||||
 | 
					                          ____________ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Password --> | 
				
			||||
 | 
					                <div invisible="question_type != 'password'"> | 
				
			||||
 | 
					                    <span>Password</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i class="fa fa-lock" role="img" | 
				
			||||
 | 
					                       aria-label="Single Line" title="Single Line">  | 
				
			||||
 | 
					                          ######### | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Range --> | 
				
			||||
 | 
					                <div invisible="question_type != 'range'"> | 
				
			||||
 | 
					                    <span>Volume</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i>------</i> | 
				
			||||
 | 
					                    <i class="fa fa-bullseye" role="img" | 
				
			||||
 | 
					                       aria-label="Single Line" title="Single Line"> | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                    <i>----</i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Signature --> | 
				
			||||
 | 
					                <div invisible="question_type != 'signature'"> | 
				
			||||
 | 
					                    <span>Signature</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i class="fa fa-edit" role="img" | 
				
			||||
 | 
					                       aria-label="Single Line" title="Single Line">  | 
				
			||||
 | 
					                          _________ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Address Zone --> | 
				
			||||
 | 
					                <div invisible="question_type != 'address'"> | 
				
			||||
 | 
					                    <span>Current Address</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Single Line" title="Single Line"> | 
				
			||||
 | 
					                            __________ | 
				
			||||
 | 
					                        <br/>          __________ | 
				
			||||
 | 
					                        <br/> | 
				
			||||
 | 
					                        ___ ___ ___<br/>______ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Name --> | 
				
			||||
 | 
					                <div invisible="question_type != 'name'"> | 
				
			||||
 | 
					                    <span>Your Name</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Name" title="Single Line">  | 
				
			||||
 | 
					                          ___ ___ ___ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- URL --> | 
				
			||||
 | 
					                <div invisible="question_type != 'url'"> | 
				
			||||
 | 
					                    <span>URL</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="URL" class="fa fa-link" | 
				
			||||
 | 
					                       title="Single Line">    ________ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Week --> | 
				
			||||
 | 
					                <div invisible="question_type != 'week'"> | 
				
			||||
 | 
					                    <span>Select a week</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Week" class="fa fa-calendar" | 
				
			||||
 | 
					                       title="Week">   Week --, --- | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Color --> | 
				
			||||
 | 
					                <div invisible="question_type != 'color'"> | 
				
			||||
 | 
					                    <span>Pick a color</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Color" class="fa fa-paint-brush" | 
				
			||||
 | 
					                       title="Color">   ________ | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Qr Code --> | 
				
			||||
 | 
					                <div invisible="question_type != 'qr'"> | 
				
			||||
 | 
					                    <span>Scan Qrcode</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Color" class="fa fa-qrcode" | 
				
			||||
 | 
					                       title="Color">  | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                    ___________ | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Qr Code --> | 
				
			||||
 | 
					                <div invisible="question_type != 'barcode'"> | 
				
			||||
 | 
					                    <span>Scan Barcode</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Color" class="fa fa-barcode" | 
				
			||||
 | 
					                       title="Color">  | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                    ___________ | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Qr Code --> | 
				
			||||
 | 
					                <div invisible="question_type != 'file'"> | 
				
			||||
 | 
					                    <span>Upload a file</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Color" class="fa fa-file" | 
				
			||||
 | 
					                       title="Color">  | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                    ___________ | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Qr Code --> | 
				
			||||
 | 
					                <div invisible="question_type != 'selection'"> | 
				
			||||
 | 
					                    <span>Select from list</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    <i role="img" aria-label="Color" class="fa fa-angle-down" | 
				
			||||
 | 
					                       title="Color">  | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                    ___________ | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Many2one --> | 
				
			||||
 | 
					                <div invisible="question_type != 'many2one'"> | 
				
			||||
 | 
					                    <span>Select a Product</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    ___________ | 
				
			||||
 | 
					                    <i role="img" aria-label="Color" class="fa fa-angle-down" | 
				
			||||
 | 
					                       title="Color">  | 
				
			||||
 | 
					                    </i> | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					                <!-- Many2many --> | 
				
			||||
 | 
					                <div invisible="question_type != 'many2many'"> | 
				
			||||
 | 
					                    <span>Select tags</span> | 
				
			||||
 | 
					                    <br/> | 
				
			||||
 | 
					                    _()_()_()_()_()_ | 
				
			||||
 | 
					                </div> | 
				
			||||
 | 
					            </xpath> | 
				
			||||
 | 
					            <!-- Adding necessary field related to custom question types --> | 
				
			||||
 | 
					            <xpath expr="//page[@name='answers']/group" position="inside"> | 
				
			||||
 | 
					                <group invisible="question_type not in ['many2one', 'many2many']"> | 
				
			||||
 | 
					                    <field name="model_id" | 
				
			||||
 | 
					                           required="question_type in ['many2one', 'many2many']"/> | 
				
			||||
 | 
					                </group> | 
				
			||||
 | 
					                <group> | 
				
			||||
 | 
					                    <field name="selection_ids" | 
				
			||||
 | 
					                           invisible="question_type != 'selection'"> | 
				
			||||
 | 
					                        <tree editable="bottom"> | 
				
			||||
 | 
					                            <field name="name"/> | 
				
			||||
 | 
					                        </tree> | 
				
			||||
 | 
					                    </field> | 
				
			||||
 | 
					                </group> | 
				
			||||
 | 
					                <group invisible="question_type != 'range'"> | 
				
			||||
 | 
					                    <field name="range_min"/> | 
				
			||||
 | 
					                    <field name="range_max"/> | 
				
			||||
 | 
					                </group> | 
				
			||||
 | 
					                <group invisible="question_type != 'qr'"> | 
				
			||||
 | 
					                    <field name="qrcode"/> | 
				
			||||
 | 
					                </group> | 
				
			||||
 | 
					                <group invisible="question_type != 'barcode'"> | 
				
			||||
 | 
					                    <field name="barcode"/> | 
				
			||||
 | 
					                </group> | 
				
			||||
 | 
					            </xpath> | 
				
			||||
 | 
					            <!-- Adding matrix field related to matrix question types --> | 
				
			||||
 | 
					            <xpath expr="//field[@name='suggested_answer_ids']/tree/field[@name='value']" | 
				
			||||
 | 
					                   position="after"> | 
				
			||||
 | 
					                <field name="answer_type" | 
				
			||||
 | 
					                       column_invisible="parent.matrix_subtype != 'custom'"/> | 
				
			||||
 | 
					                <field name="model_id" | 
				
			||||
 | 
					                       required="answer_type == 'many2one'" | 
				
			||||
 | 
					                       column_invisible="parent.matrix_subtype != 'custom'"/> | 
				
			||||
 | 
					            </xpath> | 
				
			||||
 | 
					        </field> | 
				
			||||
 | 
					    </record> | 
				
			||||
 | 
					</odoo> | 
				
			||||