From bb6c97f5e8091ced004109ce40152b9ba5428659 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Thu, 18 Dec 2025 21:46:05 +0530 Subject: [PATCH 1/4] [ADD] sign_stamp: add stamp field in sign app Purpose: The Stamp field allows users to apply an official company stamp during the document signing process. Technical changes: add new stamp field in sign.item.type Configured a multi-line placeholder to display structured stamp information (Company, Address, City, Country, VAT Number) --- sign_stamp/__init__.py | 0 sign_stamp/__manifest__.py | 14 ++++++++++++++ sign_stamp/data/sign_data.xml | 12 ++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 sign_stamp/__init__.py create mode 100644 sign_stamp/__manifest__.py create mode 100644 sign_stamp/data/sign_data.xml diff --git a/sign_stamp/__init__.py b/sign_stamp/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sign_stamp/__manifest__.py b/sign_stamp/__manifest__.py new file mode 100644 index 00000000000..85dd576afec --- /dev/null +++ b/sign_stamp/__manifest__.py @@ -0,0 +1,14 @@ +{ + 'author': 'Odoo S.A.', + 'name': 'Sign Stamp', + 'description': """ + add stamp field in sign app + """, + 'data': [ + 'data/sign_data.xml', + ], + 'depends': ['sign'], + 'license': 'LGPL-3', + 'application': True, + 'installable': True +} diff --git a/sign_stamp/data/sign_data.xml b/sign_stamp/data/sign_data.xml new file mode 100644 index 00000000000..9ac2653316a --- /dev/null +++ b/sign_stamp/data/sign_data.xml @@ -0,0 +1,12 @@ + + + + Test Stamp + stamp + stamp + Company Address City Country VAT Number + 0.3 + 0.1 + fa-circle + + From 35b5462ebae958914e3729b143dfa7d054783655 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Fri, 19 Dec 2025 18:36:54 +0530 Subject: [PATCH 2/4] [IMP] sign_stamp: add view for input fields Technical Changes: inherit sign._doc_sign template and add input fields for company, address, city, country, VAT Number --- sign_stamp/__manifest__.py | 3 +- sign_stamp/views/sign_request_templates.xml | 32 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 sign_stamp/views/sign_request_templates.xml diff --git a/sign_stamp/__manifest__.py b/sign_stamp/__manifest__.py index 85dd576afec..4b8ec0fd3cc 100644 --- a/sign_stamp/__manifest__.py +++ b/sign_stamp/__manifest__.py @@ -2,10 +2,11 @@ 'author': 'Odoo S.A.', 'name': 'Sign Stamp', 'description': """ - add stamp field in sign app + add stamp field in sing app """, 'data': [ 'data/sign_data.xml', + 'views/sign_request_templates.xml' ], 'depends': ['sign'], 'license': 'LGPL-3', diff --git a/sign_stamp/views/sign_request_templates.xml b/sign_stamp/views/sign_request_templates.xml new file mode 100644 index 00000000000..d364e8b1948 --- /dev/null +++ b/sign_stamp/views/sign_request_templates.xml @@ -0,0 +1,32 @@ + + + + From f2407795f9b13503928e671b0a7f984d50f1858f Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Mon, 22 Dec 2025 13:01:21 +0530 Subject: [PATCH 3/4] [IMP] sign_stamp: add company stamp support for users Technical Changes: - stamp_sign_stamp and stamp_sign_stamp_frame field in res.users - Implemented and methods in to fetch user stamp and frame. - Allows displaying the company stamp and frame in signature dialogs for sign requests. --- sign_stamp/__init__.py | 1 + sign_stamp/models/__init__.py | 2 ++ sign_stamp/models/res_user.py | 18 ++++++++++++++++++ sign_stamp/models/sign_request_item.py | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 sign_stamp/models/__init__.py create mode 100644 sign_stamp/models/res_user.py create mode 100644 sign_stamp/models/sign_request_item.py diff --git a/sign_stamp/__init__.py b/sign_stamp/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/sign_stamp/__init__.py +++ b/sign_stamp/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sign_stamp/models/__init__.py b/sign_stamp/models/__init__.py new file mode 100644 index 00000000000..e97cd7d1936 --- /dev/null +++ b/sign_stamp/models/__init__.py @@ -0,0 +1,2 @@ +from . import res_user +from . import sign_request_item diff --git a/sign_stamp/models/res_user.py b/sign_stamp/models/res_user.py new file mode 100644 index 00000000000..d2c6331afdf --- /dev/null +++ b/sign_stamp/models/res_user.py @@ -0,0 +1,18 @@ +from odoo import models, fields + +STAMP_USER_FIELDS = ["stamp_sign_stamp", "stamp_sign_stamp_frame"] + + +class ResUsers(models.Model): + _inherit = "res.users" + + @property + def SELF_READABLE_FIELDS(self): + return super().SELF_READABLE_FIELDS + STAMP_USER_FIELDS + + @property + def SELF_WRITEABLE_FIELDS(self): + return super().SELF_WRITEABLE_FIELDS + STAMP_USER_FIELDS + + stamp_sign_stamp = fields.Binary(string="Company Stamp", copy=False, groups="base.group_user") + stamp_sign_stamp_frame = fields.Binary(string="Company Stamp Frame", copy=False, groups="base.group_user") diff --git a/sign_stamp/models/sign_request_item.py b/sign_stamp/models/sign_request_item.py new file mode 100644 index 00000000000..11dee43f924 --- /dev/null +++ b/sign_stamp/models/sign_request_item.py @@ -0,0 +1,19 @@ +from odoo import models + + +class SignRequestItem(models.Model): + _inherit = "sign.request.item" + + def _get_user_stamp(self, stamp_type='stamp_sign_stamp'): + self.ensure_one() + stamp_user = self.partner_id.user_ids[:1] + if stamp_user and stamp_type in ['stamp_sign_stamp']: + return stamp_user[stamp_type] + return False + + def _get_user_stamp_frame(self, stamp_type='stamp_sign_stamp_frame'): + self.ensure_one() + stamp_user = self.partner_id.user_ids[:1] + if stamp_user and stamp_type in ['stamp_sign_stamp_frame']: + return stamp_user[stamp_type] + return False From 2b41e68390aa6c17aa314f8e79389a2030b9853c Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Tue, 23 Dec 2025 11:31:54 +0530 Subject: [PATCH 4/4] [IMP] sign_stamp: add test dialog box and fetch company data for auto-fill Technical Changes: add stamp_sign_stamp and stamp_sign_stamp_frame in res.users for store stamp data fetch company data in sign_request page add test dialog box when user click stamp then test dialog box is open --- sign_stamp/__init__.py | 1 + sign_stamp/__manifest__.py | 12 +++++- sign_stamp/controllers/__init__.py | 1 + sign_stamp/controllers/main.py | 40 +++++++++++++++++++ sign_stamp/data/sign_data.xml | 1 - .../sign_request/document_signable.js | 16 ++++++++ .../sign_request/signable_PDF_iframe.js | 17 ++++++++ .../components/sign_request/test_dialog.js | 7 ++++ .../components/sign_request/test_dialog.xml | 18 +++++++++ sign_stamp/views/sign_request_templates.xml | 8 ++-- 10 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 sign_stamp/controllers/__init__.py create mode 100644 sign_stamp/controllers/main.py create mode 100644 sign_stamp/static/src/components/sign_request/document_signable.js create mode 100644 sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js create mode 100644 sign_stamp/static/src/components/sign_request/test_dialog.js create mode 100644 sign_stamp/static/src/components/sign_request/test_dialog.xml diff --git a/sign_stamp/__init__.py b/sign_stamp/__init__.py index 0650744f6bc..91c5580fed3 100644 --- a/sign_stamp/__init__.py +++ b/sign_stamp/__init__.py @@ -1 +1,2 @@ +from . import controllers from . import models diff --git a/sign_stamp/__manifest__.py b/sign_stamp/__manifest__.py index 4b8ec0fd3cc..ddee539efa2 100644 --- a/sign_stamp/__manifest__.py +++ b/sign_stamp/__manifest__.py @@ -4,11 +4,21 @@ 'description': """ add stamp field in sing app """, + 'depends': ['sign', 'web'], 'data': [ 'data/sign_data.xml', 'views/sign_request_templates.xml' ], - 'depends': ['sign'], + "assets": { + "web.assets_backend": [ + "sign_stamp/static/src/components/sign_request/**/*.js", + "sign_stamp/static/src/components/sign_request/**/*.xml", + ], + "sign.assets_public_sign": [ + "sign_stamp/static/src/components/sign_request/**/*.js", + "sign_stamp/static/src/components/sign_request/**/*.xml", + ], + }, 'license': 'LGPL-3', 'application': True, 'installable': True diff --git a/sign_stamp/controllers/__init__.py b/sign_stamp/controllers/__init__.py new file mode 100644 index 00000000000..12a7e529b67 --- /dev/null +++ b/sign_stamp/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/sign_stamp/controllers/main.py b/sign_stamp/controllers/main.py new file mode 100644 index 00000000000..f29f02fa11e --- /dev/null +++ b/sign_stamp/controllers/main.py @@ -0,0 +1,40 @@ +from odoo import http +from odoo.addons.sign.controllers.main import Sign + + +class Sign(Sign): + def get_document_qweb_context(self, sign_request_id, token, **post): + result = super().get_document_qweb_context(sign_request_id, token, **post) + context = result.get('rendering_context', {}) + current_request_item = context.get('current_request_item') + sign_item_types = context.get('sign_item_types') + company_logo = http.request.env.user.company_id.logo + if company_logo: + context['logo'] = "data:image/png;base64,%s" % company_logo.decode() + else: + context['logo'] = False + if current_request_item and sign_item_types: + user_stamp = current_request_item._get_user_stamp('stamp_sign_stamp') + user_stamp_frame = current_request_item._get_user_stamp_frame('stamp_sign_stamp_frame') + encoded_stamp = ("data:image/png;base64,%s" % user_stamp.decode() if user_stamp else False) + encoded_frame = ("data:image/png;base64,%s" % user_stamp_frame.decode() if user_stamp_frame else False) + for item_type in sign_item_types: + if item_type.get('item_type') == 'stamp': + item_type['auto_value'] = encoded_stamp + item_type['frame_value'] = encoded_frame + break + result['rendering_context'] = context + return result + + @http.route(["/sign/update_user_signature"], type="jsonrpc", auth="user") + def update_signature(self, sign_request_id, role, signature_type=None, datas=None, frame_datas=None): + sign_request_item_sudo = http.request.env['sign.request.item'].sudo().search([('sign_request_id', '=', sign_request_id), ('role_id', '=', role)], limit=1) + user = http.request.env.user + if not user: + return False + allowed = sign_request_item_sudo.partner_id.id == user.partner_id.id + if not allowed or signature_type not in ['sign_signature', 'sign_initials', 'stamp_sign_stamp']: + return False + user[signature_type] = datas[datas.find(',') + 1:] + user[signature_type + '_frame'] = frame_datas[frame_datas.find(',') + 1:] if frame_datas else False + return True diff --git a/sign_stamp/data/sign_data.xml b/sign_stamp/data/sign_data.xml index 9ac2653316a..3eeee42c5db 100644 --- a/sign_stamp/data/sign_data.xml +++ b/sign_stamp/data/sign_data.xml @@ -3,7 +3,6 @@ Test Stamp stamp - stamp Company Address City Country VAT Number 0.3 0.1 diff --git a/sign_stamp/static/src/components/sign_request/document_signable.js b/sign_stamp/static/src/components/sign_request/document_signable.js new file mode 100644 index 00000000000..1a121096623 --- /dev/null +++ b/sign_stamp/static/src/components/sign_request/document_signable.js @@ -0,0 +1,16 @@ +import { patch } from "@web/core/utils/patch"; +import { Document } from "@sign/components/sign_request/document_signable"; + +patch(Document.prototype, { + getDataFromHTML() { + super.getDataFromHTML(); + const { el: parentEl } = this.props.parent; + this.signerInfo = {}; + this.signerInfo.company = parentEl.querySelector("#o_sign_signer_company_input_info")?.value; + this.signerInfo.address = parentEl.querySelector("#o_sign_signer_address_input_info")?.value; + this.signerInfo.city = parentEl.querySelector("#o_sign_signer_city_input_info")?.value; + this.signerInfo.country = parentEl.querySelector("#o_sign_signer_country_input_info")?.value; + this.signerInfo.vat = parentEl.querySelector("#o_sign_signer_vat_input_info")?.value; + console.log(this.signerInfo) + } +}); diff --git a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js new file mode 100644 index 00000000000..aa1dd17ba56 --- /dev/null +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -0,0 +1,17 @@ +import { patch } from "@web/core/utils/patch"; +import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; +import { TestDialog } from "./test_dialog"; + +patch(SignablePDFIframe.prototype, { + enableCustom(signItem) { + super.enableCustom(signItem); + const signItemType = this.signItemTypesById[signItem.data.type_id]; + if (signItemType.item_type !== "stamp") { + return; + } + signItem.el.addEventListener("click", (ev) => { + console.log("STAMP CLICKED => opening dialog"); + this.env.services.dialog.add(TestDialog); + }); + }, +}); diff --git a/sign_stamp/static/src/components/sign_request/test_dialog.js b/sign_stamp/static/src/components/sign_request/test_dialog.js new file mode 100644 index 00000000000..9b5e0f18877 --- /dev/null +++ b/sign_stamp/static/src/components/sign_request/test_dialog.js @@ -0,0 +1,7 @@ +import { Component } from "@odoo/owl"; +import { Dialog } from "@web/core/dialog/dialog"; + +export class TestDialog extends Component { + static template = "sign_stamp.test_dialog"; + static components = { Dialog }; +} diff --git a/sign_stamp/static/src/components/sign_request/test_dialog.xml b/sign_stamp/static/src/components/sign_request/test_dialog.xml new file mode 100644 index 00000000000..b2635623ae1 --- /dev/null +++ b/sign_stamp/static/src/components/sign_request/test_dialog.xml @@ -0,0 +1,18 @@ + + + + +
+ Test Dialog +
+
+ By signing, I agree that the chosen signature/initials will be a valid electronic representation of my hand-written signature/initials for all purposes when it is used on documents, including legally binding contracts. +
+ + + + + +
+
+
diff --git a/sign_stamp/views/sign_request_templates.xml b/sign_stamp/views/sign_request_templates.xml index d364e8b1948..1af0f2b11cd 100644 --- a/sign_stamp/views/sign_request_templates.xml +++ b/sign_stamp/views/sign_request_templates.xml @@ -10,22 +10,22 @@