From bb6c97f5e8091ced004109ce40152b9ba5428659 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Thu, 18 Dec 2025 21:46:05 +0530 Subject: [PATCH 01/11] [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 02/11] [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 03/11] [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 a29f51dc48010e3b4cac1394ab0ae559ba83488d Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Tue, 23 Dec 2025 11:31:54 +0530 Subject: [PATCH 04/11] [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 | 10 ++++- sign_stamp/controllers/__init__.py | 1 + sign_stamp/controllers/main.py | 40 +++++++++++++++++++ sign_stamp/data/sign_data.xml | 1 - .../sign_request/document_signable.js | 17 ++++++++ .../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 | 13 ++++-- 10 files changed, 119 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..a1c0779cd1a 100644 --- a/sign_stamp/__manifest__.py +++ b/sign_stamp/__manifest__.py @@ -4,11 +4,19 @@ '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.assets_public_sign': [ + 'sign_stamp/static/src/components/**/*', + ], + }, '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..915057817dd --- /dev/null +++ b/sign_stamp/static/src/components/sign_request/document_signable.js @@ -0,0 +1,17 @@ +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.companyInfo = {}; + this.companyInfo.company = parentEl.querySelector("#o_sign_signer_company_input_info")?.value; + this.companyInfo.address = parentEl.querySelector("#o_sign_signer_address_input_info")?.value; + this.companyInfo.city = parentEl.querySelector("#o_sign_signer_city_input_info")?.value; + this.companyInfo.country = parentEl.querySelector("#o_sign_signer_country_input_info")?.value; + this.companyInfo.vat = parentEl.querySelector("#o_sign_signer_vat_input_info")?.value; + this.companyInfo.logo = parentEl.querySelector("#o_sign_signer_logo_input_info")?.value; + console.log(this.companyInfo) + }, +}); 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..5f7e425f039 100644 --- a/sign_stamp/views/sign_request_templates.xml +++ b/sign_stamp/views/sign_request_templates.xml @@ -10,23 +10,28 @@ + From ddc8804cf7106f20844052c753a348cf05595137 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Tue, 23 Dec 2025 16:54:55 +0530 Subject: [PATCH 05/11] [IMP] sign_stamp: add company details fields in dialog box fields are auto-fill - add Input fields in dialog box - Extend getIframeProps to include companyInfo in iframe props - Pass company details (name, address, city, country, VAT) when opening stamp dialog - Prefill stamp adoption dialog inputs with company information --- sign_stamp/__manifest__.py | 6 ++ .../sign_request/document_signable.js | 10 +++ .../sign_request/signable_PDF_iframe.js | 10 ++- .../components/sign_request/test_dialog.xml | 18 ------ .../sign_request => dialogs}/test_dialog.js | 0 sign_stamp/static/src/dialogs/test_dialog.xml | 64 +++++++++++++++++++ 6 files changed, 88 insertions(+), 20 deletions(-) delete mode 100644 sign_stamp/static/src/components/sign_request/test_dialog.xml rename sign_stamp/static/src/{components/sign_request => dialogs}/test_dialog.js (100%) create mode 100644 sign_stamp/static/src/dialogs/test_dialog.xml diff --git a/sign_stamp/__manifest__.py b/sign_stamp/__manifest__.py index a1c0779cd1a..560b438cf57 100644 --- a/sign_stamp/__manifest__.py +++ b/sign_stamp/__manifest__.py @@ -12,9 +12,15 @@ 'assets': { 'web.assets_backend': [ 'sign_stamp/static/src/components/**/*', + 'sign_stamp/static/src/dialogs/**/*', + ], + 'web.assets_frontend': [ + 'sign_stamp/static/src/components/**/*', + 'sign_stamp/static/src/dialogs/**/*', ], 'sign.assets_public_sign': [ 'sign_stamp/static/src/components/**/*', + 'sign_stamp/static/src/dialogs/**/*', ], }, 'license': 'LGPL-3', diff --git a/sign_stamp/static/src/components/sign_request/document_signable.js b/sign_stamp/static/src/components/sign_request/document_signable.js index 915057817dd..a3df3b526e9 100644 --- a/sign_stamp/static/src/components/sign_request/document_signable.js +++ b/sign_stamp/static/src/components/sign_request/document_signable.js @@ -14,4 +14,14 @@ patch(Document.prototype, { this.companyInfo.logo = parentEl.querySelector("#o_sign_signer_logo_input_info")?.value; console.log(this.companyInfo) }, + getIframeProps(sign_document_id) { + const props = super.getIframeProps(sign_document_id); + const document = this.documents.find((doc) => doc.id === sign_document_id); + console.log("ADD") + console.log(this.companyInfo) + return { + ...props, + companyInfo: this.companyInfo + }; + }, }); 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 index aa1dd17ba56..6fb9648c93f 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -1,6 +1,6 @@ import { patch } from "@web/core/utils/patch"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; -import { TestDialog } from "./test_dialog"; +import { TestDialog } from "../../dialogs/test_dialog"; patch(SignablePDFIframe.prototype, { enableCustom(signItem) { @@ -11,7 +11,13 @@ patch(SignablePDFIframe.prototype, { } signItem.el.addEventListener("click", (ev) => { console.log("STAMP CLICKED => opening dialog"); - this.env.services.dialog.add(TestDialog); + this.env.services.dialog.add(TestDialog,{ + companyName: this.props.companyInfo.company, + companyAddress: this.props.companyInfo.address, + companyCity: this.props.companyInfo.city, + companyCountry: this.props.companyInfo.country, + companyVat: this.props.companyInfo.vat + }); }); }, }); diff --git a/sign_stamp/static/src/components/sign_request/test_dialog.xml b/sign_stamp/static/src/components/sign_request/test_dialog.xml deleted file mode 100644 index b2635623ae1..00000000000 --- a/sign_stamp/static/src/components/sign_request/test_dialog.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - -
- 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/static/src/components/sign_request/test_dialog.js b/sign_stamp/static/src/dialogs/test_dialog.js similarity index 100% rename from sign_stamp/static/src/components/sign_request/test_dialog.js rename to sign_stamp/static/src/dialogs/test_dialog.js diff --git a/sign_stamp/static/src/dialogs/test_dialog.xml b/sign_stamp/static/src/dialogs/test_dialog.xml new file mode 100644 index 00000000000..4fe66c91385 --- /dev/null +++ b/sign_stamp/static/src/dialogs/test_dialog.xml @@ -0,0 +1,64 @@ + + + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ 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. +
+ + + + + +
+
+
From 4dbfbc0d8dfd927ac4601fa1bef1b6d32ffeaa2d Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Wed, 24 Dec 2025 14:39:40 +0530 Subject: [PATCH 06/11] [IMP] sign_stamp: auto-fill stamp and update according to dialog box details user can see company information in stamp default when user click stamp then dialog box open and user can edit company information --- .../sign_request/document_signable.js | 2 +- .../sign_request/signable_PDF_iframe.js | 37 ++++++++++++++++++- sign_stamp/static/src/dialogs/test_dialog.xml | 24 +++++++----- sign_stamp/views/sign_request_templates.xml | 6 +-- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/sign_stamp/static/src/components/sign_request/document_signable.js b/sign_stamp/static/src/components/sign_request/document_signable.js index a3df3b526e9..63b8007db78 100644 --- a/sign_stamp/static/src/components/sign_request/document_signable.js +++ b/sign_stamp/static/src/components/sign_request/document_signable.js @@ -14,10 +14,10 @@ patch(Document.prototype, { this.companyInfo.logo = parentEl.querySelector("#o_sign_signer_logo_input_info")?.value; console.log(this.companyInfo) }, + getIframeProps(sign_document_id) { const props = super.getIframeProps(sign_document_id); const document = this.documents.find((doc) => doc.id === sign_document_id); - console.log("ADD") console.log(this.companyInfo) return { ...props, 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 index 6fb9648c93f..70f5bf7525e 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -9,14 +9,47 @@ patch(SignablePDFIframe.prototype, { if (signItemType.item_type !== "stamp") { return; } + + signItem.el.value = ` + ${this.props.companyInfo.company} + ${this.props.companyInfo.address} + ${this.props.companyInfo.city} + ${this.props.companyInfo.country} + ${this.props.companyInfo.vat} + ` + signItem.el.addEventListener("click", (ev) => { console.log("STAMP CLICKED => opening dialog"); - this.env.services.dialog.add(TestDialog,{ + this.env.services.dialog.add(TestDialog, { companyName: this.props.companyInfo.company, companyAddress: this.props.companyInfo.address, companyCity: this.props.companyInfo.city, companyCountry: this.props.companyInfo.country, - companyVat: this.props.companyInfo.vat + companyVat: this.props.companyInfo.vat, + signItemEl: signItem.el, + onCancel() { + this.props.close(); + }, + OnSign() { + const companyNameInput = document.querySelector('#company_name_input')?.value || "" + const companyAddressInput = document.querySelector('#company_address_input')?.value || "" + const companyCityInput = document.querySelector('#company_city_input')?.value || "" + const companyCountryInput = document.querySelector('#company_country_input')?.value || "" + const companyVatInput = document.querySelector('#company_vat_input')?.value || "" + this.companyName = companyNameInput + this.companyAddress = companyAddressInput + this.companyCity = companyCityInput + this.companyCountry = companyCountryInput + this.companyVat = companyVatInput + signItem.el.value = ` + ${this.companyName} + ${this.companyAddress} + ${this.companyCity} + ${this.companyCountry} + ${this.companyVat} + ` + this.props.close(); + }, }); }); }, diff --git a/sign_stamp/static/src/dialogs/test_dialog.xml b/sign_stamp/static/src/dialogs/test_dialog.xml index 4fe66c91385..69608793b36 100644 --- a/sign_stamp/static/src/dialogs/test_dialog.xml +++ b/sign_stamp/static/src/dialogs/test_dialog.xml @@ -4,7 +4,8 @@
-
-
-
-
-
- @@ -55,9 +61,9 @@ 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 5f7e425f039..f111f019c5e 100644 --- a/sign_stamp/views/sign_request_templates.xml +++ b/sign_stamp/views/sign_request_templates.xml @@ -27,11 +27,11 @@ type="hidden" t-att-value="current_request_item.partner_id.company_id.partner_id.vat if current_request_item and current_request_item.partner_id else None" /> - + t-att-value="current_request_item.partner_id.company_id.logo_web if current_request_item and current_request_item.partner_id else None" + /> From ae5b3b8573a78ab773035b9d422459882df79b5e Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Mon, 29 Dec 2025 15:45:00 +0530 Subject: [PATCH 07/11] [IMP] sign_stamp: add preview box for stamp add preview box for stamp --- .../sign_request/signable_PDF_iframe.js | 97 ++++++++++--------- .../static/src/dialogs/name_and_sign.js | 63 ++++++++++++ .../static/src/dialogs/name_and_sing.xml | 37 +++++++ sign_stamp/static/src/dialogs/stamp_dialog.js | 18 ++++ .../static/src/dialogs/stamp_dialog.xml | 47 +++++++++ sign_stamp/static/src/dialogs/test_dialog.js | 7 -- sign_stamp/static/src/dialogs/test_dialog.xml | 70 ------------- 7 files changed, 216 insertions(+), 123 deletions(-) create mode 100644 sign_stamp/static/src/dialogs/name_and_sign.js create mode 100644 sign_stamp/static/src/dialogs/name_and_sing.xml create mode 100644 sign_stamp/static/src/dialogs/stamp_dialog.js create mode 100644 sign_stamp/static/src/dialogs/stamp_dialog.xml delete mode 100644 sign_stamp/static/src/dialogs/test_dialog.js delete mode 100644 sign_stamp/static/src/dialogs/test_dialog.xml 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 index 70f5bf7525e..f54351fc74d 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -1,56 +1,61 @@ import { patch } from "@web/core/utils/patch"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; -import { TestDialog } from "../../dialogs/test_dialog"; +import { _t } from "@web/core/l10n/translation"; +import { SignNameAndSignatureDialog } from "@sign/dialogs/dialogs"; +import { StampSignDetailsDialog } from "../../dialogs/stamp_dialog";; patch(SignablePDFIframe.prototype, { enableCustom(signItem) { super.enableCustom(signItem); - const signItemType = this.signItemTypesById[signItem.data.type_id]; - if (signItemType.item_type !== "stamp") { + const signItemElement = signItem.el; + const signItemData = signItem.data; + const signItemType = this.signItemTypesById[signItemData.type_id]; + const { name, item_type: type, auto_value: autoValue } = signItemType; + if (type === _t("stamp")) { + signItemElement.addEventListener("click", (e) => { + this.handleSignatureDialogClick(e.currentTarget, signItemType); + }); + } + }, + openSignatureDialog(signatureItem, type) { + if (this.dialogOpen) { return; } - - signItem.el.value = ` - ${this.props.companyInfo.company} - ${this.props.companyInfo.address} - ${this.props.companyInfo.city} - ${this.props.companyInfo.country} - ${this.props.companyInfo.vat} - ` - - signItem.el.addEventListener("click", (ev) => { - console.log("STAMP CLICKED => opening dialog"); - this.env.services.dialog.add(TestDialog, { - companyName: this.props.companyInfo.company, - companyAddress: this.props.companyInfo.address, - companyCity: this.props.companyInfo.city, - companyCountry: this.props.companyInfo.country, - companyVat: this.props.companyInfo.vat, - signItemEl: signItem.el, - onCancel() { - this.props.close(); - }, - OnSign() { - const companyNameInput = document.querySelector('#company_name_input')?.value || "" - const companyAddressInput = document.querySelector('#company_address_input')?.value || "" - const companyCityInput = document.querySelector('#company_city_input')?.value || "" - const companyCountryInput = document.querySelector('#company_country_input')?.value || "" - const companyVatInput = document.querySelector('#company_vat_input')?.value || "" - this.companyName = companyNameInput - this.companyAddress = companyAddressInput - this.companyCity = companyCityInput - this.companyCountry = companyCountryInput - this.companyVat = companyVatInput - signItem.el.value = ` - ${this.companyName} - ${this.companyAddress} - ${this.companyCity} - ${this.companyCountry} - ${this.companyVat} - ` - this.props.close(); + const signature = { + name: this.signerName || "", + company: this.props.companyInfo?.company || "", + address: this.props.companyInfo?.address || "", + city: this.props.companyInfo?.city || "", + country: this.props.companyInfo?.country || "", + vat: this.props.companyInfo?.vat || "", + }; + const frame = {}; + const { height, width } = signatureItem.getBoundingClientRect(); + const signFrame = signatureItem.querySelector(".o_sign_frame"); + this.dialogOpen = true; + const signatureImage = signatureItem?.dataset?.signature; + this.closeFn = this.dialog.add( + type.item_type === "stamp" + ? StampSignDetailsDialog + : SignNameAndSignatureDialog, + { + frame, + signature, + signatureType: type.item_type, + displaySignatureRatio: width / height, + activeFrame: Boolean(signFrame) || !type.auto_value, + mode: "auto", + defaultFrame: type.frame_value || "", + hash: this.frameHash, + signatureImage, + onConfirm: () => {}, + onConfirmAll: () => {}, + }, + { + onClose: () => { + this.dialogOpen = false; }, - }); - }); - }, + } + ); + } }); diff --git a/sign_stamp/static/src/dialogs/name_and_sign.js b/sign_stamp/static/src/dialogs/name_and_sign.js new file mode 100644 index 00000000000..110c04bf323 --- /dev/null +++ b/sign_stamp/static/src/dialogs/name_and_sign.js @@ -0,0 +1,63 @@ +import { renderToString } from "@web/core/utils/render"; +import { patch } from "@web/core/utils/patch"; +import { NameAndSignature } from "@web/core/signature/name_and_signature"; +import { rpc } from "@web/core/network/rpc"; +import { onWillStart } from "@odoo/owl"; + +patch(NameAndSignature.prototype, { + setup() { + super.setup(...arguments); + onWillStart(async () => { + this.Notofonts = await rpc(`/web/sign/get_fonts/NotoSans-Reg.ttf`); + }); + }, + + async drawCurrentName() { + if (this.props.signatureType === "stamp") { + const font = this.Notofonts; + const stamp = this.getStampDetails(); + const canvas = this.signatureRef.el; + const img = this.getSVGStamp(font, stamp, canvas.width, canvas.height); + await this.printImage(img); + } else { + super.drawCurrentName(); + } + }, + getStampDetails() { + return { + name: this.props.signature.name, + company: this.props.signature.company, + address: this.props.signature.address, + city: this.props.signature.city, + country: this.props.signature.country, + vat: this.props.signature.vat, + logo: this.props.signature.logo, + }; + }, + + /** + * Gets an SVG matching the given parameters, output compatible with the + * src attribute of . + * + * @private + * @param {string} font: base64 encoded font to use + * @param {string} text: the name to draw + * @param {number} width: the width of the resulting image in px + * @param {number} height: the height of the resulting image in px + * @returns {string} image = mimetype + image data + */ + getSVGStamp(font, stampData, width, height) { + const svg = renderToString("stamp_sign.sign_svg_stamp", { + width: width, + height: height, + font: font, + name: stampData.name, + company: stampData.company, + address: stampData.address, + city: stampData.city, + country: stampData.country, + vat: stampData.vat, + }); + return "data:image/svg+xml," + encodeURI(svg); + }, +}); diff --git a/sign_stamp/static/src/dialogs/name_and_sing.xml b/sign_stamp/static/src/dialogs/name_and_sing.xml new file mode 100644 index 00000000000..3462b4d80a9 --- /dev/null +++ b/sign_stamp/static/src/dialogs/name_and_sing.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.js b/sign_stamp/static/src/dialogs/stamp_dialog.js new file mode 100644 index 00000000000..d0adf8abc4c --- /dev/null +++ b/sign_stamp/static/src/dialogs/stamp_dialog.js @@ -0,0 +1,18 @@ +import { Dialog } from "@web/core/dialog/dialog"; +import { SignNameAndSignature, SignNameAndSignatureDialog } from "@sign/dialogs/sign_name_and_signature_dialog"; + +export class StampSignDetails extends SignNameAndSignature { + static template = "stamp_sign.StampSignDetails"; +} + +export class StampSignDetailsDialog extends SignNameAndSignatureDialog { + static template = "stamp_sign.StampSignDetailsDialog"; + + static components = { Dialog, StampSignDetails }; + + get dialogProps() { + return { + title: "Adopt Your Stamp", + }; + } +} diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.xml b/sign_stamp/static/src/dialogs/stamp_dialog.xml new file mode 100644 index 00000000000..93bf88aa849 --- /dev/null +++ b/sign_stamp/static/src/dialogs/stamp_dialog.xml @@ -0,0 +1,47 @@ + + + + +
+ By clicking Adopt & Sign, 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/static/src/dialogs/test_dialog.js b/sign_stamp/static/src/dialogs/test_dialog.js deleted file mode 100644 index 9b5e0f18877..00000000000 --- a/sign_stamp/static/src/dialogs/test_dialog.js +++ /dev/null @@ -1,7 +0,0 @@ -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/dialogs/test_dialog.xml b/sign_stamp/static/src/dialogs/test_dialog.xml deleted file mode 100644 index 69608793b36..00000000000 --- a/sign_stamp/static/src/dialogs/test_dialog.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - -
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- 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. -
- - - - - -
-
-
From cbb21c950fbc82c8fc6333277229ed172f00794b Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Tue, 30 Dec 2025 10:15:47 +0530 Subject: [PATCH 08/11] [IMP] sign_stamp: add sign and sign all button in stamp dialog add sign and sign all button in stamp dialog --- sign_stamp/controllers/main.py | 8 +- .../sign_request/document_signable.js | 1 - .../src/components/sign_request/sign_item.xml | 17 ++ .../sign_request/signable_PDF_iframe.js | 168 +++++++++++++++--- .../static/src/dialogs/name_and_sign.js | 47 ++--- .../static/src/dialogs/name_and_sing.xml | 14 +- sign_stamp/static/src/dialogs/stamp_dialog.js | 28 +++ .../static/src/dialogs/stamp_dialog.xml | 39 ++-- sign_stamp/views/sign_request_templates.xml | 5 - 9 files changed, 253 insertions(+), 74 deletions(-) create mode 100644 sign_stamp/static/src/components/sign_request/sign_item.xml diff --git a/sign_stamp/controllers/main.py b/sign_stamp/controllers/main.py index f29f02fa11e..666963f2a51 100644 --- a/sign_stamp/controllers/main.py +++ b/sign_stamp/controllers/main.py @@ -1,5 +1,7 @@ from odoo import http from odoo.addons.sign.controllers.main import Sign +import logging +_logger = logging.getLogger(__name__) class Sign(Sign): @@ -28,12 +30,12 @@ def get_document_qweb_context(self, sign_request_id, token, **post): @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) + if signature_type == "stamp_sign": + signature_type = "stamp_sign_stamp" 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']: + if 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 diff --git a/sign_stamp/static/src/components/sign_request/document_signable.js b/sign_stamp/static/src/components/sign_request/document_signable.js index 63b8007db78..753a6a7af75 100644 --- a/sign_stamp/static/src/components/sign_request/document_signable.js +++ b/sign_stamp/static/src/components/sign_request/document_signable.js @@ -11,7 +11,6 @@ patch(Document.prototype, { this.companyInfo.city = parentEl.querySelector("#o_sign_signer_city_input_info")?.value; this.companyInfo.country = parentEl.querySelector("#o_sign_signer_country_input_info")?.value; this.companyInfo.vat = parentEl.querySelector("#o_sign_signer_vat_input_info")?.value; - this.companyInfo.logo = parentEl.querySelector("#o_sign_signer_logo_input_info")?.value; console.log(this.companyInfo) }, diff --git a/sign_stamp/static/src/components/sign_request/sign_item.xml b/sign_stamp/static/src/components/sign_request/sign_item.xml new file mode 100644 index 00000000000..6403535ccf0 --- /dev/null +++ b/sign_stamp/static/src/components/sign_request/sign_item.xml @@ -0,0 +1,17 @@ + + + + + + + + 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 index f54351fc74d..e0e6f39bf5a 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -1,39 +1,33 @@ +import { user } from "@web/core/user"; import { patch } from "@web/core/utils/patch"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; import { _t } from "@web/core/l10n/translation"; import { SignNameAndSignatureDialog } from "@sign/dialogs/dialogs"; -import { StampSignDetailsDialog } from "../../dialogs/stamp_dialog";; +import { StampSignDetailsDialog } from "../../dialogs/stamp_dialog"; +import { rpc } from "@web/core/network/rpc"; patch(SignablePDFIframe.prototype, { enableCustom(signItem) { super.enableCustom(signItem); - const signItemElement = signItem.el; - const signItemData = signItem.data; - const signItemType = this.signItemTypesById[signItemData.type_id]; - const { name, item_type: type, auto_value: autoValue } = signItemType; - if (type === _t("stamp")) { - signItemElement.addEventListener("click", (e) => { - this.handleSignatureDialogClick(e.currentTarget, signItemType); - }); + const signItemType = this.signItemTypesById[signItem.data.type_id]; + if (!signItemType || signItemType.item_type !== "stamp") { + return; } + signItem.el.addEventListener("click", (e) => { + this.openSignatureDialog(e.currentTarget, signItemType); + }); }, + openSignatureDialog(signatureItem, type) { if (this.dialogOpen) { return; } - const signature = { - name: this.signerName || "", - company: this.props.companyInfo?.company || "", - address: this.props.companyInfo?.address || "", - city: this.props.companyInfo?.city || "", - country: this.props.companyInfo?.country || "", - vat: this.props.companyInfo?.vat || "", - }; + const { signature, signMode, signatureImage } = this._prepareSignatureData(signatureItem, type); + console.log(signature) const frame = {}; const { height, width } = signatureItem.getBoundingClientRect(); const signFrame = signatureItem.querySelector(".o_sign_frame"); this.dialogOpen = true; - const signatureImage = signatureItem?.dataset?.signature; this.closeFn = this.dialog.add( type.item_type === "stamp" ? StampSignDetailsDialog @@ -44,12 +38,16 @@ patch(SignablePDFIframe.prototype, { signatureType: type.item_type, displaySignatureRatio: width / height, activeFrame: Boolean(signFrame) || !type.auto_value, - mode: "auto", + mode: signMode, defaultFrame: type.frame_value || "", hash: this.frameHash, signatureImage, - onConfirm: () => {}, - onConfirmAll: () => {}, + onConfirm: async () => { + this._handleConfirm(signature, frame, signatureItem, type) + }, + onConfirmAll: async () => { + this._handleConfirmAll(signature, frame, type) + }, }, { onClose: () => { @@ -57,5 +55,131 @@ patch(SignablePDFIframe.prototype, { }, } ); - } + }, + + _prepareSignatureData(signatureItem, type) { + const signature = { + name: this.signerName || "", + company: this.props.companyInfo?.company || "", + address: this.props.companyInfo?.address || "", + city: this.props.companyInfo?.city || "", + country: this.props.companyInfo?.country || "", + vat: this.props.companyInfo?.vat || "", + image: type.auto_value || null, + }; + + const signatureImage = signatureItem?.dataset?.signature; + const signMode = "auto"; + return { signature, signMode, signatureImage }; + }, + + async _handleConfirm(signature, frame, signatureItem, type) { + if (!signature.isSignatureEmpty && signature.signatureChanged) { + await this._applySignature(signature, frame, signatureItem, type); + } else if (signature.signatureChanged) { + this._resetSignatureItem(signatureItem, type); + } + this.closeDialog(); + this.handleInput(); + }, + + async _handleConfirmAll(signature, frame, type) { + this.signerName = signature.name; + await frame.updateFrame(); + + const frameData = frame.getFrameImageSrc(); + const signatureSrc = signature.getSignatureImage(); + type.auto_value = signatureSrc; + type.frame_value = frameData; + + if (user.userId) { + await this.updateUserSignature(type); + } + + await this._fillAllMatchingItems(signatureSrc, frameData, type); + this.closeDialog(); + this.handleInput(); + }, + + async _applySignature(signature, frame, signatureItem, type) { + this.signerName = signature.name; + + await frame.updateFrame(); + + const frameData = frame.getFrameImageSrc(); + const stampImage = signature.getSignatureImage(); + + type.auto_value = stampImage; + type.frame_value = frameData; + + if (user.userId) { + await this.updateUserSignature(type); + } + signatureItem.dataset.signature = stampImage; + signatureItem.dataset.frame = frameData; + + this.fillItemWithSignature(signatureItem, stampImage, { + frame: frameData, + hash: this.frameHash, + }); + }, + + _resetSignatureItem(signatureItem, type) { + delete signatureItem.dataset.signature; + delete signatureItem.dataset.frame; + signatureItem.replaceChildren(); + + const signHelperspan = document.createElement("span"); + signHelperspan.classList.add("o_sign_helper"); + signatureItem.append(signHelperspan); + + if (type.placeholder) { + const placeholderSpan = document.createElement("span"); + placeholderSpan.classList.add("o_placeholder"); + placeholderSpan.innerText = type.placeholder; + signatureItem.append(placeholderSpan); + } + }, + + async _fillAllMatchingItems(signatureSrc, frameData, type) { + for (const page in this.signItems) { + await Promise.all( + Object.values(this.signItems[page]).reduce((promises, signItem) => { + if ( + signItem.data.responsible === this.currentRole && + signItem.data.type_id === type.id + ) { + promises.push( + Promise.all([ + this.adjustSignatureSize(signatureSrc, signItem.el), + this.adjustSignatureSize(frameData, signItem.el), + ]).then(([data, adjustedFrame]) => { + this.fillItemWithSignature(signItem.el, data, { + frame: adjustedFrame, + hash: this.frameHash, + }); + }) + ); + } + return promises; + }, []) + ); + } + }, + + updateUserSignature(type) { + const signature_type = + type.item_type === "signature" + ? "sign_signature" + : type.item_type === "stamp" + ? "stamp_sign_stamp" + : "sign_initials"; + return rpc("/sign/update_user_signature", { + sign_request_id: this.props.requestID, + role: this.currentRole, + signature_type: signature_type, + datas: type.auto_value, + frame_datas: type.frame_value, + }); + }, }); diff --git a/sign_stamp/static/src/dialogs/name_and_sign.js b/sign_stamp/static/src/dialogs/name_and_sign.js index 110c04bf323..10ff335b2de 100644 --- a/sign_stamp/static/src/dialogs/name_and_sign.js +++ b/sign_stamp/static/src/dialogs/name_and_sign.js @@ -13,25 +13,27 @@ patch(NameAndSignature.prototype, { }, async drawCurrentName() { + console.log("signatureType:", this.props.signatureType); if (this.props.signatureType === "stamp") { - const font = this.Notofonts; - const stamp = this.getStampDetails(); - const canvas = this.signatureRef.el; - const img = this.getSVGStamp(font, stamp, canvas.width, canvas.height); - await this.printImage(img); + const font = this.Notofonts; + const stamp = this.getStampDetails(); + const canvas = this.signatureRef.el; + const img = this.getSVGStamp(font, stamp, canvas.width, canvas.height); + await this.printImage(img); } else { - super.drawCurrentName(); + super.drawCurrentName(); } }, getStampDetails() { return { - name: this.props.signature.name, - company: this.props.signature.company, - address: this.props.signature.address, - city: this.props.signature.city, - country: this.props.signature.country, - vat: this.props.signature.vat, - logo: this.props.signature.logo, + name: this.props.signature.name, + company: this.props.signature.company, + address: this.props.signature.address, + city: this.props.signature.city, + country: this.props.signature.country, + vat: this.props.signature.vat, + logo: this.props.signature.logo, + image: this.props.signature.image, }; }, @@ -48,15 +50,16 @@ patch(NameAndSignature.prototype, { */ getSVGStamp(font, stampData, width, height) { const svg = renderToString("stamp_sign.sign_svg_stamp", { - width: width, - height: height, - font: font, - name: stampData.name, - company: stampData.company, - address: stampData.address, - city: stampData.city, - country: stampData.country, - vat: stampData.vat, + width: width, + height: height, + font: font, + name: stampData.name, + company: stampData.company, + address: stampData.address, + city: stampData.city, + country: stampData.country, + vat: stampData.vat, + image: stampData.image, }); return "data:image/svg+xml," + encodeURI(svg); }, diff --git a/sign_stamp/static/src/dialogs/name_and_sing.xml b/sign_stamp/static/src/dialogs/name_and_sing.xml index 3462b4d80a9..a692c71f34c 100644 --- a/sign_stamp/static/src/dialogs/name_and_sing.xml +++ b/sign_stamp/static/src/dialogs/name_and_sing.xml @@ -14,23 +14,27 @@ - + - + - + - + - + + + + + diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.js b/sign_stamp/static/src/dialogs/stamp_dialog.js index d0adf8abc4c..603e9411b78 100644 --- a/sign_stamp/static/src/dialogs/stamp_dialog.js +++ b/sign_stamp/static/src/dialogs/stamp_dialog.js @@ -3,6 +3,34 @@ import { SignNameAndSignature, SignNameAndSignatureDialog } from "@sign/dialogs/ export class StampSignDetails extends SignNameAndSignature { static template = "stamp_sign.StampSignDetails"; + + triggerFileUpload() { + const fileInput = document.querySelector("input[name='logo']"); + if (fileInput) { + fileInput.click(); + } + } + + onInputStampDetails(ev) { + const field = ev.target.name; + if (field === "logo") { + const file = ev.target.files[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = (e) => { + this.props.signature.image = e.target.result; + this.drawCurrentName(); + }; + reader.readAsDataURL(file); + return; + } + const value = ev.target.value; + if (field && this.props.signature?.hasOwnProperty(field)) { + this.props.signature[field] = value; + this.drawCurrentName(); + } + } } export class StampSignDetailsDialog extends SignNameAndSignatureDialog { diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.xml b/sign_stamp/static/src/dialogs/stamp_dialog.xml index 93bf88aa849..ed177f8e87b 100644 --- a/sign_stamp/static/src/dialogs/stamp_dialog.xml +++ b/sign_stamp/static/src/dialogs/stamp_dialog.xml @@ -1,15 +1,15 @@ +
- By clicking Adopt & Sign, I agree that the chosen signature/initials will be a - valid electronic representation of my hand-written signature/initials for all + By clicking Adopt & Sign, I agree that the chosen signature/initials/stamp will be a + valid electronic representation of my hand-written signature/initials/stamp for all purposes when it is used on documents, including legally binding contracts.
- + @@ -19,28 +19,35 @@
- - + +
- - + +
- - + +
- - + +
- - + +
- - + + +
+
+ +
+ + +
diff --git a/sign_stamp/views/sign_request_templates.xml b/sign_stamp/views/sign_request_templates.xml index f111f019c5e..1af0f2b11cd 100644 --- a/sign_stamp/views/sign_request_templates.xml +++ b/sign_stamp/views/sign_request_templates.xml @@ -27,11 +27,6 @@ type="hidden" t-att-value="current_request_item.partner_id.company_id.partner_id.vat if current_request_item and current_request_item.partner_id else None" /> - From 2d0a44305ca618dbafdd780dd658a5c566b6aa41 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Tue, 30 Dec 2025 14:09:41 +0530 Subject: [PATCH 09/11] [IMP] sign_stamp: remove signall button from stamp remove signall button from stamp and also remove onConfirmAll function Now primary button is Sign --- sign_stamp/controllers/main.py | 5 +- sign_stamp/models/sign_request_item.py | 16 +- .../sign_request/document_signable.js | 3 - .../sign_request/signable_PDF_iframe.js | 153 ++++-------------- .../static/src/dialogs/name_and_sign.js | 2 +- sign_stamp/static/src/dialogs/stamp_dialog.js | 4 +- .../static/src/dialogs/stamp_dialog.xml | 17 +- 7 files changed, 52 insertions(+), 148 deletions(-) diff --git a/sign_stamp/controllers/main.py b/sign_stamp/controllers/main.py index 666963f2a51..c0f2aa56918 100644 --- a/sign_stamp/controllers/main.py +++ b/sign_stamp/controllers/main.py @@ -1,10 +1,9 @@ from odoo import http from odoo.addons.sign.controllers.main import Sign -import logging -_logger = logging.getLogger(__name__) 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', {}) @@ -30,8 +29,6 @@ def get_document_qweb_context(self, sign_request_id, token, **post): @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): - if signature_type == "stamp_sign": - signature_type = "stamp_sign_stamp" user = http.request.env.user if not user: return False diff --git a/sign_stamp/models/sign_request_item.py b/sign_stamp/models/sign_request_item.py index 11dee43f924..7aaea93a3d5 100644 --- a/sign_stamp/models/sign_request_item.py +++ b/sign_stamp/models/sign_request_item.py @@ -4,16 +4,16 @@ class SignRequestItem(models.Model): _inherit = "sign.request.item" - def _get_user_stamp(self, stamp_type='stamp_sign_stamp'): + def _get_user_stamp(self, signature_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] + sign_user = self.partner_id.user_ids[:1] + if sign_user and signature_type in ['stamp_sign_stamp']: + return sign_user[signature_type] return False - def _get_user_stamp_frame(self, stamp_type='stamp_sign_stamp_frame'): + def _get_user_stamp_frame(self, signature_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] + sign_user = self.partner_id.user_ids[:1] + if sign_user and signature_type in ['stamp_sign_stamp_frame']: + return sign_user[signature_type] return False diff --git a/sign_stamp/static/src/components/sign_request/document_signable.js b/sign_stamp/static/src/components/sign_request/document_signable.js index 753a6a7af75..d5371ba2ba7 100644 --- a/sign_stamp/static/src/components/sign_request/document_signable.js +++ b/sign_stamp/static/src/components/sign_request/document_signable.js @@ -11,13 +11,10 @@ patch(Document.prototype, { this.companyInfo.city = parentEl.querySelector("#o_sign_signer_city_input_info")?.value; this.companyInfo.country = parentEl.querySelector("#o_sign_signer_country_input_info")?.value; this.companyInfo.vat = parentEl.querySelector("#o_sign_signer_vat_input_info")?.value; - console.log(this.companyInfo) }, getIframeProps(sign_document_id) { const props = super.getIframeProps(sign_document_id); - const document = this.documents.find((doc) => doc.id === sign_document_id); - console.log(this.companyInfo) return { ...props, companyInfo: this.companyInfo 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 index e0e6f39bf5a..06dc1f9dd6b 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -1,10 +1,8 @@ import { user } from "@web/core/user"; import { patch } from "@web/core/utils/patch"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; -import { _t } from "@web/core/l10n/translation"; import { SignNameAndSignatureDialog } from "@sign/dialogs/dialogs"; import { StampSignDetailsDialog } from "../../dialogs/stamp_dialog"; -import { rpc } from "@web/core/network/rpc"; patch(SignablePDFIframe.prototype, { enableCustom(signItem) { @@ -23,15 +21,12 @@ patch(SignablePDFIframe.prototype, { return; } const { signature, signMode, signatureImage } = this._prepareSignatureData(signatureItem, type); - console.log(signature) const frame = {}; const { height, width } = signatureItem.getBoundingClientRect(); const signFrame = signatureItem.querySelector(".o_sign_frame"); this.dialogOpen = true; this.closeFn = this.dialog.add( - type.item_type === "stamp" - ? StampSignDetailsDialog - : SignNameAndSignatureDialog, + type.item_type === "stamp" ? StampSignDetailsDialog : SignNameAndSignatureDialog, { frame, signature, @@ -43,10 +38,37 @@ patch(SignablePDFIframe.prototype, { hash: this.frameHash, signatureImage, onConfirm: async () => { - this._handleConfirm(signature, frame, signatureItem, type) - }, - onConfirmAll: async () => { - this._handleConfirmAll(signature, frame, type) + if (!signature.isSignatureEmpty && signature.signatureChanged) { + const signatureName = signature.name; + this.props.updateSignerName(signatureName); + await frame.updateFrame(); + const frameData = frame.getFrameImageSrc(); + const signatureSrc = signature.getSignatureImage(); + type.auto_value = signatureSrc; + type.frame_value = frameData; + if (user.userId) { + await this.updateUserSignature(type); + } + this.fillItemWithSignature(signatureItem, signatureSrc, { + frame: frameData, + hash: this.frameHash, + }); + } else if (signature.signatureChanged) { + delete signatureItem.dataset.signature; + delete signatureItem.dataset.frame; + signatureItem.replaceChildren(); + const signHelperSpan = document.createElement("span"); + signHelperSpan.classList.add("o_sign_helper"); + signatureItem.append(signHelperSpan); + if (type.placeholder) { + const placeholderSpan = document.createElement("span"); + placeholderSpan.classList.add("o_placeholder"); + placeholderSpan.innerText = type.placeholder; + signatureItem.append(placeholderSpan); + } + } + this.closeDialog(); + this.handleInput(); }, }, { @@ -67,119 +89,8 @@ patch(SignablePDFIframe.prototype, { vat: this.props.companyInfo?.vat || "", image: type.auto_value || null, }; - const signatureImage = signatureItem?.dataset?.signature; const signMode = "auto"; return { signature, signMode, signatureImage }; }, - - async _handleConfirm(signature, frame, signatureItem, type) { - if (!signature.isSignatureEmpty && signature.signatureChanged) { - await this._applySignature(signature, frame, signatureItem, type); - } else if (signature.signatureChanged) { - this._resetSignatureItem(signatureItem, type); - } - this.closeDialog(); - this.handleInput(); - }, - - async _handleConfirmAll(signature, frame, type) { - this.signerName = signature.name; - await frame.updateFrame(); - - const frameData = frame.getFrameImageSrc(); - const signatureSrc = signature.getSignatureImage(); - type.auto_value = signatureSrc; - type.frame_value = frameData; - - if (user.userId) { - await this.updateUserSignature(type); - } - - await this._fillAllMatchingItems(signatureSrc, frameData, type); - this.closeDialog(); - this.handleInput(); - }, - - async _applySignature(signature, frame, signatureItem, type) { - this.signerName = signature.name; - - await frame.updateFrame(); - - const frameData = frame.getFrameImageSrc(); - const stampImage = signature.getSignatureImage(); - - type.auto_value = stampImage; - type.frame_value = frameData; - - if (user.userId) { - await this.updateUserSignature(type); - } - signatureItem.dataset.signature = stampImage; - signatureItem.dataset.frame = frameData; - - this.fillItemWithSignature(signatureItem, stampImage, { - frame: frameData, - hash: this.frameHash, - }); - }, - - _resetSignatureItem(signatureItem, type) { - delete signatureItem.dataset.signature; - delete signatureItem.dataset.frame; - signatureItem.replaceChildren(); - - const signHelperspan = document.createElement("span"); - signHelperspan.classList.add("o_sign_helper"); - signatureItem.append(signHelperspan); - - if (type.placeholder) { - const placeholderSpan = document.createElement("span"); - placeholderSpan.classList.add("o_placeholder"); - placeholderSpan.innerText = type.placeholder; - signatureItem.append(placeholderSpan); - } - }, - - async _fillAllMatchingItems(signatureSrc, frameData, type) { - for (const page in this.signItems) { - await Promise.all( - Object.values(this.signItems[page]).reduce((promises, signItem) => { - if ( - signItem.data.responsible === this.currentRole && - signItem.data.type_id === type.id - ) { - promises.push( - Promise.all([ - this.adjustSignatureSize(signatureSrc, signItem.el), - this.adjustSignatureSize(frameData, signItem.el), - ]).then(([data, adjustedFrame]) => { - this.fillItemWithSignature(signItem.el, data, { - frame: adjustedFrame, - hash: this.frameHash, - }); - }) - ); - } - return promises; - }, []) - ); - } - }, - - updateUserSignature(type) { - const signature_type = - type.item_type === "signature" - ? "sign_signature" - : type.item_type === "stamp" - ? "stamp_sign_stamp" - : "sign_initials"; - return rpc("/sign/update_user_signature", { - sign_request_id: this.props.requestID, - role: this.currentRole, - signature_type: signature_type, - datas: type.auto_value, - frame_datas: type.frame_value, - }); - }, }); diff --git a/sign_stamp/static/src/dialogs/name_and_sign.js b/sign_stamp/static/src/dialogs/name_and_sign.js index 10ff335b2de..0be55cdf981 100644 --- a/sign_stamp/static/src/dialogs/name_and_sign.js +++ b/sign_stamp/static/src/dialogs/name_and_sign.js @@ -13,7 +13,6 @@ patch(NameAndSignature.prototype, { }, async drawCurrentName() { - console.log("signatureType:", this.props.signatureType); if (this.props.signatureType === "stamp") { const font = this.Notofonts; const stamp = this.getStampDetails(); @@ -24,6 +23,7 @@ patch(NameAndSignature.prototype, { super.drawCurrentName(); } }, + getStampDetails() { return { name: this.props.signature.name, diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.js b/sign_stamp/static/src/dialogs/stamp_dialog.js index 603e9411b78..7a822d8b58b 100644 --- a/sign_stamp/static/src/dialogs/stamp_dialog.js +++ b/sign_stamp/static/src/dialogs/stamp_dialog.js @@ -15,8 +15,8 @@ export class StampSignDetails extends SignNameAndSignature { const field = ev.target.name; if (field === "logo") { const file = ev.target.files[0]; - if (!file) return; - + if (!file) + return; const reader = new FileReader(); reader.onload = (e) => { this.props.signature.image = e.target.result; diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.xml b/sign_stamp/static/src/dialogs/stamp_dialog.xml index ed177f8e87b..9c3cfbef974 100644 --- a/sign_stamp/static/src/dialogs/stamp_dialog.xml +++ b/sign_stamp/static/src/dialogs/stamp_dialog.xml @@ -9,8 +9,7 @@ purposes when it is used on documents, including legally binding contracts. - - +
@@ -20,32 +19,32 @@
- +
- +
- +
- +
- +
- +
- +
From 94db4cce389524eb6fa581cd2d5b8745038fe1c3 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Wed, 31 Dec 2025 11:13:54 +0530 Subject: [PATCH 10/11] [IMP] sign_stamp: add company stamp support in Sign module Purpose: Add a new Stamp sign item type to the Sign module, allowing companies to apply an official stamp containing company details and logo when validating documents. Technical Changes: 1.data: -Added new sign.item.type entry for stamp in sign_data.xml with placeholder. 2.views: - Extended sign._doc_sign to inject company-related fields (company, address, city, country, VAT) into the DOM. - Introduced sign_request_templates.xml for provide company details to the frontend. 3.Frontend: - Patched document_signable.js to fetch company details from the DOM and pass them to the iframe. - Extended signable_PDF_iframe.js to: Detect stamp items, open a custom stamp dialog, handle confirm/cancel actions 4.Dialogs & Signature Rendering: - Added StampSignDetailsDialog for capturing stamp information and logo upload. - Extended NameAndSignature to generate a dynamic SVG stamp using company data and optional logo. - Ensured real-time preview updates on input changes. --- sign_stamp/__init__.py | 2 - sign_stamp/__manifest__.py | 6 ++- sign_stamp/controllers/__init__.py | 1 - sign_stamp/controllers/main.py | 39 ------------------- sign_stamp/data/sign_data.xml | 2 +- sign_stamp/models/__init__.py | 2 - sign_stamp/models/res_user.py | 18 --------- sign_stamp/models/sign_request_item.py | 19 --------- .../sign_request/signable_PDF_iframe.js | 35 ++++++----------- .../static/src/dialogs/name_and_sign.js | 23 +---------- .../static/src/dialogs/name_and_sing.xml | 14 +++---- .../static/src/dialogs/stamp_dialog.xml | 32 +++++++-------- sign_stamp/views/sign_request_templates.xml | 30 +++----------- 13 files changed, 45 insertions(+), 178 deletions(-) delete mode 100644 sign_stamp/__init__.py delete mode 100644 sign_stamp/controllers/__init__.py delete mode 100644 sign_stamp/controllers/main.py delete mode 100644 sign_stamp/models/__init__.py delete mode 100644 sign_stamp/models/res_user.py delete mode 100644 sign_stamp/models/sign_request_item.py diff --git a/sign_stamp/__init__.py b/sign_stamp/__init__.py deleted file mode 100644 index 91c5580fed3..00000000000 --- a/sign_stamp/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import controllers -from . import models diff --git a/sign_stamp/__manifest__.py b/sign_stamp/__manifest__.py index 560b438cf57..842d7bc24ba 100644 --- a/sign_stamp/__manifest__.py +++ b/sign_stamp/__manifest__.py @@ -2,7 +2,11 @@ 'author': 'Odoo S.A.', 'name': 'Sign Stamp', 'description': """ - add stamp field in sing app + Adds a Company Stamp feature to the Sign application. + This module introduces a new Stamp sign item that allows companies to + apply an official stamp containing company details such as name, address, + VAT number, and logo. The stamp is rendered dynamically and can be used + as a valid electronic signature when signing documents. """, 'depends': ['sign', 'web'], 'data': [ diff --git a/sign_stamp/controllers/__init__.py b/sign_stamp/controllers/__init__.py deleted file mode 100644 index 12a7e529b67..00000000000 --- a/sign_stamp/controllers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import main diff --git a/sign_stamp/controllers/main.py b/sign_stamp/controllers/main.py deleted file mode 100644 index c0f2aa56918..00000000000 --- a/sign_stamp/controllers/main.py +++ /dev/null @@ -1,39 +0,0 @@ -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): - user = http.request.env.user - if not user: - return False - if 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 3eeee42c5db..d5a1aa897c1 100644 --- a/sign_stamp/data/sign_data.xml +++ b/sign_stamp/data/sign_data.xml @@ -1,7 +1,7 @@ - Test Stamp + Stamp stamp Company Address City Country VAT Number 0.3 diff --git a/sign_stamp/models/__init__.py b/sign_stamp/models/__init__.py deleted file mode 100644 index e97cd7d1936..00000000000 --- a/sign_stamp/models/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index d2c6331afdf..00000000000 --- a/sign_stamp/models/res_user.py +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 7aaea93a3d5..00000000000 --- a/sign_stamp/models/sign_request_item.py +++ /dev/null @@ -1,19 +0,0 @@ -from odoo import models - - -class SignRequestItem(models.Model): - _inherit = "sign.request.item" - - def _get_user_stamp(self, signature_type='stamp_sign_stamp'): - self.ensure_one() - sign_user = self.partner_id.user_ids[:1] - if sign_user and signature_type in ['stamp_sign_stamp']: - return sign_user[signature_type] - return False - - def _get_user_stamp_frame(self, signature_type='stamp_sign_stamp_frame'): - self.ensure_one() - sign_user = self.partner_id.user_ids[:1] - if sign_user and signature_type in ['stamp_sign_stamp_frame']: - return sign_user[signature_type] - return False 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 index 06dc1f9dd6b..4a4365d9188 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -1,4 +1,3 @@ -import { user } from "@web/core/user"; import { patch } from "@web/core/utils/patch"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; import { SignNameAndSignatureDialog } from "@sign/dialogs/dialogs"; @@ -20,7 +19,14 @@ patch(SignablePDFIframe.prototype, { if (this.dialogOpen) { return; } - const { signature, signMode, signatureImage } = this._prepareSignatureData(signatureItem, type); + const signature = { + name: this.signerName || "", + company: this.props.companyInfo?.company || "", + address: this.props.companyInfo?.address || "", + city: this.props.companyInfo?.city || "", + country: this.props.companyInfo?.country || "", + vat: this.props.companyInfo?.vat || "", + }; const frame = {}; const { height, width } = signatureItem.getBoundingClientRect(); const signFrame = signatureItem.querySelector(".o_sign_frame"); @@ -33,10 +39,9 @@ patch(SignablePDFIframe.prototype, { signatureType: type.item_type, displaySignatureRatio: width / height, activeFrame: Boolean(signFrame) || !type.auto_value, - mode: signMode, + mode: "auto", defaultFrame: type.frame_value || "", hash: this.frameHash, - signatureImage, onConfirm: async () => { if (!signature.isSignatureEmpty && signature.signatureChanged) { const signatureName = signature.name; @@ -44,18 +49,11 @@ patch(SignablePDFIframe.prototype, { await frame.updateFrame(); const frameData = frame.getFrameImageSrc(); const signatureSrc = signature.getSignatureImage(); - type.auto_value = signatureSrc; - type.frame_value = frameData; - if (user.userId) { - await this.updateUserSignature(type); - } this.fillItemWithSignature(signatureItem, signatureSrc, { frame: frameData, hash: this.frameHash, }); } else if (signature.signatureChanged) { - delete signatureItem.dataset.signature; - delete signatureItem.dataset.frame; signatureItem.replaceChildren(); const signHelperSpan = document.createElement("span"); signHelperSpan.classList.add("o_sign_helper"); @@ -79,18 +77,7 @@ patch(SignablePDFIframe.prototype, { ); }, - _prepareSignatureData(signatureItem, type) { - const signature = { - name: this.signerName || "", - company: this.props.companyInfo?.company || "", - address: this.props.companyInfo?.address || "", - city: this.props.companyInfo?.city || "", - country: this.props.companyInfo?.country || "", - vat: this.props.companyInfo?.vat || "", - image: type.auto_value || null, - }; - const signatureImage = signatureItem?.dataset?.signature; - const signMode = "auto"; - return { signature, signMode, signatureImage }; + getSignatureValueFromElement(item) { + return item.data.type === "stamp" ? item.el.dataset.signature : super.getSignatureValueFromElement(item) }, }); diff --git a/sign_stamp/static/src/dialogs/name_and_sign.js b/sign_stamp/static/src/dialogs/name_and_sign.js index 0be55cdf981..2c0d5a8c225 100644 --- a/sign_stamp/static/src/dialogs/name_and_sign.js +++ b/sign_stamp/static/src/dialogs/name_and_sign.js @@ -1,20 +1,11 @@ import { renderToString } from "@web/core/utils/render"; import { patch } from "@web/core/utils/patch"; import { NameAndSignature } from "@web/core/signature/name_and_signature"; -import { rpc } from "@web/core/network/rpc"; -import { onWillStart } from "@odoo/owl"; patch(NameAndSignature.prototype, { - setup() { - super.setup(...arguments); - onWillStart(async () => { - this.Notofonts = await rpc(`/web/sign/get_fonts/NotoSans-Reg.ttf`); - }); - }, - async drawCurrentName() { if (this.props.signatureType === "stamp") { - const font = this.Notofonts; + const font = this.fonts[this.currentFont]; const stamp = this.getStampDetails(); const canvas = this.signatureRef.el; const img = this.getSVGStamp(font, stamp, canvas.width, canvas.height); @@ -32,22 +23,10 @@ patch(NameAndSignature.prototype, { city: this.props.signature.city, country: this.props.signature.country, vat: this.props.signature.vat, - logo: this.props.signature.logo, image: this.props.signature.image, }; }, - /** - * Gets an SVG matching the given parameters, output compatible with the - * src attribute of . - * - * @private - * @param {string} font: base64 encoded font to use - * @param {string} text: the name to draw - * @param {number} width: the width of the resulting image in px - * @param {number} height: the height of the resulting image in px - * @returns {string} image = mimetype + image data - */ getSVGStamp(font, stampData, width, height) { const svg = renderToString("stamp_sign.sign_svg_stamp", { width: width, diff --git a/sign_stamp/static/src/dialogs/name_and_sing.xml b/sign_stamp/static/src/dialogs/name_and_sing.xml index a692c71f34c..9e9926287c6 100644 --- a/sign_stamp/static/src/dialogs/name_and_sing.xml +++ b/sign_stamp/static/src/dialogs/name_and_sing.xml @@ -1,6 +1,6 @@ - + - + - + - + - + - + - + diff --git a/sign_stamp/static/src/dialogs/stamp_dialog.xml b/sign_stamp/static/src/dialogs/stamp_dialog.xml index 9c3cfbef974..7e9e4b7fe39 100644 --- a/sign_stamp/static/src/dialogs/stamp_dialog.xml +++ b/sign_stamp/static/src/dialogs/stamp_dialog.xml @@ -4,9 +4,7 @@
- By clicking Adopt & Sign, I agree that the chosen signature/initials/stamp will be a - valid electronic representation of my hand-written signature/initials/stamp for all - purposes when it is used on documents, including legally binding contracts. + By clicking Sign, I confirm this stamp will be used as my electronic signature.
@@ -18,33 +16,33 @@
- - + +
- - + +
- - + +
- - + +
- - + +
- - + +
- +
- +
diff --git a/sign_stamp/views/sign_request_templates.xml b/sign_stamp/views/sign_request_templates.xml index 1af0f2b11cd..24c26fd7cd0 100644 --- a/sign_stamp/views/sign_request_templates.xml +++ b/sign_stamp/views/sign_request_templates.xml @@ -2,31 +2,11 @@ From 53d7a9a3b35cfa827b52d4eb099bee761b913697 Mon Sep 17 00:00:00 2001 From: utpat-odoo Date: Fri, 2 Jan 2026 18:08:41 +0530 Subject: [PATCH 11/11] [IMP] sign_stamp: add company name in stamp field Purpose: Ensure that stamp sign items display the current company name automatically, providing consistent and company-aware labeling when using stamps in documents. Technical Changes: - Added a sequence field to control priority and ensure stamp items with the highest priority appear at the top. - Extended the sign.item.type model to compute the stamp name using the active company context. - Implemented the _compute_display_name method to dynamically assign the company name to stamp items. --- sign_stamp/__init__.py | 1 + sign_stamp/data/sign_data.xml | 9 +++++++-- sign_stamp/models/__init__.py | 1 + sign_stamp/models/sign_item_type.py | 17 +++++++++++++++++ .../sign_request/signable_PDF_iframe.js | 13 +------------ 5 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 sign_stamp/__init__.py create mode 100644 sign_stamp/models/__init__.py create mode 100644 sign_stamp/models/sign_item_type.py diff --git a/sign_stamp/__init__.py b/sign_stamp/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/sign_stamp/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sign_stamp/data/sign_data.xml b/sign_stamp/data/sign_data.xml index d5a1aa897c1..074abd25986 100644 --- a/sign_stamp/data/sign_data.xml +++ b/sign_stamp/data/sign_data.xml @@ -1,9 +1,14 @@ + + 0 + + - Stamp + + 0 stamp - Company Address City Country VAT Number + Company Address City Country VAT 0.3 0.1 fa-circle diff --git a/sign_stamp/models/__init__.py b/sign_stamp/models/__init__.py new file mode 100644 index 00000000000..ac5bea22810 --- /dev/null +++ b/sign_stamp/models/__init__.py @@ -0,0 +1 @@ +from . import sign_item_type diff --git a/sign_stamp/models/sign_item_type.py b/sign_stamp/models/sign_item_type.py new file mode 100644 index 00000000000..9a42e5683cb --- /dev/null +++ b/sign_stamp/models/sign_item_type.py @@ -0,0 +1,17 @@ +from odoo import api, fields, models + + +class SignItemType(models.Model): + _inherit = "sign.item.type" + _order = "sequence" + + sequence = fields.Integer(string="Sequence", default=1) + display_name = fields.Char(compute="_compute_display_name") + + @api.depends_context('company') + def _compute_display_name(self): + self.display_name = self.env.company.name + for record in self: + if record.item_type == "stamp" and record.sequence == 0: + record.name = record.display_name + break 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 index 4a4365d9188..8f590493fe4 100644 --- a/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js +++ b/sign_stamp/static/src/components/sign_request/signable_PDF_iframe.js @@ -53,18 +53,7 @@ patch(SignablePDFIframe.prototype, { frame: frameData, hash: this.frameHash, }); - } else if (signature.signatureChanged) { - signatureItem.replaceChildren(); - const signHelperSpan = document.createElement("span"); - signHelperSpan.classList.add("o_sign_helper"); - signatureItem.append(signHelperSpan); - if (type.placeholder) { - const placeholderSpan = document.createElement("span"); - placeholderSpan.classList.add("o_placeholder"); - placeholderSpan.innerText = type.placeholder; - signatureItem.append(placeholderSpan); - } - } + } this.closeDialog(); this.handleInput(); },