Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ env:

# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449
parserOptions:
ecmaVersion: 2019
ecmaVersion: 2021

overrides:
- files:
Expand Down
1 change: 1 addition & 0 deletions setup/survey_sign_oca/odoo/addons/survey_sign_oca
6 changes: 6 additions & 0 deletions setup/survey_sign_oca/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
174 changes: 174 additions & 0 deletions survey_sign_oca/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
===============
Survey Sign Oca
===============

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:c6ec61a6c8661f9097946736d2cd67efc929e1531803516fa6d82728aab2c98a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsign-lightgray.png?logo=github
:target: https://github.com/OCA/sign/tree/16.0/survey_sign_oca
:alt: OCA/sign
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/sign-16-0/sign-16-0-survey_sign_oca
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/sign&target_branch=16.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module allows you to make survey signature requests manually or
automatically.

**Table of contents**

.. contents::
:local:

Configuration
=============

1. Go to Sign > Settings > Roles and create a new one with the following
data if not there:

- For the survey participant

- Partner type: Expression
- Expression: {{object.partner_id.id}}

- For the survey responsible if you want

- Partner type: Expression
- Expression: {{object.survey_id.user_id.partner_id.id}}

2. Go to Sign > Settings > Fields and create a new one with the
following data if not there:

- Text Survey Field

- Field Type: text
- Default Value: survey

- Check Survey Field

- Field Type: check
- Default Value: survey

- Signature Survey Field

- Field Type: signature
- Default Value: survey

3. Go to Sign > Templates and create a template with the following data:

- Model: Survey User Input
- In some of the elements you will have to set the previously created
role[s].

4. Go to Settings > Survey Sign OCA:

5. Defines the template previously created (optional, only for automatic
creation of signature requests).

Usage
=====

A smart-button will be displayed on the participations form view of the
linked sign requests.

Manual Sign Request creation process:

::

- Go to Surveys \> Participations and change to list view.
- Select the records that you want.
- Click on the "Sign from template" action.
- Select a template.
- Click on Generate.
- Sign Requests will have been generated (and sent) for the selected items.

Automatic Sign Request creation process:

::

- When a partner takes a survey, a sign request will be created automatically.

For generated sign requests scoping survey, input values are populated
automatically with the survey answered values.

Answers for multiple choice questions are displayed as a sequence if you
add the question in placeholder. If you suffix the question in
placeholder with specific answer then it will be displayed or checked if
it is the answer.

Answers of yes/no type are shown in text fields as Yes/No and in check
fields as checked/unchecked.

Answers of type binary can be trated as signature fields if the survey
question is the same as the signature placeholder in sign request.

In that case make sure to have this module:
``survey_question_type_binary`` from OCA installed.

Signers can still update the input values if they want.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/sign/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/sign/issues/new?body=module:%20survey_sign_oca%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Kencove

Contributors
------------

- `Kencove <https://kencove.com>`__

- Mohamed Alkobrosli

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-Kencove| image:: https://github.com/Kencove.png?size=40px
:target: https://github.com/Kencove
:alt: Kencove

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-Kencove|

This module is part of the `OCA/sign <https://github.com/OCA/sign/tree/16.0/survey_sign_oca>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions survey_sign_oca/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
19 changes: 19 additions & 0 deletions survey_sign_oca/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 Kencove - Mohamed Alkobrosli
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Survey Sign Oca",
"version": "16.0.1.0.0",
"category": "Surveys",
"website": "https://github.com/OCA/sign",
"author": "Kencove, Odoo Community Association (OCA)",
"license": "AGPL-3",
"depends": ["sign_oca", "survey"],
"data": [
"views/survey_user_views.xml",
"views/res_config_settings_view.xml",
"views/sign_oca_request_views.xml",
"data/sign_oca_role.xml",
],
"installable": True,
"maintainers": ["Kencove"],
}
33 changes: 33 additions & 0 deletions survey_sign_oca/data/sign_oca_role.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<odoo>
<record id="sign_field_survey_text" model="sign.oca.field">
<field name="name">Survey Text</field>
<field name="field_type">text</field>
<field name="default_value">survey</field>
</record>

<record id="sign_field_survey_check" model="sign.oca.field">
<field name="name">Survey Check</field>
<field name="field_type">check</field>
<field name="default_value">survey</field>
</record>

<record id="sign_field_survey_signature" model="sign.oca.field">
<field name="name">Survey Signature</field>
<field name="field_type">signature</field>
<field name="default_value">survey</field>
</record>

<record id="role_survey_user_input" model="sign.oca.role">
<field name="name">Survey Participant</field>
<field name="partner_selection_policy">expression</field>
<field name="expression_partner">{{object.partner_id.id}}</field>
</record>

<record id="role_survey_responsible_input" model="sign.oca.role">
<field name="name">Survey Responsible</field>
<field name="partner_selection_policy">expression</field>
<field
name="expression_partner"
>{{object.survey_id.user_id.partner_id.id}}</field>
</record>
</odoo>
4 changes: 4 additions & 0 deletions survey_sign_oca/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import survey_invite
from . import res_company
from . import res_config_settings
from . import sign_oca_request
14 changes: 14 additions & 0 deletions survey_sign_oca/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2025 Kencove - Mohamed Alkobrosli
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class ResCompany(models.Model):
_inherit = "res.company"

survey_user_input_sign_oca_template_id = fields.Many2one(
comodel_name="sign.oca.template",
domain="[('model_id.model', '=', 'survey.user_input')]",
string="Sign Oca Template",
)
15 changes: 15 additions & 0 deletions survey_sign_oca/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2025 Kencove - Mohamed Alkobrosli
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

survey_user_input_sign_oca_template_id = fields.Many2one(
comodel_name="sign.oca.template",
related="company_id.survey_user_input_sign_oca_template_id",
string="Sign Oca Template",
readonly=False,
)
109 changes: 109 additions & 0 deletions survey_sign_oca/models/sign_oca_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copyright 2025 Kencove - Mohamed Alkobrosli
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from collections import defaultdict

from odoo import api, fields, models


# This class is to format questions of type (yes, no)
# in case we need the answer as (True, False) in case of filling checkboxes
class SurveyUtils:
@staticmethod
def is_yes_no_answer(value):
return str(value).strip().lower() in ["yes", "no"]

@staticmethod
def answer_is_yes(value):
return str(value).strip().lower() == "yes"

@staticmethod
def format_answer(answer):
if SurveyUtils.is_yes_no_answer(answer):
answer = SurveyUtils.answer_is_yes(answer)
return answer


class SignOcaRequest(models.Model):
_inherit = "sign.oca.request"

# This field is required for the inverse of survey.user.input.
survey_user_input_id = fields.Many2one(
comodel_name="survey.user_input",
compute="_compute_survey_user_input_id",
string="Survey Participation",
readonly=True,
store=True,
ondelete="cascade",
)

@api.depends("record_ref")
def _compute_survey_user_input_id(self):
for item in self.filtered(
lambda x: x.record_ref and x.record_ref._name == "survey.user_input"
):
item.survey_user_input_id = item.record_ref.id


class SignOcaRequestSigner(models.Model):

_inherit = "sign.oca.request.signer"

def get_related_survey_answers(self):
self.ensure_one()
survey_participation = self.request_id.record_ref
survey = {}
answers_list = []
if survey_participation:
for line in survey_participation.user_input_line_ids:
# Questions of type matrix have questions and suggested answers like a matrix
# We map the answeres to the questions for each matrix type
if line.question_id.question_type == "matrix":
answer = line.suggested_answer_id.value
survey.update({line.matrix_row_id.value: answer})
elif line.question_id.question_type in ["binary", "signature"]:
answer = line.answer_binary_ids[:1].value_binary
survey.update({line.question_id.display_name: answer})
elif line.question_id.question_type == "multiple_choice":
answer = line.display_name
answers_list.append([line.question_id.display_name, answer])
survey.update(
{f"{line.question_id.display_name}, {answer}": answer}
)
else:
answer = line.display_name
survey.update({line.question_id.display_name: answer})
# Map answers for the same questions
mapped_answers = defaultdict(list)
for question, answer in answers_list:
mapped_answers[question].append(answer)
survey.update(dict(mapped_answers))
return survey

def fill_survey_related_items(self, vals):
survey = self.get_related_survey_answers()
items = vals["items"]
for key in items:
item = items[key]
placeholder = item.get("placeholder")
# According to placeholder we target the answer of a question
# having same text as the placeholder
# and we choose the answer format based on the item field_typ
if survey.get(placeholder) and item["role_id"] == self.role_id.id:
if survey.get(placeholder) and item["field_type"] == "text":
item["value"] = survey.get(placeholder)
elif survey.get(placeholder) and item["field_type"] == "check":
item["value"] = SurveyUtils.format_answer(survey.get(placeholder))
elif survey.get(placeholder) and item["field_type"] == "signature":
item["value"] = survey.get(placeholder)
if item.get("default_value") and item.get("default_value") != "survey":
item["value"] = vals.get("partner").get(item["default_value"])
return vals

def get_info(self, access_token=False):
vals = super().get_info(access_token)
# get survey answers for this sign request
# and fill items with answers / default values
model_id = self.request_id.template_id.model_id
if model_id and model_id.model == "survey.user_input":
vals = self.fill_survey_related_items(vals)
return vals
Loading
Loading