From 47a40a442ccaddb9edda737dbf89b6a781c80fc5 Mon Sep 17 00:00:00 2001 From: Antoni Marroig Campomar Date: Fri, 17 May 2024 17:43:33 +0200 Subject: [PATCH] [MIG] sign_oca: Migration to 17.0 > > Co-authored-by: Bernat Puig --- sign_oca/__manifest__.py | 4 +- sign_oca/controllers/main.py | 3 +- .../migrations/16.0.1.1.1/pre-migration.py | 15 ---- sign_oca/models/__init__.py | 1 + sign_oca/models/res_company.py | 14 +++ sign_oca/models/sign_oca_request.py | 86 ++++++++++++------- .../sign_oca_configure.esm.js | 39 +++++---- .../sign_oca_configure/sign_oca_configure.xml | 1 - .../sign_oca_pdf/sign_oca_pdf.esm.js | 9 +- .../sign_oca_pdf/sign_oca_pdf_action.esm.js | 27 +++--- .../sign_oca_pdf_common.esm.js | 17 ++-- .../sign_oca_pdf_common.xml | 2 +- .../sign_oca_pdf_common_action.esm.js | 25 +++--- .../sign_oca_pdf_portal.esm.js | 35 +++++--- .../sign_oca_pdf_portal.xml | 2 +- sign_oca/static/src/elements/check.esm.js | 9 +- sign_oca/static/src/elements/signature.esm.js | 6 +- sign_oca/static/src/elements/text.esm.js | 7 +- sign_oca/static/src/js/main.esm.js | 7 -- sign_oca/static/src/js/sign_oca.esm.js | 18 ++-- .../src/js/signer_menu_container.esm.js | 10 +-- .../static/src/js/signer_menu_view.esm.js | 12 +-- sign_oca/static/src/js/systray.esm.js | 3 +- sign_oca/static/src/js/systray_service.esm.js | 1 + .../static/src/xml/signer_menu_container.xml | 2 +- sign_oca/static/src/xml/systray.xml | 2 +- sign_oca/templates/assets.xml | 1 - sign_oca/tests/test_sign.py | 15 +++- sign_oca/tests/test_sign_portal.py | 31 ++++--- sign_oca/views/res_partner_views.xml | 2 +- sign_oca/views/sign_oca_request.xml | 42 +++++---- sign_oca/views/sign_oca_request_log.xml | 8 +- sign_oca/views/sign_oca_role.xml | 6 +- sign_oca/wizards/__init__.py | 1 + sign_oca/wizards/res_config_settings.py | 12 +++ .../wizards/res_config_settings_views.xml | 29 +++++++ .../wizards/sign_oca_template_generate.xml | 7 +- 37 files changed, 299 insertions(+), 212 deletions(-) delete mode 100644 sign_oca/migrations/16.0.1.1.1/pre-migration.py create mode 100644 sign_oca/models/res_company.py delete mode 100644 sign_oca/static/src/js/main.esm.js create mode 100644 sign_oca/wizards/res_config_settings.py create mode 100644 sign_oca/wizards/res_config_settings_views.xml diff --git a/sign_oca/__manifest__.py b/sign_oca/__manifest__.py index 0e395fb7..b515c739 100644 --- a/sign_oca/__manifest__.py +++ b/sign_oca/__manifest__.py @@ -5,7 +5,7 @@ "name": "Sign Oca", "summary": """ Allow to sign documents inside Odoo CE""", - "version": "16.0.2.0.0", + "version": "17.0.1.0.0", "license": "AGPL-3", "author": "Dixmit,Odoo Community Association (OCA)", "website": "https://github.com/OCA/sign", @@ -14,6 +14,7 @@ "security/security.xml", "views/menu.xml", "data/data.xml", + "wizards/res_config_settings_views.xml", "wizards/sign_oca_template_generate.xml", "wizards/sign_oca_template_generate_multi.xml", "views/res_partner_views.xml", @@ -61,7 +62,6 @@ "sign_oca/static/src/components/sign_oca_pdf_portal/sign_oca_pdf_portal.esm.js", "sign_oca/static/src/components/sign_oca_pdf_portal/sign_oca_pdf_portal.xml", "sign_oca/static/src/scss/portal.scss", - "sign_oca/static/src/js/*.js", "sign_oca/static/src/xml/*.xml", ], "sign_oca.sign_assets": [ diff --git a/sign_oca/controllers/main.py b/sign_oca/controllers/main.py index 920940f5..8e471579 100644 --- a/sign_oca/controllers/main.py +++ b/sign_oca/controllers/main.py @@ -101,5 +101,4 @@ def get_sign_oca_sign_access(self, signer_id, access_token, items): ) except (AccessError, MissingError): return request.redirect("/my") - signer_sudo.action_sign(items, access_token=access_token) - return True + return signer_sudo.action_sign(items, access_token=access_token) diff --git a/sign_oca/migrations/16.0.1.1.1/pre-migration.py b/sign_oca/migrations/16.0.1.1.1/pre-migration.py deleted file mode 100644 index 9daf67cd..00000000 --- a/sign_oca/migrations/16.0.1.1.1/pre-migration.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2024 ForgeFlow -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -import logging - -_logger = logging.getLogger(__name__) - - -def migrate(cr, version): - _logger.info("Change partner_type field to partner_selection_policy.") - query = ( - "ALTER TABLE sign_oca_role RENAME COLUMN" - " 'partner_type' TO 'partner_selection_policy'" - ) - cr.execute(query) diff --git a/sign_oca/models/__init__.py b/sign_oca/models/__init__.py index 32aa7235..1cbaaba0 100644 --- a/sign_oca/models/__init__.py +++ b/sign_oca/models/__init__.py @@ -1,3 +1,4 @@ +from . import res_company from . import res_users from . import res_partner from . import sign_oca_template diff --git a/sign_oca/models/res_company.py b/sign_oca/models/res_company.py new file mode 100644 index 00000000..6b21ca54 --- /dev/null +++ b/sign_oca/models/res_company.py @@ -0,0 +1,14 @@ +# Copyright 2024 ForgeFlow S.L. (http://www.forgeflow.com) +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + sign_oca_send_sign_request_copy = fields.Boolean( + string="Send signers a copy of the final signed document", + help="Once all signers have signed the request, a copy of " + "the final document will be sent to each of them.", + ) diff --git a/sign_oca/models/sign_oca_request.py b/sign_oca/models/sign_oca_request.py index b6300e6b..a601aa0f 100644 --- a/sign_oca/models/sign_oca_request.py +++ b/sign_oca/models/sign_oca_request.py @@ -24,16 +24,12 @@ class SignOcaRequest(models.Model): name = fields.Char(required=True) active = fields.Boolean(default=True) - template_id = fields.Many2one("sign.oca.template", readonly=True) - data = fields.Binary( - required=True, readonly=True, states={"draft": [("readonly", False)]} - ) + template_id = fields.Many2one("sign.oca.template") + data = fields.Binary(required=True) filename = fields.Char() user_id = fields.Many2one( comodel_name="res.users", string="Responsible", - readonly=True, - states={"draft": [("readonly", False)]}, default=lambda self: self.env.user, required=True, ) @@ -45,8 +41,6 @@ class SignOcaRequest(models.Model): ) ], string="Object", - readonly=True, - states={"draft": [("readonly", False)]}, ) signed = fields.Boolean(copy=False) signer_ids = fields.One2many( @@ -54,8 +48,6 @@ class SignOcaRequest(models.Model): inverse_name="request_id", auto_join=True, copy=True, - readonly=True, - states={"draft": [("readonly", False)]}, ) signer_id = fields.Many2one( comodel_name="sign.oca.request.signer", @@ -70,7 +62,6 @@ class SignOcaRequest(models.Model): ("cancel", "Cancelled"), ], default="draft", - readonly=True, required=True, copy=False, tracking=True, @@ -80,16 +71,13 @@ class SignOcaRequest(models.Model): to_sign = fields.Boolean(compute="_compute_to_sign") signatory_data = fields.Serialized( default=lambda r: {}, - readonly=True, copy=False, ) - current_hash = fields.Char(readonly=True, copy=False) + current_hash = fields.Char(copy=False) company_id = fields.Many2one( "res.company", default=lambda r: r.env.company.id, required=True, - readonly=True, - states={"draft": [("readonly", False)]}, ) next_item_id = fields.Integer(compute="_compute_next_item_id") @@ -264,6 +252,40 @@ def action_send(self, sign_now=False, message=""): email_layout_xmlid="mail.mail_notification_light", ) + def action_send_signed_request(self): + self.ensure_one() + if ( + self.state != "signed" + or not self.env.company.sign_oca_send_sign_request_copy + ): + return + for signer in self.signer_ids: + attachments = ( + self.env["ir.attachment"] + .sudo() + .search( + [ + ("res_model", "=", "sign.oca.request"), + ("res_id", "=", self.id), + ("res_field", "=", "data"), + ] + ) + ) + # The message will not be linked to the record because we do not want + # it happen. + self.env["mail.thread"].message_notify( + body=_( + "%(name)s (%(email)s) has sent the signed document.", + name=self.create_uid.name, + email=self.create_uid.email, + ), + partner_ids=signer.partner_id.ids, + subject=_("Signed document"), + subtype_id=self.env.ref("mail.mt_comment").id, + mail_auto_delete=False, + attachment_ids=attachments.ids, + ) + def _check_signed(self): self.ensure_one() if self.state != "sent": @@ -310,8 +332,8 @@ class SignOcaRequestSigner(models.Model): partner_name = fields.Char(related="partner_id.name") partner_id = fields.Many2one("res.partner", required=True, ondelete="restrict") role_id = fields.Many2one("sign.oca.role", required=True, ondelete="restrict") - signed_on = fields.Datetime(readonly=True) - signature_hash = fields.Char(readonly=True) + signed_on = fields.Datetime() + signature_hash = fields.Char() model = fields.Char(compute="_compute_model", store=True) res_id = fields.Integer(compute="_compute_res_id", store=True) is_allow_signature = fields.Boolean(compute="_compute_is_allow_signature") @@ -358,10 +380,10 @@ def get_info(self, access_token=False): "items": self.request_id.signatory_data, "to_sign": self.request_id.to_sign, "partner": { - "id": self.env.user.partner_id.id, - "name": self.env.user.partner_id.name, - "email": self.env.user.partner_id.email, - "phone": self.env.user.partner_id.phone, + "id": self.partner_id.id, + "name": self.partner_id.name, + "email": self.partner_id.email, + "phone": self.partner_id.phone, }, } @@ -422,7 +444,11 @@ def action_sign(self, items, access_token=False): self.signature_hash = final_hash self.request_id._check_signed() self._set_action_log("sign", access_token=access_token) - # TODO: Add a return + self.request_id.action_send_signed_request() + return { + "type": "ir.actions.act_url", + "url": self.access_url, + } def _check_signable(self, item): if not item["required"]: @@ -510,9 +536,9 @@ def _set_action_log(self, action, **kwargs): self.ensure_one() return self.request_id._set_action_log(action, signer_id=self.id, **kwargs) - def name_get(self): - result = [(signer.id, (signer.partner_id.display_name)) for signer in self] - return result + def _compute_display_name(self): + for signer in self: + signer.display_name = signer.partner_id.display_name class SignRequestLog(models.Model): @@ -524,13 +550,10 @@ class SignRequestLog(models.Model): uid = fields.Many2one( "res.users", required=True, - readonly=True, ondelete="cascade", default=lambda r: r.env.user.id, ) - date = fields.Datetime( - required=True, readonly=True, default=lambda r: fields.Datetime.now() - ) + date = fields.Datetime(required=True, default=lambda r: fields.Datetime.now()) partner_id = fields.Many2one( "res.partner", required=True, default=lambda r: r.env.user.partner_id.id ) @@ -549,7 +572,6 @@ class SignRequestLog(models.Model): ("configure", "Configure"), ], required=True, - readonly=True, ) - access_token = fields.Char(readonly=True) - ip = fields.Char(readonly=True) + access_token = fields.Char() + ip = fields.Char() diff --git a/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.esm.js b/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.esm.js index 5178eb92..ace400de 100644 --- a/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.esm.js +++ b/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.esm.js @@ -1,12 +1,14 @@ -/** @odoo-module **/ +/** @odoo-module QWeb **/ -import {ComponentWrapper} from "web.OwlCompatibility"; -import AbstractAction from "web.AbstractAction"; -import Dialog from "web.Dialog"; -import core from "web.core"; -import ControlPanel from "web.ControlPanel"; +import {Component} from "@odoo/owl"; +import {ControlPanel} from "@web/search/control_panel/control_panel"; +import {Dialog} from "@web/core/dialog/dialog"; +import {FormRenderer} from "@web/views/form/form_renderer"; import SignOcaPdfCommon from "../sign_oca_pdf_common/sign_oca_pdf_common.esm.js"; -const _t = core._t; +import {_t} from "@web/core/l10n/translation"; +import {registry} from "@web/core/registry"; +import {renderToString} from "@web/core/utils/render"; + export class SignOcaConfigureControlPanel extends ControlPanel {} SignOcaConfigureControlPanel.template = "sign_oca.SignOcaConfigureControlPanel"; export class SignOcaConfigure extends SignOcaPdfCommon { @@ -35,7 +37,7 @@ export class SignOcaConfigure extends SignOcaPdfCommon { } var position = page.getBoundingClientRect(); this.contextMenu = $( - core.qweb.render("sign_oca.sign_iframe_contextmenu", { + renderToString("sign_oca.sign_iframe_contextmenu", { page, e, left: ((e.pageX - position.x) * 100) / position.width + "%", @@ -111,7 +113,7 @@ export class SignOcaConfigure extends SignOcaPdfCommon { var dialog = new Dialog(this, { title: _t("Edit field"), $content: $( - core.qweb.render("sign_oca.sign_oca_field_edition", { + renderToString("sign_oca.sign_oca_field_edition", { item, info: this.info, }) @@ -367,9 +369,8 @@ export class SignOcaConfigure extends SignOcaPdfCommon { } } -export const SignOcaConfigureAction = AbstractAction.extend({ - hasControlPanel: true, - init: function (parent, action) { +export class SignOcaConfigureAction extends Component { + init(parent, action) { this._super.apply(this, arguments); this.model = (action.params.res_model !== undefined && action.params.res_model) || @@ -377,24 +378,24 @@ export const SignOcaConfigureAction = AbstractAction.extend({ this.res_id = (action.params.res_id !== undefined && action.params.res_id) || action.context.params.id; - }, + } async start() { await this._super(...arguments); - this.component = new ComponentWrapper(this, SignOcaConfigure, { + this.component = new FormRenderer(this, SignOcaConfigure, { model: this.model, res_id: this.res_id, }); this.$el.addClass("o_sign_oca_action"); return this.component.mount(this.$(".o_content")[0]); - }, - getState: function () { + } + getState() { var result = this._super(...arguments); result = _.extend({}, result, { res_model: this.model, res_id: this.res_id, }); return result; - }, -}); -core.action_registry.add("sign_oca_configure", SignOcaConfigureAction); + } +} SignOcaConfigure.template = "sign_oca.SignOcaConfigure"; +registry.category("actions").add("sign_oca_configure", SignOcaConfigureAction); diff --git a/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.xml b/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.xml index 01801d67..15bf4b1c 100644 --- a/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.xml +++ b/sign_oca/static/src/components/sign_oca_configure/sign_oca_configure.xml @@ -107,7 +107,6 @@ t-name="sign_oca.SignOcaConfigure" t-inherit="sign_oca.SignOcaPdfCommon" t-inherit-mode="primary" - owl="1" >
{ return ( item.required && - item.role === this.info.role && + item.role_id === this.info.role_id && !SignRegistry.get(item.field_type).check(item) ); }).length === 0; diff --git a/sign_oca/static/src/components/sign_oca_pdf/sign_oca_pdf_action.esm.js b/sign_oca/static/src/components/sign_oca_pdf/sign_oca_pdf_action.esm.js index eac7195d..8dc83b1c 100644 --- a/sign_oca/static/src/components/sign_oca_pdf/sign_oca_pdf_action.esm.js +++ b/sign_oca/static/src/components/sign_oca_pdf/sign_oca_pdf_action.esm.js @@ -1,14 +1,12 @@ /** @odoo-module **/ -import AbstractAction from "web.AbstractAction"; -import {ComponentWrapper} from "web.OwlCompatibility"; +import {Component} from "@odoo/owl"; +import {FormRenderer} from "@web/views/form/form_renderer"; import SignOcaPdf from "./sign_oca_pdf.esm.js"; -import core from "web.core"; +import {registry} from "@web/core/registry"; -const SignOcaPdfAction = AbstractAction.extend({ - className: "o_sign_oca_content", - hasControlPanel: true, - init: function (parent, action) { +export class SignOcaPdfAction extends Component { + init(parent, action) { this._super.apply(this, arguments); this.model = (action.params.res_model !== undefined && action.params.res_model) || @@ -16,25 +14,24 @@ const SignOcaPdfAction = AbstractAction.extend({ this.res_id = (action.params.res_id !== undefined && action.params.res_id) || action.context.params.id; - }, + } async start() { await this._super(...arguments); - this.component = new ComponentWrapper(this, SignOcaPdf, { + this.component = new FormRenderer(this, SignOcaPdf, { model: this.model, res_id: this.res_id, updateControlPanel: this.updateControlPanel.bind(this), trigger: this.trigger_up.bind(this), }); return this.component.mount(this.$(".o_content")[0]); - }, - getState: function () { + } + getState() { var result = this._super(...arguments); result = _.extend({}, result, { res_model: this.model, res_id: this.res_id, }); return result; - }, -}); -core.action_registry.add("sign_oca", SignOcaPdfAction); -export default SignOcaPdfAction; + } +} +registry.category("actions").add("sign_oca", SignOcaPdfAction); diff --git a/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.esm.js b/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.esm.js index b0099e71..1adf6bc1 100644 --- a/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.esm.js +++ b/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.esm.js @@ -1,8 +1,9 @@ -/** @odoo-module **/ -const {Component, onMounted, onWillStart, onWillUnmount, useRef} = owl; -import Dialog from "web.Dialog"; -import core from "web.core"; -const _t = core._t; +/** @odoo-module QWeb **/ +import {Component, onMounted, onWillStart, onWillUnmount, useRef} from "@odoo/owl"; +import {Dialog} from "@web/core/dialog/dialog"; +import {_t} from "@web/core/l10n/translation"; +import {renderToString} from "@web/core/utils/render"; + export default class SignOcaPdfCommon extends Component { setup() { super.setup(...arguments); @@ -11,8 +12,8 @@ export default class SignOcaPdfCommon extends Component { this.pdf_url = this.getPdfUrl(); this.viewer_url = "/web/static/lib/pdfjs/web/viewer.html?file=" + this.pdf_url; this.iframe = useRef("sign_oca_iframe"); - var iframeResolve = undefined; - var iframeReject = undefined; + var iframeResolve = ""; + var iframeReject = ""; this.iframeLoaded = new Promise(function (resolve, reject) { iframeResolve = resolve; iframeReject = reject; @@ -112,7 +113,7 @@ export default class SignOcaPdfCommon extends Component { item.page - 1 ]; var signatureItem = $( - core.qweb.render(this.field_template, { + renderToString(this.field_template, { ...item, }) ); diff --git a/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.xml b/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.xml index 2a313a14..5a9b0638 100644 --- a/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.xml +++ b/sign_oca/static/src/components/sign_oca_pdf_common/sign_oca_pdf_common.xml @@ -1,6 +1,6 @@ - +