fix(tablet): ACL, loading hang, timer offset + FP-tz clock

Four fixes shipped together — all surfaced during tablet UX walkthrough
on entech.

1. sale.order ACL on step completion
   Technicians hit "Access Denied... sale.order" when starting/finishing
   any step. _fp_check_receiving_gate + the serial-promotion helpers +
   _fp_resolve_contract_review_part read step.job_id.sale_order_id (and
   sale_order_line_ids) without sudo. Per Rule 13m, denormalized cross-
   module reads in tablet controllers must sudo() the source recordset.

2. Workspace stuck on "Loading Job Workspace…" after Hand Off + relogin
   Action params aren't URL-encoded, so the workspace remounts with
   jobId=null. refresh() exited early, state.data stayed null, "Loading"
   shown indefinitely. onMounted now redirects to the plant kanban
   when jobId is null or the initial load returns no data.

3. 4-hour timer offset on active steps
   workspace_controller used fp_format() to serialize date_started —
   which converts naive UTC to user tz wall time first. JS then
   appended 'Z' and parsed as UTC, so timer was offset by the user's
   tz (4h on EDT). Switched to fp_isoformat_utc() (proper +00:00 ISO)
   and dropped the Z-append in formatActiveStepElapsed +
   isActiveStepOvertime.

4. Lock-screen clock follows FP regional setting
   tablet_lock.js used d.getHours() / d.toLocaleDateString() — browser
   tz. Now /fp/tablet/tiles returns tz_name (fp_user_tz resolution:
   user.tz → company.x_fc_default_tz → UTC) and the formatters use
   Intl.DateTimeFormat with the explicit timeZone option. plant_overview
   now consumes server_time (already fp_format'd) instead of toLocaleTime
   String. Same chain Odoo backend uses, so PDF / view / tablet all
   agree on what time it is.

Versions: fusion_plating_jobs 19.0.10.30.0,
fusion_plating_shopfloor 19.0.33.1.12.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-24 20:31:25 -04:00
parent bfc138251a
commit 80f80fb707
8 changed files with 102 additions and 37 deletions

View File

@@ -889,7 +889,8 @@ class FpJobStep(models.Model):
skipped.
"""
for step in self:
job = step.job_id
# sudo() — technicians lack sale.order ACL (Rule 13m).
job = step.sudo().job_id
if not job.sale_order_line_ids:
continue
serials = job.sale_order_line_ids.mapped('x_fc_serial_ids')
@@ -909,7 +910,8 @@ class FpJobStep(models.Model):
in-flight serials to `inspected` so the shipper sees them ready
for packing. Conservative — only promotes from `in_process`."""
for step in self:
job = step.job_id
# sudo() — technicians lack sale.order ACL (Rule 13m).
job = step.sudo().job_id
if not job.sale_order_line_ids:
continue
# Is this the highest-sequence non-cancelled step on the job?
@@ -964,7 +966,8 @@ class FpJobStep(models.Model):
Falls through to None when no part can be resolved (no SO line,
SO line without x_fc_part_catalog_id, etc.)."""
self.ensure_one()
for so_line in self.job_id.sale_order_line_ids:
# sudo() — technicians lack sale.order ACL (Rule 13m).
for so_line in self.sudo().job_id.sale_order_line_ids:
if (so_line.x_fc_part_catalog_id
and 'fp.contract.review' in self.env):
return so_line.x_fc_part_catalog_id
@@ -1165,7 +1168,11 @@ class FpJobStep(models.Model):
for step in self:
if step._fp_is_contract_review_step():
continue
so = step.job_id.sale_order_id
# sudo() — technicians don't have sale.order ACL but the
# gate's purpose is checking a denormalized state field.
# Rule 13m: cross-module reads in tablet/floor controllers
# must sudo() the source recordset.
so = step.sudo().job_id.sale_order_id
if not so:
# Internal rework / no SO — gate doesn't apply.
continue