Skip to content
Merged
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
145 changes: 145 additions & 0 deletions fieldservice_sign/README.rst
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/OCA/field-service/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/field-service/issues/new?body=module:%20fieldservice_sign%0Aversion:%2019.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
-------

* Gray Matter Logic

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

- `Gray Matter Logic <https://www.graymatterlogic.com>`__:

- Maxime Chambreuil <maxime.chambreuil@graymatterlogic.com>

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 <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-max3903|

This module is part of the `OCA/field-service <https://github.com/OCA/field-service/tree/19.0/fieldservice_sign>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions fieldservice_sign/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (C) 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import models
29 changes: 29 additions & 0 deletions fieldservice_sign/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (C) 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
# 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,
}
8 changes: 8 additions & 0 deletions fieldservice_sign/demo/fsm_order.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="fsm_order_sign_demo" model="fsm.order">
<field name="location_id" ref="fieldservice.test_location" />
</record>
</odoo>
13 changes: 13 additions & 0 deletions fieldservice_sign/demo/res_company.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="base.main_company" model="res.company">
<field name="fsm_signature_capture" eval="True" />
<field name="fsm_document_signing" eval="True" />
<field
name="fsm_order_sign_oca_template_id"
ref="sign_oca_template_fsm_order_demo"
/>
</record>
</odoo>
10 changes: 10 additions & 0 deletions fieldservice_sign/demo/sign_oca_role.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="sign_role_fsm_order_customer" model="sign.oca.role">
<field name="name">FSM Order Customer</field>
<field name="partner_selection_policy">expression</field>
<field name="expression_partner">{{object.location_id.owner_id.id}}</field>
</record>
</odoo>
10 changes: 10 additions & 0 deletions fieldservice_sign/demo/sign_oca_template.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="sign_oca_template_fsm_order_demo" model="sign.oca.template">
<field name="name">FSM Order Approval</field>
<field name="model_id" search="[('model', '=', 'fsm.order')]" />
<field name="data" type="base64" file="sign_oca/tests/empty.pdf" />
</record>
</odoo>
16 changes: 16 additions & 0 deletions fieldservice_sign/demo/sign_oca_template_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="sign_oca_template_fsm_order_demo_item" model="sign.oca.template.item">
<field name="template_id" ref="sign_oca_template_fsm_order_demo" />
<field name="field_id" ref="sign_oca.sign_field_signature" />
<field name="role_id" ref="sign_role_fsm_order_customer" />
<field name="page">1</field>
<field name="position_x">10</field>
<field name="position_y">10</field>
<field name="width">30</field>
<field name="height">4</field>
<field name="required" eval="True" />
</record>
</odoo>
7 changes: 7 additions & 0 deletions fieldservice_sign/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (C) 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
# 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
117 changes: 117 additions & 0 deletions fieldservice_sign/models/fsm_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Copyright (C) 2026 Gray Matter Logic (<https://www.graymatterlogic.com>).
# 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()
Loading
Loading