Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion project_worksheet/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Project Worksheet",
"summary": "Manage field worksheets for project interventions",
"version": "14.0.2.1.0",
"version": "14.0.3.0.0",
"category": "Project Management",
"website": "https://github.com/OCA/project",
"author": "Numigi, Odoo Community Association (OCA)",
Expand Down
15 changes: 15 additions & 0 deletions project_worksheet/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,26 @@ class ProjectWorksheetPortal(http.Controller):
)
def portal_worksheet_view(self, worksheet_id, access_token=None, **kw):
worksheet = self._get_worksheet(worksheet_id, access_token)
backend_redirect = self._redirect_internal_user_to_backend(worksheet)
if backend_redirect:
return backend_redirect
return request.render(
"project_worksheet.portal_worksheet_template",
{"worksheet": worksheet, "error": kw.get("error")}
)

def _redirect_internal_user_to_backend(self, worksheet):
# The portal link shared with the client must not expose the client
# portal page to internal Odoo users: send them to the backend form
# instead (same behaviour as a sale order).
user = request.env.user
if user.has_group("base.group_user"):
return request.redirect(self._get_backend_form_url(worksheet))
return None

def _get_backend_form_url(self, worksheet):
return "/web#id=%s&model=%s&view_type=form" % (worksheet.id, worksheet._name)

@http.route(
["/my/worksheet/<int:worksheet_id>/accept"],
type="http",
Expand Down
120 changes: 95 additions & 25 deletions project_worksheet/i18n/fr.po
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-16 08:53+0000\n"
"PO-Revision-Date: 2026-09-16 08:53+0000\n"
"POT-Creation-Date: 2026-10-02 09:10+0000\n"
"PO-Revision-Date: 2026-10-02 09:10+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: project_worksheet
#: model:mail.template,report_name:project_worksheet.email_template_worksheet_confirmed
msgid "${object.name}"
msgstr "Feuille_installation_${object.name}"

#. module: project_worksheet
#: model:ir.actions.report,print_report_name:project_worksheet.action_report_project_worksheet
msgid "'Worksheet - %s' % (object.name)"
Expand Down Expand Up @@ -158,6 +163,11 @@ msgstr "<span style=\"font-size: 12px;\" class=\"text-muted\">Date et Heure</spa
msgid "<span style=\"font-size: 12px;\" class=\"text-muted\">Printed Name</span>"
msgstr "<span style=\"font-size: 12px;\" class=\"text-muted\">Nom imprimé</span>"

#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.portal_worksheet_template
msgid "<strong>Notes:</strong>"
msgstr "<strong>Notes :</strong>"

#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.portal_worksheet_template
msgid "<strong>Project:</strong>"
Expand Down Expand Up @@ -203,6 +213,11 @@ msgstr "Statut de l'activité"
msgid "Activity Type Icon"
msgstr "Icône de type d'activité"

#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.project_worksheet_view_form
msgid "Add internal notes here..."
msgstr "Ajouter des notes internes ici..."

#. module: project_worksheet
#: model:ir.model,name:project_worksheet.model_account_analytic_line
msgid "Analytic Line"
Expand Down Expand Up @@ -301,6 +316,7 @@ msgstr "URL du portail client"

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet_line__date
#: model_terms:ir.ui.view,arch_db:project_worksheet.portal_worksheet_template
#: model_terms:ir.ui.view,arch_db:project_worksheet.report_worksheet_document
msgid "Date"
msgstr "Date"
Expand Down Expand Up @@ -493,6 +509,11 @@ msgstr "Délai d'approbation responsable"
msgid "Manager Approval Rules"
msgstr "Règles d'approbation responsable"

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet__manager_approver_id
msgid "Manager Approver"
msgstr "Approbateur responsable"

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet__message_has_error
msgid "Message Delivery error"
Expand Down Expand Up @@ -545,6 +566,12 @@ msgstr ""
"Aucun montant n'apparaît sur ce document. La facturation est établie "
"séparément par l'administration."

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet__internal_notes
#: model_terms:ir.ui.view,arch_db:project_worksheet.project_worksheet_view_form
msgid "Notes"
msgstr "Notes"

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet__message_needaction_counter
msgid "Number of Actions"
Expand Down Expand Up @@ -582,6 +609,14 @@ msgstr "Nombre de messages avec erreur d'envoi"
msgid "Number of unread messages"
msgstr "Nombre de messages non lus"

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid "Only a worksheet manager can reset a confirmed worksheet to draft."
msgstr ""
"Seul un responsable des feuilles d'installation peut remettre en brouillon "
"une feuille confirmée."

#. module: project_worksheet
#: model:ir.model.fields.selection,name:project_worksheet.selection__project_worksheet__state__open
#: model_terms:ir.ui.view,arch_db:project_worksheet.project_worksheet_view_form
Expand Down Expand Up @@ -679,6 +714,11 @@ msgstr "Rechercher des feuilles d'installation"
msgid "Security Token"
msgstr "Jeton de sécurité"

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet_line__sequence
msgid "Sequence"
msgstr "Séquence"

#. module: project_worksheet
#: model:ir.actions.act_window,name:project_worksheet.project_worksheet_config_settings_action
#: model:ir.ui.menu,name:project_worksheet.menu_project_worksheet_settings
Expand All @@ -690,6 +730,16 @@ msgstr "Paramètres"
msgid "Site"
msgstr "Site"

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid ""
"Some timesheets are already locked. A timesheet administrator must unlock "
"them first."
msgstr ""
"Certaines feuilles de temps sont déjà verrouillées. Un administrateur des "
"feuilles de temps doit d'abord les déverrouiller."

#. module: project_worksheet
#: model:ir.model.fields,field_description:project_worksheet.field_project_worksheet__state
#: model_terms:ir.ui.view,arch_db:project_worksheet.project_worksheet_view_search
Expand Down Expand Up @@ -732,7 +782,19 @@ msgstr "Tâche"
#: code:addons/project_worksheet/models/project_worksheet_line.py:0
#, python-format
msgid "The date of a worksheet line must fall within the worksheet period."
msgstr "La date d'une ligne de feuille d'installation doit être comprise dans la période de la feuille."
msgstr ""
"La date d'une ligne de feuille d'installation doit être comprise dans la "
"période de la feuille."

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid ""
"The period start and end dates must fall within the same calendar week "
"(Monday to Sunday)."
msgstr ""
"Les dates de début et de fin de période doivent se situer dans la même "
"semaine calendaire (du lundi au dimanche)."

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
Expand Down Expand Up @@ -839,16 +901,17 @@ msgstr "Feuille d'installation approuvée par le client depuis l'IP : %s"
msgid "Worksheet approved by the client."
msgstr "Feuille d'installation approuvée par le client."

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid "Worksheet approved internally by manager: %s"
msgstr "Feuille d'installation approuvée en interne par le responsable : %s"

#. module: project_worksheet
#: model:ir.model.fields,help:project_worksheet.field_account_analytic_line__worksheet_id
msgid "Worksheet associated with this timesheet entry."
msgstr "Feuille d'installation associée à cette entrée de feuille de temps."

#. module: project_worksheet
#: model:mail.template,report_name:project_worksheet.email_template_worksheet_confirmed
msgid "Worksheet_${object.name}"
msgstr "Feuille_installation_${object.name}"

#. module: project_worksheet
#: model:ir.actions.act_window,name:project_worksheet.project_worksheet_action
#: model:ir.module.category,name:project_worksheet.module_category_project_worksheet
Expand All @@ -864,19 +927,37 @@ msgstr "Feuilles d'installation"
#: model:ir.cron,cron_name:project_worksheet.ir_cron_remind_pending_worksheets
#: model:ir.cron,name:project_worksheet.ir_cron_remind_pending_worksheets
msgid "Worksheets: Remind pending client approvals"
msgstr "Feuilles d'installation : Relancer les approbations clients en attente"
msgstr "Feuilles de chantier : Relancer les approbations clients en attente"

#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.portal_worksheet_template
msgid "You are not authorized to approve this worksheet."
msgstr "Vous n'êtes pas autorisé à approuver cette feuille d'installation."

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid "You can only delete a worksheet in the 'New' state"
msgstr ""
"Vous ne pouvez supprimer une feuille d'installation que lorsqu'elle est à "
"l'état « Nouveau »."

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid "You cannot approve a worksheet with no lines or zero total hours."
msgstr "Vous ne pouvez pas approuver une feuille d'installation sans lignes ou avec un total d'heures à zéro."
msgstr ""
"Vous ne pouvez pas approuver une feuille d'installation sans lignes ou avec "
"un total d'heures à zéro."

#. module: project_worksheet
#: code:addons/project_worksheet/models/account_analytic_line.py:0
#: code:addons/project_worksheet/models/project_worksheet.py:0
#, python-format
msgid "You cannot delete a timesheet line linked to a worksheet."
msgstr "Vous ne pouvez pas supprimer une ligne de feuille de temps liée à une feuille d'installation."
msgid ""
"You cannot have overlapping worksheets for the same project and supervisor."
msgstr ""
"Vous ne pouvez pas avoir de feuilles d'installation qui se chevauchent pour "
"le même projet et le même superviseur."

#. module: project_worksheet
#: code:addons/project_worksheet/models/account_analytic_line.py:0
Expand All @@ -886,12 +967,6 @@ msgstr ""
"Vous ne pouvez pas modifier les feuilles de temps liées à une feuille "
"d'installation approuvée."

#. module: project_worksheet
#: code:addons/project_worksheet/models/project_worksheet_line.py:0
#, python-format
msgid "You cannot record timesheets on a parent task."
msgstr "Vous ne pouvez pas enregistrer de feuilles de temps sur une tâche parente."

#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.portal_worksheet_template
msgid "You must check the confirmation box to proceed."
Expand All @@ -905,9 +980,4 @@ msgstr "jours"
#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.report_worksheet_document
msgid "to"
msgstr "au"

#. module: project_worksheet
#: model_terms:ir.ui.view,arch_db:project_worksheet.portal_worksheet_template
msgid "You are not authorized to approve this worksheet."
msgstr "Vous n'êtes pas autorisé à approuver cette feuille d'installation."
msgstr "au"
28 changes: 20 additions & 8 deletions project_worksheet/models/account_analytic_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ class AccountAnalyticLine(models.Model):
help="Worksheet associated with this timesheet entry.",
)

@api.constrains("worksheet_id", "unit_amount", "task_id")
@api.constrains("worksheet_id", "unit_amount", "task_id", "date", "employee_id", "name")
def _check_locked_worksheet(self):
# Prevent modification if the associated worksheet is approved
locked_lines = (line for line in self if self._is_worksheet_approved(line))
for _line in locked_lines:
if self._is_worksheet_lock_bypassed():
return
for line in self:
line._validate_not_linked_to_worksheet()

def _is_worksheet_lock_bypassed(self):
# Verify if the action is triggered internally by the worksheet synchronization
return self.env.context.get("bypass_worksheet_lock")

def _validate_not_linked_to_worksheet(self):
if self.worksheet_id:
self._raise_locked_worksheet_error()

def _is_worksheet_approved(self, line):
Expand All @@ -35,15 +43,19 @@ def _raise_locked_worksheet_error(self):
)

def unlink(self):
self._check_unlink_conditions()
if not self._is_worksheet_lock_bypassed():
self._check_unlink_conditions()
return super().unlink()

def _check_unlink_conditions(self):
linked_lines = (line for line in self if line.worksheet_id)
for _line in linked_lines:
for line in self:
line._validate_unlink_allowed()

def _validate_unlink_allowed(self):
if self.worksheet_id:
self._raise_unlink_linked_line_error()

def _raise_unlink_linked_line_error(self):
raise UserError(
_("You cannot delete a timesheet line linked to a worksheet.")
"You cannot delete a timesheet line linked to a worksheet."
)
Loading
Loading