diff --git a/fieldservice_sign/README.rst b/fieldservice_sign/README.rst new file mode 100644 index 0000000000..5116673eb7 --- /dev/null +++ b/fieldservice_sign/README.rst @@ -0,0 +1,145 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +==================== +Field Service - Sign +==================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:18abaccc11d7489d636730dae5d0a0ff39ca484f527b3a6ef803ea6c172d48aa + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Ffield--service-lightgray.png?logo=github + :target: https://github.com/OCA/field-service/tree/19.0/fieldservice_sign + :alt: OCA/field-service +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/field-service-19-0/field-service-19-0-fieldservice_sign + :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/field-service&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds configurable customer signing features on Field Service +orders: + +- **On-site signature capture** — uses the native signature widget and + the signature fields already provided by ``fieldservice`` + (``signature``, ``signed_by``, ``signed_on``). Capture can be enabled + or disabled per company, and optionally required before an order can + be completed. +- **Document signing** — integrates with ``sign_oca`` to launch a + signature request for a document linked to the order, expose its + status on the order, and optionally require a signed document before + completion. + +Both features are independent. An administrator can enable none, either +one, or both from Field Service settings. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Go to *Field Service > Configuration > Settings* and open the +**Signatures** section: + +1. Enable **On-site Signature Capture** to surface customer signature + capture on orders. Optionally enable **Require Signature to + Complete**. +2. Enable **Document Signing** to allow launching ``sign_oca`` requests + from orders. Optionally enable **Require Document Signed to + Complete**. +3. Select the **FSM Order Sign Template** used when requesting a + document signature. The template model must be ``fsm.order``. + +Usage +===== + +On-site signature capture +------------------------- + +1. Open a Field Service order. +2. When the order stage requires a signature, use **Sign** to capture + the customer signature. +3. Complete the order. If the company requires a signature, completion + is blocked until one is captured. + +The signature is also printed on the Service Order report when capture +is enabled. + +Document signing +---------------- + +1. Open a Field Service order. +2. Click **Request Signature** to create and send a ``sign_oca`` request + from the company template. +3. Follow the linked signature requests from the smart button or the + **Document Signing** tab. +4. Complete the order. If the company requires a signed document, + completion is blocked until at least one request is signed. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Gray Matter Logic + +Contributors +------------ + +- `Gray Matter Logic `__: + + - Maxime Chambreuil + +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-max3903| image:: https://github.com/max3903.png?size=40px + :target: https://github.com/max3903 + :alt: max3903 + +Current `maintainer `__: + +|maintainer-max3903| + +This module is part of the `OCA/field-service `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_sign/__init__.py b/fieldservice_sign/__init__.py new file mode 100644 index 0000000000..10b976be9c --- /dev/null +++ b/fieldservice_sign/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/fieldservice_sign/__manifest__.py b/fieldservice_sign/__manifest__.py new file mode 100644 index 0000000000..2db31dc926 --- /dev/null +++ b/fieldservice_sign/__manifest__.py @@ -0,0 +1,29 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Field Service - Sign", + "summary": "Customer signature capture and document signing on FSM orders", + "version": "19.0.1.0.0", + "category": "Field Service", + "author": "Gray Matter Logic, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/field-service", + "license": "AGPL-3", + "depends": ["fieldservice", "sign_oca"], + "data": [ + "views/res_config_settings_views.xml", + "views/fsm_order_views.xml", + "views/sign_oca_request_views.xml", + "report/fsm_order_report_templates.xml", + ], + "demo": [ + "demo/sign_oca_role.xml", + "demo/sign_oca_template.xml", + "demo/sign_oca_template_item.xml", + "demo/res_company.xml", + "demo/fsm_order.xml", + ], + "development_status": "Beta", + "maintainers": ["max3903"], + "installable": True, +} diff --git a/fieldservice_sign/demo/fsm_order.xml b/fieldservice_sign/demo/fsm_order.xml new file mode 100644 index 0000000000..add90aa327 --- /dev/null +++ b/fieldservice_sign/demo/fsm_order.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/fieldservice_sign/demo/res_company.xml b/fieldservice_sign/demo/res_company.xml new file mode 100644 index 0000000000..a296d87e35 --- /dev/null +++ b/fieldservice_sign/demo/res_company.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/fieldservice_sign/demo/sign_oca_role.xml b/fieldservice_sign/demo/sign_oca_role.xml new file mode 100644 index 0000000000..72820650c8 --- /dev/null +++ b/fieldservice_sign/demo/sign_oca_role.xml @@ -0,0 +1,10 @@ + + + + + FSM Order Customer + expression + {{object.location_id.owner_id.id}} + + diff --git a/fieldservice_sign/demo/sign_oca_template.xml b/fieldservice_sign/demo/sign_oca_template.xml new file mode 100644 index 0000000000..3c924637ce --- /dev/null +++ b/fieldservice_sign/demo/sign_oca_template.xml @@ -0,0 +1,10 @@ + + + + + FSM Order Approval + + + + diff --git a/fieldservice_sign/demo/sign_oca_template_item.xml b/fieldservice_sign/demo/sign_oca_template_item.xml new file mode 100644 index 0000000000..10a9b29313 --- /dev/null +++ b/fieldservice_sign/demo/sign_oca_template_item.xml @@ -0,0 +1,16 @@ + + + + + + + + 1 + 10 + 10 + 30 + 4 + + + diff --git a/fieldservice_sign/models/__init__.py b/fieldservice_sign/models/__init__.py new file mode 100644 index 0000000000..e58b05e6b9 --- /dev/null +++ b/fieldservice_sign/models/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import fsm_order +from . import res_company +from . import res_config_settings +from . import sign_oca_request diff --git a/fieldservice_sign/models/fsm_order.py b/fieldservice_sign/models/fsm_order.py new file mode 100644 index 0000000000..5f39038ad9 --- /dev/null +++ b/fieldservice_sign/models/fsm_order.py @@ -0,0 +1,117 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class FSMOrder(models.Model): + _inherit = "fsm.order" + + fsm_signature_capture = fields.Boolean( + related="company_id.fsm_signature_capture", + ) + fsm_document_signing = fields.Boolean( + related="company_id.fsm_document_signing", + ) + sign_request_ids = fields.One2many( + comodel_name="sign.oca.request", + inverse_name="fsm_order_id", + string="Signature Requests", + ) + sign_request_count = fields.Integer( + compute="_compute_sign_request_count", + compute_sudo=True, + ) + sign_request_id = fields.Many2one( + comodel_name="sign.oca.request", + string="Signature Request", + compute="_compute_sign_request_id", + compute_sudo=True, + store=True, + ) + sign_request_state = fields.Selection( + related="sign_request_id.state", + string="Signature Request Status", + ) + + @api.depends("sign_request_ids") + def _compute_sign_request_count(self): + for order in self: + order.sign_request_count = len(order.sign_request_ids) + + @api.depends("sign_request_ids", "sign_request_ids.state") + def _compute_sign_request_id(self): + for order in self: + order.sign_request_id = order.sign_request_ids.sorted("id", reverse=True)[ + :1 + ] + + def action_view_sign_requests(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "sign_oca.sign_oca_request_act_window" + ) + action["domain"] = [("id", "in", self.sign_request_ids.ids)] + action["context"] = dict( + self.env.context, + default_record_ref=f"{self._name},{self.id}", + ) + return action + + def action_request_document_signature(self): + self.ensure_one() + if not self.company_id.fsm_document_signing: + raise UserError( + self.env._( + "Document signing is disabled for company %(company)s.", + company=self.company_id.display_name, + ) + ) + template = self.company_id.fsm_order_sign_oca_template_id + if not template: + raise UserError( + self.env._( + "Configure an FSM Order Sign Template on the company " + "before requesting a document signature." + ) + ) + request = ( + self.env["sign.oca.request"] + .sudo() + .create(template._prepare_sign_oca_request_vals_from_record(self)) + ) + request.action_send() + return self.action_view_sign_requests() + + def _check_signature_requirements_before_complete(self): + for order in self: + company = order.company_id + if ( + company.fsm_signature_capture + and company.fsm_require_signature_to_complete + and not order.signature + ): + raise UserError( + self.env._( + "Customer signature is required before completing " + "order %(order)s.", + order=order.display_name, + ) + ) + if ( + company.fsm_document_signing + and company.fsm_require_document_signed_to_complete + and not order.sign_request_ids.filtered(lambda r: r.state == "2_signed") + ): + raise UserError( + self.env._( + "A signed document is required before completing " + "order %(order)s.", + order=order.display_name, + ) + ) + + def action_complete(self): + self._check_signature_requirements_before_complete() + return super().action_complete() diff --git a/fieldservice_sign/models/res_company.py b/fieldservice_sign/models/res_company.py new file mode 100644 index 0000000000..8a97631130 --- /dev/null +++ b/fieldservice_sign/models/res_company.py @@ -0,0 +1,31 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + fsm_signature_capture = fields.Boolean( + string="FSM Signature Capture", + help="Enable on-site customer signature capture on Field Service orders.", + ) + fsm_document_signing = fields.Boolean( + string="FSM Document Signing", + help="Enable sign_oca document signing on Field Service orders.", + ) + fsm_require_signature_to_complete = fields.Boolean( + string="Require Signature to Complete", + help="Block completing an order until a customer signature is captured.", + ) + fsm_require_document_signed_to_complete = fields.Boolean( + string="Require Document Signed to Complete", + help="Block completing an order until a linked signature request is signed.", + ) + fsm_order_sign_oca_template_id = fields.Many2one( + comodel_name="sign.oca.template", + string="FSM Order Sign Template", + domain="[('model_id.model', '=', 'fsm.order')]", + help="Default sign_oca template used when requesting a document signature.", + ) diff --git a/fieldservice_sign/models/res_config_settings.py b/fieldservice_sign/models/res_config_settings.py new file mode 100644 index 0000000000..7db37ff0f8 --- /dev/null +++ b/fieldservice_sign/models/res_config_settings.py @@ -0,0 +1,29 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + fsm_signature_capture = fields.Boolean( + related="company_id.fsm_signature_capture", + readonly=False, + ) + fsm_document_signing = fields.Boolean( + related="company_id.fsm_document_signing", + readonly=False, + ) + fsm_require_signature_to_complete = fields.Boolean( + related="company_id.fsm_require_signature_to_complete", + readonly=False, + ) + fsm_require_document_signed_to_complete = fields.Boolean( + related="company_id.fsm_require_document_signed_to_complete", + readonly=False, + ) + fsm_order_sign_oca_template_id = fields.Many2one( + related="company_id.fsm_order_sign_oca_template_id", + readonly=False, + ) diff --git a/fieldservice_sign/models/sign_oca_request.py b/fieldservice_sign/models/sign_oca_request.py new file mode 100644 index 0000000000..d355bd94e3 --- /dev/null +++ b/fieldservice_sign/models/sign_oca_request.py @@ -0,0 +1,24 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class SignOcaRequest(models.Model): + _inherit = "sign.oca.request" + + fsm_order_id = fields.Many2one( + comodel_name="fsm.order", + string="Field Service Order", + compute="_compute_fsm_order_id", + readonly=True, + store=True, + ) + + @api.depends("record_ref") + def _compute_fsm_order_id(self): + for request in self: + if request.record_ref and request.record_ref._name == "fsm.order": + request.fsm_order_id = request.record_ref.id + else: + request.fsm_order_id = False diff --git a/fieldservice_sign/pyproject.toml b/fieldservice_sign/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/fieldservice_sign/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fieldservice_sign/readme/CONFIGURE.md b/fieldservice_sign/readme/CONFIGURE.md new file mode 100644 index 0000000000..124ffb2708 --- /dev/null +++ b/fieldservice_sign/readme/CONFIGURE.md @@ -0,0 +1,9 @@ +Go to *Field Service \> Configuration \> Settings* and open the **Signatures** +section: + +1. Enable **On-site Signature Capture** to surface customer signature capture + on orders. Optionally enable **Require Signature to Complete**. +2. Enable **Document Signing** to allow launching `sign_oca` requests from + orders. Optionally enable **Require Document Signed to Complete**. +3. Select the **FSM Order Sign Template** used when requesting a document + signature. The template model must be `fsm.order`. diff --git a/fieldservice_sign/readme/CONTRIBUTORS.md b/fieldservice_sign/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..52c0a8351b --- /dev/null +++ b/fieldservice_sign/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Gray Matter Logic](https://www.graymatterlogic.com): + - Maxime Chambreuil \<\> diff --git a/fieldservice_sign/readme/DESCRIPTION.md b/fieldservice_sign/readme/DESCRIPTION.md new file mode 100644 index 0000000000..ddac536060 --- /dev/null +++ b/fieldservice_sign/readme/DESCRIPTION.md @@ -0,0 +1,12 @@ +This module adds configurable customer signing features on Field Service orders: + +* **On-site signature capture** — uses the native signature widget and the + signature fields already provided by `fieldservice` (`signature`, + `signed_by`, `signed_on`). Capture can be enabled or disabled per company, + and optionally required before an order can be completed. +* **Document signing** — integrates with `sign_oca` to launch a signature + request for a document linked to the order, expose its status on the order, + and optionally require a signed document before completion. + +Both features are independent. An administrator can enable none, either one, +or both from Field Service settings. diff --git a/fieldservice_sign/readme/USAGE.md b/fieldservice_sign/readme/USAGE.md new file mode 100644 index 0000000000..d164d6a756 --- /dev/null +++ b/fieldservice_sign/readme/USAGE.md @@ -0,0 +1,20 @@ +## On-site signature capture + +1. Open a Field Service order. +2. When the order stage requires a signature, use **Sign** to capture the + customer signature. +3. Complete the order. If the company requires a signature, completion is + blocked until one is captured. + +The signature is also printed on the Service Order report when capture is +enabled. + +## Document signing + +1. Open a Field Service order. +2. Click **Request Signature** to create and send a `sign_oca` request from the + company template. +3. Follow the linked signature requests from the smart button or the + **Document Signing** tab. +4. Complete the order. If the company requires a signed document, completion is + blocked until at least one request is signed. diff --git a/fieldservice_sign/report/fsm_order_report_templates.xml b/fieldservice_sign/report/fsm_order_report_templates.xml new file mode 100644 index 0000000000..c64ca14d97 --- /dev/null +++ b/fieldservice_sign/report/fsm_order_report_templates.xml @@ -0,0 +1,32 @@ + + + + + diff --git a/fieldservice_sign/static/description/icon.png b/fieldservice_sign/static/description/icon.png new file mode 100644 index 0000000000..e3757caa15 Binary files /dev/null and b/fieldservice_sign/static/description/icon.png differ diff --git a/fieldservice_sign/static/description/index.html b/fieldservice_sign/static/description/index.html new file mode 100644 index 0000000000..799aa6da6e --- /dev/null +++ b/fieldservice_sign/static/description/index.html @@ -0,0 +1,496 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Field Service - Sign

+ +

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runboat

+

This module adds configurable customer signing features on Field Service +orders:

+
    +
  • On-site signature capture — uses the native signature widget and +the signature fields already provided by fieldservice +(signature, signed_by, signed_on). Capture can be enabled +or disabled per company, and optionally required before an order can +be completed.
  • +
  • Document signing — integrates with sign_oca to launch a +signature request for a document linked to the order, expose its +status on the order, and optionally require a signed document before +completion.
  • +
+

Both features are independent. An administrator can enable none, either +one, or both from Field Service settings.

+

Table of contents

+ +
+

Configuration

+

Go to Field Service > Configuration > Settings and open the +Signatures section:

+
    +
  1. Enable On-site Signature Capture to surface customer signature +capture on orders. Optionally enable Require Signature to +Complete.
  2. +
  3. Enable Document Signing to allow launching sign_oca requests +from orders. Optionally enable Require Document Signed to +Complete.
  4. +
  5. Select the FSM Order Sign Template used when requesting a +document signature. The template model must be fsm.order.
  6. +
+
+
+

Usage

+
+

On-site signature capture

+
    +
  1. Open a Field Service order.
  2. +
  3. When the order stage requires a signature, use Sign to capture +the customer signature.
  4. +
  5. Complete the order. If the company requires a signature, completion +is blocked until one is captured.
  6. +
+

The signature is also printed on the Service Order report when capture +is enabled.

+
+
+

Document signing

+
    +
  1. Open a Field Service order.
  2. +
  3. Click Request Signature to create and send a sign_oca request +from the company template.
  4. +
  5. Follow the linked signature requests from the smart button or the +Document Signing tab.
  6. +
  7. Complete the order. If the company requires a signed document, +completion is blocked until at least one request is signed.
  8. +
+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Gray Matter Logic
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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.

+

Current maintainer:

+

max3903

+

This module is part of the OCA/field-service project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/fieldservice_sign/tests/__init__.py b/fieldservice_sign/tests/__init__.py new file mode 100644 index 0000000000..4c76e864b6 --- /dev/null +++ b/fieldservice_sign/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_fsm_order_sign diff --git a/fieldservice_sign/tests/test_fsm_order_sign.py b/fieldservice_sign/tests/test_fsm_order_sign.py new file mode 100644 index 0000000000..a2f58a8ebc --- /dev/null +++ b/fieldservice_sign/tests/test_fsm_order_sign.py @@ -0,0 +1,171 @@ +# Copyright (C) 2026 Gray Matter Logic (). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 + +from odoo.exceptions import UserError +from odoo.tools.misc import file_open + +from odoo.addons.fieldservice.tests.test_fsm_common import FSMCommon + +TEST_IMAGE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACklEQVR4nGP4DwABAQEAGN2N9wAAAABJRU5ErkJggg==" # noqa: E501 + + +class TestFieldserviceSign(FSMCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.company = cls.env.company + cls.order = cls.env["fsm.order"].create({"location_id": cls.test_location.id}) + with file_open("sign_oca/tests/empty.pdf", "rb") as pdf_file: + pdf_data = base64.b64encode(pdf_file.read()) + cls.sign_role = cls.env["sign.oca.role"].create( + { + "name": "FSM Test Customer", + "partner_selection_policy": "expression", + "expression_partner": "{{object.location_id.owner_id.id}}", + } + ) + cls.template = cls.env["sign.oca.template"].create( + { + "name": "FSM Order Test Template", + "model_id": cls.env["ir.model"]._get_id("fsm.order"), + "data": pdf_data, + "item_ids": [ + ( + 0, + 0, + { + "field_id": cls.env.ref("sign_oca.sign_field_signature").id, + "role_id": cls.sign_role.id, + "page": 1, + "position_x": 10, + "position_y": 10, + "width": 30, + "height": 4, + "required": True, + }, + ) + ], + } + ) + cls.company.write( + { + "fsm_signature_capture": False, + "fsm_document_signing": False, + "fsm_require_signature_to_complete": False, + "fsm_require_document_signed_to_complete": False, + "fsm_order_sign_oca_template_id": False, + } + ) + + def test_complete_without_guards(self): + self.order.action_complete() + self.assertEqual( + self.order.stage_id, + self.env.ref("fieldservice.fsm_stage_completed"), + ) + + def test_require_signature_to_complete(self): + self.company.write( + { + "fsm_signature_capture": True, + "fsm_require_signature_to_complete": True, + } + ) + with self.assertRaisesRegex(UserError, "Customer signature is required"): + self.order.action_complete() + self.order.write( + { + "signed_by": "Test Customer", + "signature": TEST_IMAGE_BASE64, + } + ) + self.order.action_complete() + self.assertEqual( + self.order.stage_id, + self.env.ref("fieldservice.fsm_stage_completed"), + ) + + def test_request_document_signature_disabled(self): + with self.assertRaisesRegex(UserError, "Document signing is disabled"): + self.order.action_request_document_signature() + + def test_request_document_signature_without_template(self): + self.company.fsm_document_signing = True + with self.assertRaisesRegex(UserError, "Configure an FSM Order Sign Template"): + self.order.action_request_document_signature() + + def test_request_document_signature(self): + self.company.write( + { + "fsm_document_signing": True, + "fsm_order_sign_oca_template_id": self.template.id, + } + ) + action = self.order.action_request_document_signature() + self.assertEqual(self.order.sign_request_count, 1) + request = self.order.sign_request_id + self.assertTrue(request) + self.assertEqual(request.fsm_order_id, self.order) + self.assertEqual(request.state, "0_sent") + self.assertIn( + self.test_location.owner_id, + request.signer_ids.partner_id, + ) + self.assertEqual(self.order.sign_request_state, "0_sent") + self.assertEqual( + action["domain"], + [("id", "in", self.order.sign_request_ids.ids)], + ) + + def test_require_document_signed_to_complete(self): + self.company.write( + { + "fsm_document_signing": True, + "fsm_require_document_signed_to_complete": True, + "fsm_order_sign_oca_template_id": self.template.id, + } + ) + self.order.action_request_document_signature() + with self.assertRaisesRegex(UserError, "A signed document is required"): + self.order.action_complete() + self.order.sign_request_id.state = "2_signed" + self.order.action_complete() + self.assertEqual( + self.order.stage_id, + self.env.ref("fieldservice.fsm_stage_completed"), + ) + + def test_action_view_sign_requests(self): + self.company.write( + { + "fsm_document_signing": True, + "fsm_order_sign_oca_template_id": self.template.id, + } + ) + self.order.action_request_document_signature() + action = self.order.action_view_sign_requests() + self.assertEqual( + action["domain"], + [("id", "in", self.order.sign_request_ids.ids)], + ) + self.assertEqual( + action["context"]["default_record_ref"], + f"fsm.order,{self.order.id}", + ) + + def test_compute_fsm_order_id_clears_non_order_refs(self): + self.company.write( + { + "fsm_document_signing": True, + "fsm_order_sign_oca_template_id": self.template.id, + } + ) + self.order.action_request_document_signature() + request = self.order.sign_request_id + self.assertEqual(request.fsm_order_id, self.order) + partner = self.test_partner + request.record_ref = f"{partner._name},{partner.id}" + self.assertFalse(request.fsm_order_id) diff --git a/fieldservice_sign/views/fsm_order_views.xml b/fieldservice_sign/views/fsm_order_views.xml new file mode 100644 index 0000000000..d1e32d6dab --- /dev/null +++ b/fieldservice_sign/views/fsm_order_views.xml @@ -0,0 +1,78 @@ + + + + + fsm.order.form.inherit.fieldservice.sign + fsm.order + + + + + + + + + + +
+ +
+ + not fsm_signature_capture or (not require_signature and not signed_by and not signature and not signed_on) + + + + + + + + + + + + + + + + +
+
+
diff --git a/fieldservice_sign/views/res_config_settings_views.xml b/fieldservice_sign/views/res_config_settings_views.xml new file mode 100644 index 0000000000..6e854dea21 --- /dev/null +++ b/fieldservice_sign/views/res_config_settings_views.xml @@ -0,0 +1,69 @@ + + + + + res.config.settings.view.form.inherit.fieldservice.sign + res.config.settings + + + + + + +
+
+ +
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+
diff --git a/fieldservice_sign/views/sign_oca_request_views.xml b/fieldservice_sign/views/sign_oca_request_views.xml new file mode 100644 index 0000000000..a89c3545ae --- /dev/null +++ b/fieldservice_sign/views/sign_oca_request_views.xml @@ -0,0 +1,54 @@ + + + + + sign.oca.request.search.inherit.fieldservice.sign + sign.oca.request + + + + + + + + + + + + + sign.oca.request.form.inherit.fieldservice.sign + sign.oca.request + + + + + + + + + + sign.oca.request.list.inherit.fieldservice.sign + sign.oca.request + + + + + + + +