fsm_visit_confirmation: translate template, move away from ratings
This commit is contained in:
parent
95a95298bb
commit
81a4a904c7
6 changed files with 703 additions and 66 deletions
|
|
@ -3,10 +3,9 @@
|
||||||
import logging
|
import logging
|
||||||
import werkzeug
|
import werkzeug
|
||||||
|
|
||||||
from odoo import http
|
from odoo import http, _
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
from odoo.tools.translate import _
|
from odoo.addons.base.models.ir_qweb import keep_query
|
||||||
from odoo.tools.misc import get_lang
|
|
||||||
from odoo.addons.portal.controllers.portal import CustomerPortal
|
from odoo.addons.portal.controllers.portal import CustomerPortal
|
||||||
from odoo.exceptions import AccessError, MissingError
|
from odoo.exceptions import AccessError, MissingError
|
||||||
|
|
||||||
|
|
@ -20,65 +19,101 @@ class CustomerPortalExtended(CustomerPortal):
|
||||||
def portal_my_task(self, task_id, access_token=None, **kw):
|
def portal_my_task(self, task_id, access_token=None, **kw):
|
||||||
"""Override to allow access with token for public users."""
|
"""Override to allow access with token for public users."""
|
||||||
try:
|
try:
|
||||||
task_sudo = self._document_check_access(
|
task = request.env["project.task"].browse(task_id)
|
||||||
"project.task", task_id, access_token
|
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
|
# 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
|
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(
|
@http.route(
|
||||||
"/my/fsm_confirmation/<string:token>/<string:action>",
|
"/my/fsm_confirmation/<string:action>",
|
||||||
type="http",
|
type="http",
|
||||||
auth="public",
|
auth="public",
|
||||||
website=True,
|
website=True,
|
||||||
)
|
)
|
||||||
def fsm_confirmation_action(self, token, action, **kwargs):
|
def fsm_confirmation_action(self, action, **kwargs):
|
||||||
"""Custom landing page for FSM visit confirmations.
|
"""Custom landing page for FSM visit confirmations.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
token: The access token for the task
|
|
||||||
action: Either 'approve' or 'change'
|
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"):
|
if action not in ("approve", "change"):
|
||||||
raise werkzeug.exceptions.BadRequest(_("Invalid action"))
|
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 it's an approval, update the task state directly
|
||||||
if action == "approve":
|
if action == "approve":
|
||||||
try:
|
try:
|
||||||
# Update task state to approved
|
# Update task state to approved
|
||||||
task_sudo.sudo().write({"state": "03_approved"})
|
task.write({"state": "03_approved"})
|
||||||
|
|
||||||
# Add a message to the chatter
|
# Add a message to the chatter
|
||||||
task_sudo.sudo().message_post(
|
task.message_post(
|
||||||
body=_("Visit approved by customer"),
|
body=_("Visit approved by customer"),
|
||||||
message_type="comment",
|
message_type="comment",
|
||||||
subtype_xmlid="mail.mt_note",
|
subtype_xmlid="mail.mt_note",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Always redirect to the task portal page with success message
|
# Always redirect to the task portal page with success message
|
||||||
return request.redirect(
|
redirect_url = "/my/task/%s?%s" % (
|
||||||
"/my/task/%s?visit_confirmation_status=approved&access_token=%s"
|
task.id,
|
||||||
% (task_sudo.id, token)
|
keep_query(
|
||||||
|
"access_token",
|
||||||
|
visit_confirmation_status="approved",
|
||||||
|
lang=lang,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
return request.redirect(redirect_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.exception("Error approving task: %s", e)
|
_logger.exception(_("Error approving task: %s"), e)
|
||||||
return self._render_error_page(str(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
|
# For change requests, redirect to the task portal page with change request form
|
||||||
return request.redirect(
|
redirect_url = "/my/task/%s?%s" % (
|
||||||
"/my/task/%s?visit_confirmation_status=change&token=%s&access_token=%s"
|
task.id,
|
||||||
% (task_sudo.id, token, token)
|
keep_query(
|
||||||
|
"access_token",
|
||||||
|
visit_confirmation_status="change",
|
||||||
|
lang=lang,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
return request.redirect(redirect_url)
|
||||||
|
|
||||||
@http.route(
|
@http.route(
|
||||||
"/my/fsm_confirmation/submit_change",
|
"/my/fsm_confirmation/submit_change",
|
||||||
|
|
@ -90,60 +125,146 @@ class CustomerPortalExtended(CustomerPortal):
|
||||||
)
|
)
|
||||||
def fsm_confirmation_submit_change(self, **kwargs):
|
def fsm_confirmation_submit_change(self, **kwargs):
|
||||||
"""Handle the submission of change request form."""
|
"""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", "")
|
feedback = kwargs.get("feedback", "")
|
||||||
|
if not task:
|
||||||
if not token or not feedback:
|
return self._render_error_page(
|
||||||
return request.redirect("/my")
|
_("Task not found or invalid token"), task=None
|
||||||
|
)
|
||||||
# Get the task using the token
|
if not feedback:
|
||||||
task_sudo = self._get_task_by_token(token)
|
return self._render_error_page(
|
||||||
if not task_sudo:
|
_("No feedback provided. Please try again!"), task=task
|
||||||
return self._render_error_page(_("Task not found or invalid token"))
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Update task state to changes requested
|
# 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
|
# Add the feedback as a message to the chatter
|
||||||
task_sudo.sudo().message_post(
|
task.message_post(
|
||||||
body=_("Changes requested by customer: %s") % feedback,
|
body=_("Changes requested by customer: %s") % feedback,
|
||||||
message_type="comment",
|
message_type="comment",
|
||||||
subtype_xmlid="mail.mt_comment",
|
subtype_xmlid="mail.mt_comment",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Always redirect to the task portal page with success message
|
# Always redirect to the task portal page with success message
|
||||||
return request.redirect(
|
redirect_url = "/my/task/%s?%s" % (
|
||||||
"/my/task/%s?visit_confirmation_status=change_submitted&access_token=%s"
|
task.id,
|
||||||
% (task_sudo.id, token)
|
keep_query(
|
||||||
|
access_token=kwargs.get("access_token"),
|
||||||
|
visit_confirmation_status="change_submitted",
|
||||||
|
lang=lang,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
return request.redirect(redirect_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.exception("Error submitting change request: %s", e)
|
_logger.exception(_("Error submitting change request: %s"), e)
|
||||||
return self._render_error_page(str(e))
|
return self._render_error_page(
|
||||||
|
_("Error submitting change request: %s") % str(e), task=task
|
||||||
|
)
|
||||||
|
|
||||||
def _get_task_by_token(self, token):
|
def _get_task_by_token(self, token):
|
||||||
"""Get a task by its access token."""
|
"""Get a task by its access token."""
|
||||||
# First try to find the task directly by token
|
# First try to find the task directly by access token
|
||||||
task = request.env["project.task"].sudo().search([("access_token", "=", token)], limit=1)
|
task = (
|
||||||
|
request.env["project.task"]
|
||||||
|
.sudo()
|
||||||
|
.search([("access_token", "=", token)], limit=1)
|
||||||
|
)
|
||||||
if task:
|
if task:
|
||||||
return task
|
return task
|
||||||
|
|
||||||
# If not found, try to find it through the rating token
|
# If not found, try to find it through the rating token
|
||||||
# This is for backward compatibility with existing tokens in emails
|
# 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":
|
if rating and rating.res_model == "project.task":
|
||||||
task = request.env["project.task"].sudo().browse(rating.res_id)
|
task = request.env["project.task"].sudo().browse(rating.res_id)
|
||||||
if task.exists():
|
if task.exists():
|
||||||
return task
|
return task
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _render_error_page(self, error_message):
|
def _render_error_page(self, error_message, task=None, **kwargs):
|
||||||
"""Render an error page."""
|
"""Render an error page."""
|
||||||
return request.render(
|
values = {
|
||||||
"http_routing.http_error",
|
"status_code": "400",
|
||||||
{
|
"status_message": error_message,
|
||||||
"status_code": "400",
|
"lang": self._get_lang(task=task, lang=kwargs.get("lang")),
|
||||||
"status_message": error_message,
|
}
|
||||||
},
|
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
<field name="body_html" type="html">
|
<field name="body_html" type="html">
|
||||||
<div>
|
<div>
|
||||||
<div style="margin-bottom: 16px;">
|
<div style="margin-bottom: 16px;">
|
||||||
Dear <t t-out="object.work_order_contacts and object.work_order_contacts[0].name or object.partner_id.name"/>,
|
Dear <t t-out="object.work_order_contacts and object.work_order_contacts[0].name or object.partner_id.name"/>
|
||||||
|
,
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom: 16px;">
|
<div style="margin-bottom: 16px;">
|
||||||
We would like to confirm the details of your upcoming service visit:
|
We would like to confirm the details of your upcoming service visit:
|
||||||
|
|
@ -29,7 +30,8 @@
|
||||||
<t t-set="partner_tz" t-value="user.tz or object.company_id.partner_id.tz or 'America/Toronto'"/>
|
<t t-set="partner_tz" t-value="user.tz or object.company_id.partner_id.tz or 'America/Toronto'"/>
|
||||||
<span t-field="object.planned_date_begin" t-options="{'widget': 'datetime', 'timezone': partner_tz}"/>
|
<span t-field="object.planned_date_begin" t-options="{'widget': 'datetime', 'timezone': partner_tz}"/>
|
||||||
<div style="font-size: 12px; color: #666;">
|
<div style="font-size: 12px; color: #666;">
|
||||||
(Timezone: <t t-out="partner_tz"/>)
|
(Timezone: <t t-out="partner_tz"/>
|
||||||
|
)
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -42,7 +44,8 @@
|
||||||
<t t-out="object.partner_id.street2"/>
|
<t t-out="object.partner_id.street2"/>
|
||||||
<br/>
|
<br/>
|
||||||
</t>
|
</t>
|
||||||
<t t-out="object.partner_id.city"/>, <t t-out="object.partner_id.state_id.code"/>
|
<t t-out="object.partner_id.city"/>
|
||||||
|
, <t t-out="object.partner_id.state_id.code"/>
|
||||||
<t t-out="object.partner_id.zip"/>
|
<t t-out="object.partner_id.zip"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -65,12 +68,13 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:10px;text-align:center;">
|
<td style="padding:10px;text-align:center;">
|
||||||
<t t-set="base_url" t-value="object.get_base_url()"/>
|
<t t-set="base_url" t-value="object.get_base_url()"/>
|
||||||
<a t-att-href="base_url + '/my/fsm_confirmation/' + object.access_token + '/approve'" style="background-color:#28a745;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;">
|
<t t-set="lang" t-value="object.work_order_contacts and object.work_order_contacts[0].lang or object.partner_id.lang or 'en_US'"/>
|
||||||
|
<a t-att-href="base_url + '/my/fsm_confirmation/approve?' + keep_query(lang=lang,access_token=object.access_token)" style="background-color:#28a745;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;">
|
||||||
Approve Visit
|
Approve Visit
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:10px;text-align:center;">
|
<td style="padding:10px;text-align:center;">
|
||||||
<a t-att-href="base_url + '/my/fsm_confirmation/' + object.access_token + '/change'" style="background-color:#dc3545;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;">
|
<a t-att-href="base_url + '/my/fsm_confirmation/change?' + keep_query(lang=lang,access_token=object.access_token)" style="background-color:#dc3545;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;">
|
||||||
Request Changes
|
Request Changes
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -78,7 +82,8 @@
|
||||||
</table>
|
</table>
|
||||||
<div style="margin-top: 16px;">
|
<div style="margin-top: 16px;">
|
||||||
<p>Best regards,</p>
|
<p>Best regards,</p>
|
||||||
<p>The <t t-out="object.company_id.name"/> service management team</p>
|
<p>The <t t-out="object.company_id.name"/>
|
||||||
|
service management team</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</field>
|
</field>
|
||||||
|
|
|
||||||
299
fsm_visit_confirmation/i18n/fr.po
Normal file
299
fsm_visit_confirmation/i18n/fr.po
Normal file
|
|
@ -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 <marc@bemade.org>\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 ""
|
||||||
|
"<div>\n"
|
||||||
|
" <div style=\"margin-bottom: 16px;\">\n"
|
||||||
|
" Dear <t t-out=\"object.work_order_contacts and object.work_order_contacts[0].name or object.partner_id.name\"></t>\n"
|
||||||
|
",\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <div style=\"margin-bottom: 16px;\">\n"
|
||||||
|
" We would like to confirm the details of your upcoming service visit:\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <table style=\"border-style:solid;box-sizing:border-box;border-left-color:#e7e7e7;border-bottom-color:#e7e7e7;border-right-color:#e7e7e7;border-top-color:#e7e7e7;border-left-width:1px;border-bottom-width:1px;border-right-width:1px;border-top-width:1px;-webkit-border-vertical-spacing:0px;-webkit-border-horizontal-spacing:0px;border-collapse:collapse;caption-side:bottom;width:100%;border-spacing:0;border:1pxsolid#e7e7e7\" width=\"100%\">\n"
|
||||||
|
" <tbody style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\"><tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Task Summary</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-out=\"object.name\"></t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Technician Arrival</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-set=\"partner\" t-value=\"object.work_order_contacts and object.work_order_contacts[0] or object.partner_id\"></t>\n"
|
||||||
|
" <t t-set=\"partner_tz\" t-value=\"user.tz or object.company_id.partner_id.tz or 'America/Toronto'\"></t>\n"
|
||||||
|
" <span t-field=\"object.planned_date_begin\" t-options=\"{'widget': 'datetime', 'timezone': partner_tz}\"></span>\n"
|
||||||
|
" <div style=\"font-size: 12px; color: #666;\">\n"
|
||||||
|
" (Timezone: <t t-out=\"partner_tz\"></t>\n"
|
||||||
|
")\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Location</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-out=\"object.partner_id.street\"></t>\n"
|
||||||
|
" <br>\n"
|
||||||
|
" <t t-if=\"object.partner_id.street2\">\n"
|
||||||
|
" <t t-out=\"object.partner_id.street2\"></t>\n"
|
||||||
|
" <br>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" <t t-out=\"object.partner_id.city\"></t>\n"
|
||||||
|
", <t t-out=\"object.partner_id.state_id.code\"></t>\n"
|
||||||
|
" <t t-out=\"object.partner_id.zip\"></t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa\">Assigned Technician(s)</td>\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px\">\n"
|
||||||
|
" <t t-foreach=\"object.user_ids\" t-as=\"user\">\n"
|
||||||
|
" <t t-out=\"user.name\"></t>\n"
|
||||||
|
" <t t-if=\"not user_last\">\n"
|
||||||
|
" <br>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </tbody></table>\n"
|
||||||
|
" <div style=\"margin: 16px 0px 16px 0px;\">\n"
|
||||||
|
" Please take a moment to confirm this visit by clicking one of the following options:\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <table style=\"box-sizing:border-box;-webkit-border-vertical-spacing:0px;-webkit-border-horizontal-spacing:0px;border-collapse:collapse;caption-side:bottom;width:100%;border-spacing:0;\" width=\"100%\">\n"
|
||||||
|
" <tbody style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\"><tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;text-align:center\">\n"
|
||||||
|
" <t t-set=\"base_url\" t-value=\"object.get_base_url()\"></t>\n"
|
||||||
|
" <t t-set=\"lang\" t-value=\"object.work_order_contacts and object.work_order_contacts[0].lang or object.partner_id.lang or 'en_US'\"></t>\n"
|
||||||
|
" <a t-att-href=\"base_url + '/my/fsm_confirmation/approve?access_token=' + object.access_token + '&lang=' + lang\" style=\"box-sizing:border-box;background-color:#28a745;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;\">\n"
|
||||||
|
" Approve Visit\n"
|
||||||
|
" </a>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;text-align:center\">\n"
|
||||||
|
" <a t-att-href=\"base_url + '/my/fsm_confirmation/change?access_token=' + object.access_token + '&lang=' + lang\" style=\"box-sizing:border-box;background-color:#dc3545;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;\">\n"
|
||||||
|
" Request Changes\n"
|
||||||
|
" </a>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </tbody></table>\n"
|
||||||
|
" <div style=\"margin-top: 16px;\">\n"
|
||||||
|
" <p style=\"margin:0px 0 16px 0;box-sizing:border-box;\">Best regards,</p>\n"
|
||||||
|
" <p style=\"margin:0px 0 16px 0;box-sizing:border-box;\">The <t t-out=\"object.company_id.name\"></t>\n"
|
||||||
|
" service management team</p>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"<div>\n"
|
||||||
|
" <div style=\"margin-bottom: 16px;\">\n"
|
||||||
|
" Cher/chère<t t-out=\"object.work_order_contacts and object.work_order_contacts[0].name or object.partner_id.name\"></t>\n"
|
||||||
|
",\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <div style=\"margin-bottom: 16px;\">\n"
|
||||||
|
" Nous aimerions confirmer les détails de votre visite de service à venir.\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <table style=\"border-style:solid;box-sizing:border-box;border-left-color:#e7e7e7;border-bottom-color:#e7e7e7;border-right-color:#e7e7e7;border-top-color:#e7e7e7;border-left-width:1px;border-bottom-width:1px;border-right-width:1px;border-top-width:1px;-webkit-border-vertical-spacing:0px;-webkit-border-horizontal-spacing:0px;border-collapse:collapse;caption-side:bottom;width:100%;border-spacing:0;border:1pxsolid#e7e7e7\" width=\"100%\">\n"
|
||||||
|
" <tbody style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\"><tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Visite</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-out=\"object.name\"></t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Début</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-set=\"partner\" t-value=\"object.work_order_contacts and object.work_order_contacts[0] or object.partner_id\"></t>\n"
|
||||||
|
" <t t-set=\"partner_tz\" t-value=\"user.tz or object.company_id.partner_id.tz or 'America/Toronto'\"></t>\n"
|
||||||
|
" <span t-field=\"object.planned_date_begin\" t-options=\"{'widget': 'datetime', 'timezone': partner_tz}\"></span>\n"
|
||||||
|
" <div style=\"font-size: 12px; color: #666;\">\n"
|
||||||
|
" (Fuseau horaire: <t t-out=\"partner_tz\"></t>\n"
|
||||||
|
")\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Lieu</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-out=\"object.partner_id.street\"></t>\n"
|
||||||
|
" <br>\n"
|
||||||
|
" <t t-if=\"object.partner_id.street2\">\n"
|
||||||
|
" <t t-out=\"object.partner_id.street2\"></t>\n"
|
||||||
|
" <br>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" <t t-out=\"object.partner_id.city\"></t>\n"
|
||||||
|
", <t t-out=\"object.partner_id.state_id.code\"></t>\n"
|
||||||
|
" <t t-out=\"object.partner_id.zip\"></t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa\">Technicien(s) assigné(s)</td>\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px\">\n"
|
||||||
|
" <t t-foreach=\"object.user_ids\" t-as=\"user\">\n"
|
||||||
|
" <t t-out=\"user.name\"></t>\n"
|
||||||
|
" <t t-if=\"not user_last\">\n"
|
||||||
|
" <br>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </tbody></table>\n"
|
||||||
|
" <div style=\"margin: 16px 0px 16px 0px;\">\n"
|
||||||
|
" Veuillez prendre un moment pour confirmer cette visite en cliquant sur une des options suivantes :\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <table style=\"box-sizing:border-box;-webkit-border-vertical-spacing:0px;-webkit-border-horizontal-spacing:0px;border-collapse:collapse;caption-side:bottom;width:100%;border-spacing:0;\" width=\"100%\">\n"
|
||||||
|
" <tbody style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\"><tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;text-align:center\">\n"
|
||||||
|
" <t t-set=\"base_url\" t-value=\"object.get_base_url()\"></t>\n"
|
||||||
|
" <t t-set=\"lang\" t-value=\"object.work_order_contacts and object.work_order_contacts[0].lang or object.partner_id.lang or 'en_US'\"></t>\n"
|
||||||
|
" <a t-att-href=\"base_url + '/my/fsm_confirmation/approve?access_token=' + object.access_token + '&lang=' + lang\" style=\"box-sizing:border-box;background-color:#28a745;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;\">\n"
|
||||||
|
" Confirmer la visite\n"
|
||||||
|
" </a>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;text-align:center\">\n"
|
||||||
|
" <a t-att-href=\"base_url + '/my/fsm_confirmation/change?access_token=' + object.access_token + '&lang=' + lang\" style=\"box-sizing:border-box;background-color:#dc3545;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;\">\n"
|
||||||
|
" Demander des changements\n"
|
||||||
|
" </a>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </tbody></table>\n"
|
||||||
|
" <div style=\"margin-top: 16px;\">\n"
|
||||||
|
" <p style=\"margin:0px 0 16px 0;box-sizing:border-box;\">Cordialement,</p>\n"
|
||||||
|
" <p style=\"margin:0px 0 16px 0;box-sizing:border-box;\">L'équipe de gestion des services de <t t-out=\"object.company_id.name\"></t></p>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task
|
||||||
|
msgid ""
|
||||||
|
"<i class=\"fa fa-check-circle me-2\"/>\n"
|
||||||
|
" <strong>Success!</strong> Thank you for approving this service visit."
|
||||||
|
msgstr ""
|
||||||
|
"<i class=\"fa fa-check-circle me-2\"/>\n"
|
||||||
|
" <strong>Succès!</strong> 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 ""
|
||||||
|
"<i class=\"fa fa-exclamation-triangle me-2\"/>\n"
|
||||||
|
" Request Changes to Service Visit"
|
||||||
|
msgstr ""
|
||||||
|
"<i class=\"fa fa-exclamation-triangle me-2\"/>\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 ""
|
||||||
|
"<i class=\"fa fa-info-circle me-2\"/>\n"
|
||||||
|
" <strong>Thank you!</strong> Your change request has been submitted. Our team will review it shortly."
|
||||||
|
msgstr ""
|
||||||
|
"<i class=\"fa fa-info-circle me-2\"/>\n"
|
||||||
|
" <strong>Merci!</strong> 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 :"
|
||||||
211
fsm_visit_confirmation/i18n/fsm_visit_confirmation.pot
Normal file
211
fsm_visit_confirmation/i18n/fsm_visit_confirmation.pot
Normal file
|
|
@ -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 ""
|
||||||
|
"<div>\n"
|
||||||
|
" <div style=\"margin-bottom: 16px;\">\n"
|
||||||
|
" Dear <t t-out=\"object.work_order_contacts and object.work_order_contacts[0].name or object.partner_id.name\"></t>\n"
|
||||||
|
",\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <div style=\"margin-bottom: 16px;\">\n"
|
||||||
|
" We would like to confirm the details of your upcoming service visit:\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <table style=\"border-style:solid;box-sizing:border-box;border-left-color:#e7e7e7;border-bottom-color:#e7e7e7;border-right-color:#e7e7e7;border-top-color:#e7e7e7;border-left-width:1px;border-bottom-width:1px;border-right-width:1px;border-top-width:1px;-webkit-border-vertical-spacing:0px;-webkit-border-horizontal-spacing:0px;border-collapse:collapse;caption-side:bottom;width:100%;border-spacing:0;border:1pxsolid#e7e7e7\" width=\"100%\">\n"
|
||||||
|
" <tbody style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\"><tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Task Summary</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-out=\"object.name\"></t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Technician Arrival</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-set=\"partner\" t-value=\"object.work_order_contacts and object.work_order_contacts[0] or object.partner_id\"></t>\n"
|
||||||
|
" <t t-set=\"partner_tz\" t-value=\"user.tz or object.company_id.partner_id.tz or 'America/Toronto'\"></t>\n"
|
||||||
|
" <span t-field=\"object.planned_date_begin\" t-options=\"{'widget': 'datetime', 'timezone': partner_tz}\"></span>\n"
|
||||||
|
" <div style=\"font-size: 12px; color: #666;\">\n"
|
||||||
|
" (Timezone: <t t-out=\"partner_tz\"></t>\n"
|
||||||
|
")\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa;border-bottom:1pxsolid#e7e7e7\">Location</td>\n"
|
||||||
|
" <td style=\"border-style:solid;box-sizing:border-box;border-bottom-color:#e7e7e7;border-left-width:0px;border-bottom-width:1px;border-right-width:0px;border-top-width:0px;padding:10px;border-bottom:1pxsolid#e7e7e7\">\n"
|
||||||
|
" <t t-out=\"object.partner_id.street\"></t>\n"
|
||||||
|
" <br>\n"
|
||||||
|
" <t t-if=\"object.partner_id.street2\">\n"
|
||||||
|
" <t t-out=\"object.partner_id.street2\"></t>\n"
|
||||||
|
" <br>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" <t t-out=\"object.partner_id.city\"></t>\n"
|
||||||
|
", <t t-out=\"object.partner_id.state_id.code\"></t>\n"
|
||||||
|
" <t t-out=\"object.partner_id.zip\"></t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;background-color:#f8f9fa\">Assigned Technician(s)</td>\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px\">\n"
|
||||||
|
" <t t-foreach=\"object.user_ids\" t-as=\"user\">\n"
|
||||||
|
" <t t-out=\"user.name\"></t>\n"
|
||||||
|
" <t t-if=\"not user_last\">\n"
|
||||||
|
" <br>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" </t>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </tbody></table>\n"
|
||||||
|
" <div style=\"margin: 16px 0px 16px 0px;\">\n"
|
||||||
|
" Please take a moment to confirm this visit by clicking one of the following options:\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <table style=\"box-sizing:border-box;-webkit-border-vertical-spacing:0px;-webkit-border-horizontal-spacing:0px;border-collapse:collapse;caption-side:bottom;width:100%;border-spacing:0;\" width=\"100%\">\n"
|
||||||
|
" <tbody style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\"><tr style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px\">\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;text-align:center\">\n"
|
||||||
|
" <t t-set=\"base_url\" t-value=\"object.get_base_url()\"></t>\n"
|
||||||
|
" <t t-set=\"lang\" t-value=\"object.work_order_contacts and object.work_order_contacts[0].lang or object.partner_id.lang or 'en_US'\"></t>\n"
|
||||||
|
" <a t-att-href=\"base_url + '/my/fsm_confirmation/approve?access_token=' + object.access_token + '&lang=' + lang\" style=\"box-sizing:border-box;background-color:#28a745;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;\">\n"
|
||||||
|
" Approve Visit\n"
|
||||||
|
" </a>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" <td style=\"border-style:none;box-sizing:border-box;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;border-top-width:0px;padding:10px;text-align:center\">\n"
|
||||||
|
" <a t-att-href=\"base_url + '/my/fsm_confirmation/change?access_token=' + object.access_token + '&lang=' + lang\" style=\"box-sizing:border-box;background-color:#dc3545;padding:8px 16px;text-decoration:none;color:#fff;border-radius:5px;font-size:13px;\">\n"
|
||||||
|
" Request Changes\n"
|
||||||
|
" </a>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </tbody></table>\n"
|
||||||
|
" <div style=\"margin-top: 16px;\">\n"
|
||||||
|
" <p style=\"margin:0px 0 16px 0;box-sizing:border-box;\">Best regards,</p>\n"
|
||||||
|
" <p style=\"margin:0px 0 16px 0;box-sizing:border-box;\">The <t t-out=\"object.company_id.name\"></t>\n"
|
||||||
|
" service management team</p>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task
|
||||||
|
msgid ""
|
||||||
|
"<i class=\"fa fa-check-circle me-2\"/>\n"
|
||||||
|
" <strong>Success!</strong> Thank you for approving this service visit."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task
|
||||||
|
msgid ""
|
||||||
|
"<i class=\"fa fa-exclamation-triangle me-2\"/>\n"
|
||||||
|
" Request Changes to Service Visit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task
|
||||||
|
msgid ""
|
||||||
|
"<i class=\"fa fa-info-circle me-2\"/>\n"
|
||||||
|
" <strong>Thank you!</strong> Your change request has been submitted. Our team will review it shortly."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/fsm_visit_confirmation/controllers/main.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Changes requested by customer: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model:mail.template,name:fsm_visit_confirmation.fsm_visit_confirmation_email_template
|
||||||
|
msgid "FSM Visit Confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/fsm_visit_confirmation/controllers/main.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Invalid action"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model:mail.template,subject:fsm_visit_confirmation.fsm_visit_confirmation_email_template
|
||||||
|
msgid "Service Visit Confirmation: {{ object.name }}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task
|
||||||
|
msgid "Submit Request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. 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 ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/fsm_visit_confirmation/controllers/main.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Visit approved by customer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: fsm_visit_confirmation
|
||||||
|
#: model_terms:ir.ui.view,arch_db:fsm_visit_confirmation.portal_my_task
|
||||||
|
msgid "Your Comments:"
|
||||||
|
msgstr ""
|
||||||
|
|
@ -120,7 +120,7 @@ class TestFSMVisitConfirmation(HttpCase):
|
||||||
"""Test the complete task approval flow"""
|
"""Test the complete task approval flow"""
|
||||||
# Test the FSM confirmation approve endpoint
|
# Test the FSM confirmation approve endpoint
|
||||||
self.authenticate(None, None)
|
self.authenticate(None, None)
|
||||||
url = f"/my/fsm_confirmation/{self.token}/approve"
|
url = f"/my/fsm_confirmation/approve?access_token={self.token}"
|
||||||
response = self.url_open(url)
|
response = self.url_open(url)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.status_code, 200, "FSM confirmation approve should succeed"
|
response.status_code, 200, "FSM confirmation approve should succeed"
|
||||||
|
|
@ -146,7 +146,7 @@ class TestFSMVisitConfirmation(HttpCase):
|
||||||
"""Test the complete task request changes flow"""
|
"""Test the complete task request changes flow"""
|
||||||
# Test the FSM confirmation change endpoint
|
# Test the FSM confirmation change endpoint
|
||||||
self.authenticate(None, None)
|
self.authenticate(None, None)
|
||||||
url = f"/my/fsm_confirmation/{self.token}/change"
|
url = f"/my/fsm_confirmation/change?access_token={self.token}"
|
||||||
response = self.url_open(url)
|
response = self.url_open(url)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.status_code, 200, "FSM confirmation change should succeed"
|
response.status_code, 200, "FSM confirmation change should succeed"
|
||||||
|
|
@ -157,7 +157,7 @@ class TestFSMVisitConfirmation(HttpCase):
|
||||||
response = self.url_open(
|
response = self.url_open(
|
||||||
url,
|
url,
|
||||||
data={
|
data={
|
||||||
"token": self.token,
|
"access_token": self.token,
|
||||||
"feedback": "Need some changes",
|
"feedback": "Need some changes",
|
||||||
"csrf_token": http.Request.csrf_token(self),
|
"csrf_token": http.Request.csrf_token(self),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@
|
||||||
<p>Please provide details about the changes you would like to make to this service visit:</p>
|
<p>Please provide details about the changes you would like to make to this service visit:</p>
|
||||||
<form action="/my/fsm_confirmation/submit_change" method="post">
|
<form action="/my/fsm_confirmation/submit_change" method="post">
|
||||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||||
<input type="hidden" name="token" t-att-value="request.params.get('token')"/>
|
<input type="hidden" name="access_token" t-att-value="request.params.get('access_token')"/>
|
||||||
|
<input type="hidden" name="lang" t-att-value="request.context.get('lang') or request.params.get('lang')"/>
|
||||||
|
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label for="feedback" class="form-label">Your Comments:</label>
|
<label for="feedback" class="form-label">Your Comments:</label>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue