diff --git a/fsm_visit_confirmation/controllers/main.py b/fsm_visit_confirmation/controllers/main.py index 29d4269..9794b24 100644 --- a/fsm_visit_confirmation/controllers/main.py +++ b/fsm_visit_confirmation/controllers/main.py @@ -3,10 +3,9 @@ import logging import werkzeug -from odoo import http +from odoo import http, _ from odoo.http import request -from odoo.tools.translate import _ -from odoo.tools.misc import get_lang +from odoo.addons.base.models.ir_qweb import keep_query from odoo.addons.portal.controllers.portal import CustomerPortal from odoo.exceptions import AccessError, MissingError @@ -20,65 +19,101 @@ class CustomerPortalExtended(CustomerPortal): def portal_my_task(self, task_id, access_token=None, **kw): """Override to allow access with token for public users.""" try: - task_sudo = self._document_check_access( - "project.task", task_id, access_token + task = request.env["project.task"].browse(task_id) + task.check_access_rights("read") + task.check_access_rights("write") + task.check_access_rule("read") + task.check_access_rule("write") + except AccessError: + task = None + values = self._get_portal_values(task, access_token=access_token, **kw) + if not task: + task = values.get("task", False) + if not task: + return self._render_error_page( + _("Task not found or invalid token"), task=None ) - except (AccessError, MissingError): - return request.redirect("/my") # Pass the task to the original method - return super(CustomerPortalExtended, self).portal_my_task( + result = super(CustomerPortalExtended, self).portal_my_task( task_id, access_token=access_token, **kw ) + # If the result is a Response object (like a redirect), return it directly + if isinstance(result, werkzeug.wrappers.Response): + return result + + # Otherwise, it's a rendered template, so we need to update the qcontext + if hasattr(result, "qcontext"): + result.qcontext.update(values) + + return result + @http.route( - "/my/fsm_confirmation//", + "/my/fsm_confirmation/", type="http", auth="public", website=True, ) - def fsm_confirmation_action(self, token, action, **kwargs): + def fsm_confirmation_action(self, action, **kwargs): """Custom landing page for FSM visit confirmations. Args: - token: The access token for the task action: Either 'approve' or 'change' + Kwargs: + access_token: The access token for the task """ + values = self._get_portal_values(**kwargs) + task = values.get("task", False) + if not task: + return self._render_error_page( + _("Task not found or invalid token"), task=None + ) + # Store the language in kwargs for use in redirects + lang = values.get("lang") + if action not in ("approve", "change"): raise werkzeug.exceptions.BadRequest(_("Invalid action")) - # Get the task using the token - task_sudo = self._get_task_by_token(token) - if not task_sudo: - return self._render_error_page(_("Task not found or invalid token")) - # If it's an approval, update the task state directly if action == "approve": try: # Update task state to approved - task_sudo.sudo().write({"state": "03_approved"}) - + task.write({"state": "03_approved"}) + # Add a message to the chatter - task_sudo.sudo().message_post( + task.message_post( body=_("Visit approved by customer"), message_type="comment", subtype_xmlid="mail.mt_note", ) # Always redirect to the task portal page with success message - return request.redirect( - "/my/task/%s?visit_confirmation_status=approved&access_token=%s" - % (task_sudo.id, token) + redirect_url = "/my/task/%s?%s" % ( + task.id, + keep_query( + "access_token", + visit_confirmation_status="approved", + lang=lang, + ), ) + return request.redirect(redirect_url) except Exception as e: - _logger.exception("Error approving task: %s", e) - return self._render_error_page(str(e)) + _logger.exception(_("Error approving task: %s"), e) + return self._render_error_page( + _("Error approving task: %s") % str(e), task=task + ) # For change requests, redirect to the task portal page with change request form - return request.redirect( - "/my/task/%s?visit_confirmation_status=change&token=%s&access_token=%s" - % (task_sudo.id, token, token) + redirect_url = "/my/task/%s?%s" % ( + task.id, + keep_query( + "access_token", + visit_confirmation_status="change", + lang=lang, + ), ) + return request.redirect(redirect_url) @http.route( "/my/fsm_confirmation/submit_change", @@ -90,60 +125,146 @@ class CustomerPortalExtended(CustomerPortal): ) def fsm_confirmation_submit_change(self, **kwargs): """Handle the submission of change request form.""" - token = kwargs.get("token") + values = self._get_portal_values(**kwargs) + + task = values.get("task", False) + lang = values.get("lang") feedback = kwargs.get("feedback", "") - - if not token or not feedback: - return request.redirect("/my") - - # Get the task using the token - task_sudo = self._get_task_by_token(token) - if not task_sudo: - return self._render_error_page(_("Task not found or invalid token")) + if not task: + return self._render_error_page( + _("Task not found or invalid token"), task=None + ) + if not feedback: + return self._render_error_page( + _("No feedback provided. Please try again!"), task=task + ) try: # Update task state to changes requested - task_sudo.sudo().write({"state": "02_changes_requested"}) - + task.write({"state": "02_changes_requested"}) + # Add the feedback as a message to the chatter - task_sudo.sudo().message_post( + task.message_post( body=_("Changes requested by customer: %s") % feedback, message_type="comment", subtype_xmlid="mail.mt_comment", ) # Always redirect to the task portal page with success message - return request.redirect( - "/my/task/%s?visit_confirmation_status=change_submitted&access_token=%s" - % (task_sudo.id, token) + redirect_url = "/my/task/%s?%s" % ( + task.id, + keep_query( + access_token=kwargs.get("access_token"), + visit_confirmation_status="change_submitted", + lang=lang, + ), ) + return request.redirect(redirect_url) except Exception as e: - _logger.exception("Error submitting change request: %s", e) - return self._render_error_page(str(e)) + _logger.exception(_("Error submitting change request: %s"), e) + return self._render_error_page( + _("Error submitting change request: %s") % str(e), task=task + ) def _get_task_by_token(self, token): """Get a task by its access token.""" - # First try to find the task directly by token - task = request.env["project.task"].sudo().search([("access_token", "=", token)], limit=1) + # First try to find the task directly by access token + task = ( + request.env["project.task"] + .sudo() + .search([("access_token", "=", token)], limit=1) + ) if task: return task - + # If not found, try to find it through the rating token # This is for backward compatibility with existing tokens in emails - rating = request.env["rating.rating"].sudo().search([("access_token", "=", token)], limit=1) + rating = ( + request.env["rating.rating"] + .sudo() + .search([("access_token", "=", token)], limit=1) + ) if rating and rating.res_model == "project.task": task = request.env["project.task"].sudo().browse(rating.res_id) if task.exists(): return task - + return None - def _render_error_page(self, error_message): + def _render_error_page(self, error_message, task=None, **kwargs): """Render an error page.""" - return request.render( - "http_routing.http_error", - { - "status_code": "400", - "status_message": error_message, - }, + values = { + "status_code": "400", + "status_message": error_message, + "lang": self._get_lang(task=task, lang=kwargs.get("lang")), + } + + return request.render("http_routing.http_error", values) + + def _get_portal_values(self, task=None, **kwargs): + """Get common values for portal templates. + + Args: + task: The task record if already loaded + **kwargs: Keyword arguments from the request + + Returns: + Dictionary with values for template rendering + """ + values = {"page_name": "task"} + if not task: + access_token = kwargs.get("access_token") + if access_token: + task = self._get_task_by_token(access_token) + if task: + values["task"] = task + + # Set language and update values dictionary + values["lang"] = self._get_lang(task=task, lang=kwargs.get("lang")) + + # Add any additional parameters from kwargs that might be needed in templates + for key in ["visit_confirmation_status", "error", "warning", "success"]: + if kwargs.get(key): + values[key] = kwargs.get(key) + + return values + + def _get_lang(self, task=None, lang=None): + """Helper method to set the language in the request environment. + + Args: + task: The task record (optional) + lang: Language code (e.g., 'en_US', 'fr_FR') (optional) + + Returns: + The language code that was set, or None if no language was set + """ + # If lang is not provided, try to get it from the task + if not lang and task: + lang_partner = ( + task.work_order_contacts + and task.work_order_contacts[0] + or task.partner_id + ) + if lang_partner and lang_partner.lang: + lang = lang_partner.lang + + if not lang: + return None + + # Get the language record + lang_code = lang.replace("_", "-") + lang_id = ( + request.env["res.lang"] + .sudo() + .search(["|", ("code", "=", lang_code), ("code", "=", lang)], limit=1) ) + + if lang_id: + # Set the context language + request.env = request.env( + context=dict(request.env.context, lang=lang_id.code) + ) + return lang_id.code + + return None diff --git a/fsm_visit_confirmation/data/mail_templates.xml b/fsm_visit_confirmation/data/mail_templates.xml index 6bdf82a..cadc3d9 100644 --- a/fsm_visit_confirmation/data/mail_templates.xml +++ b/fsm_visit_confirmation/data/mail_templates.xml @@ -10,7 +10,8 @@
- Dear , + Dear +,
We would like to confirm the details of your upcoming service visit: @@ -29,7 +30,8 @@
- (Timezone: ) + (Timezone: +)
@@ -42,7 +44,8 @@
- , + +, @@ -65,12 +68,13 @@ - + + Approve Visit - + Request Changes @@ -78,7 +82,8 @@

Best regards,

-

The service management team

+

The + service management team

diff --git a/fsm_visit_confirmation/i18n/fr.po b/fsm_visit_confirmation/i18n/fr.po new file mode 100644 index 0000000..90a2ee3 --- /dev/null +++ b/fsm_visit_confirmation/i18n/fr.po @@ -0,0 +1,299 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fsm_visit_confirmation +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-27 00:31+0000\n" +"PO-Revision-Date: 2025-02-27 00:31+0000\n" +"Last-Translator: Marc Durepos \n" +"Language-Team: French\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: fsm_visit_confirmation +#: model:mail.template,body_html:fsm_visit_confirmation.fsm_visit_confirmation_email_template +msgid "" +"
\n" +"
\n" +" Dear \n" +",\n" +"
\n" +"
\n" +" We would like to confirm the details of your upcoming service visit:\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
Task Summary\n" +" \n" +"
Technician Arrival\n" +" \n" +" \n" +" \n" +"
\n" +" (Timezone: \n" +")\n" +"
\n" +"
Location\n" +" \n" +"
\n" +" \n" +" \n" +"
\n" +"
\n" +" \n" +", \n" +" \n" +"
Assigned Technician(s)\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +" Please take a moment to confirm this visit by clicking one of the following options:\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" (Fuseau horaire: \n" +")\n" +"
\n" +"
Lieu\n" +" \n" +"
\n" +" \n" +" \n" +"
\n" +"
\n" +" \n" +", \n" +" \n" +"
Technicien(s) assigné(s)\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +" Veuillez prendre un moment pour confirmer cette visite en cliquant sur une des options suivantes :\n" +"
\n" +" \n" +" \n" +"
\n" +" \n" +" \n" +"

\n" +" \n" +" \n" +" " + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "" +"\n" +" Success! Thank you for approving this service visit." +msgstr "" +"\n" +" Succès! Merci d'avoir approuvé cette visite de service." + + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "" +"\n" +" Request Changes to Service Visit" +msgstr "" +"\n" +" Demander des changements à la visite de service" + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "" +"\n" +" Thank you! Your change request has been submitted. Our team will review it shortly." +msgstr "" +"\n" +" Merci! Votre demande de changement a été soumise. Notre équipe examinerait cette demande rapidement." + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "Changes requested by customer: %s" +msgstr "Changements demandés par client: %s" + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "Error approving task: %s" +msgstr "Erreur lors de l'approbation de la tâche: %s" + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "Error submitting change request: %s" +msgstr "Erreur lors de la soumission de la demande de changement: %s" + +#. module: fsm_visit_confirmation +#: model:mail.template,name:fsm_visit_confirmation.fsm_visit_confirmation_email_template +msgid "FSM Visit Confirmation" +msgstr "Confirmation de visite de service" + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "Invalid action" +msgstr "Action invalide" + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "No feedback provided. Please try again!" +msgstr "Aucun commentaire fourni. Veuillez réessayer!" + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "Please explain what changes you need..." +msgstr "Veuillez expliquer les modifications nécessaires..." + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "" +"Please provide details about the changes you would like to make to this " +"service visit:" +msgstr "" +"Veuillez fournir des informations sur les modifications que vous souhaitez " +"apporter à cette visite de service :" + +#. module: fsm_visit_confirmation +#: model:mail.template,description:fsm_visit_confirmation.fsm_visit_confirmation_email_template +msgid "Send a visit confirmation email to the customer" +msgstr "Envoyer un email de confirmation de visite au client" + +#. module: fsm_visit_confirmation +#: model:mail.template,subject:fsm_visit_confirmation.fsm_visit_confirmation_email_template +msgid "Service Visit Confirmation: {{ object.name }}" +msgstr "Confirmation de visite de service: {{ object.name }}" + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "Submit Request" +msgstr "Soumettre la demande" + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "Task not found or invalid token" +msgstr "Tâche introuvable ou jeton invalide" + +#. module: fsm_visit_confirmation +#. odoo-python +#: code:addons/fsm_visit_confirmation/controllers/main.py:0 +#, python-format +msgid "Visit approved by customer" +msgstr "Visite approuvée par le client" + +#. module: fsm_visit_confirmation +#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task +msgid "Your Comments:" +msgstr "Vos commentaires :" diff --git a/fsm_visit_confirmation/i18n/fsm_visit_confirmation.pot b/fsm_visit_confirmation/i18n/fsm_visit_confirmation.pot new file mode 100644 index 0000000..0b790cc --- /dev/null +++ b/fsm_visit_confirmation/i18n/fsm_visit_confirmation.pot @@ -0,0 +1,211 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fsm_visit_confirmation +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-02-27 00:31+0000\n" +"PO-Revision-Date: 2025-02-27 00:31+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: fsm_visit_confirmation +#: model:mail.template,body_html:fsm_visit_confirmation.fsm_visit_confirmation_email_template +msgid "" +"
\n" +"
\n" +" Dear \n" +",\n" +"
\n" +"
\n" +" We would like to confirm the details of your upcoming service visit:\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
Task Summary\n" +" \n" +"
Technician Arrival\n" +" \n" +" \n" +" \n" +"
\n" +" (Timezone: \n" +")\n" +"
\n" +"
Location\n" +" \n" +"
\n" +" \n" +" \n" +"
\n" +"
\n" +" \n" +", \n" +" \n" +"
Assigned Technician(s)\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +" Please take a moment to confirm this visit by clicking one of the following options:\n" +"
\n" +" \n" +" \n" +"
\n" +" \n" +" \n" +" + +