fix(simple-editor): HTML in chatter + library form + expand per-step inline edit
Three fixes from user feedback:
1. Chatter posting raw HTML
_AUDIT_BODY in migration 19.0.18.8.0 was a plain str with <p>
tags. message_post escaped it for safety, so the chatter pill
rendered '<p><strong>...</strong></p>' literally to the recipe
author. Wrapped in markupsafe.Markup so Odoo recognises it as
safe HTML. Going forward: ANY message_post body containing HTML
tags MUST be wrapped in Markup() — most callers already do this,
the migration script was the outlier.
2. Library template editor showed raw <p> tags
onOpenLibraryEdit was JSON-cloning the payload directly without
running description through the existing _htmlToText helper that
the per-step editor uses. Added the conversion. Save path
(onSaveLibraryEditor + library_save) already wraps via
_textToHtml so storage stays HTML-compatible.
3. Per-step inline form was missing critical fields — user had to
delete + re-add a step to change Type/workflow trigger/parallel/signoff
onToggleEdit now also captures default_kind, triggers_workflow_state_id,
parallel_start, requires_signoff into the edit state. onSaveStep
sends them in the write vals. Added _fpResetStepEdit helper to
keep open/cancel/save reset paths in sync.
New per-step form has:
* Step Type (Default Kind) dropdown — drives workflow milestone
triggers + step-kind routing (e.g. contract_review opens QA-005)
* Triggers Workflow State dropdown (Sub 14) — per-step override
* Parallel Start checkbox (Sub 13)
* Require QA Sign-off checkbox
step_write controller endpoint also gained a field whitelist —
was previously accepting any vals dict from the client (security
hole + opaque to maintainers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,15 @@ class SimpleRecipeController(http.Controller):
|
||||
'work_center_id': step.work_center_id.id if step.work_center_id else False,
|
||||
'source_template_id': step.source_template_id.id or False,
|
||||
'collect_measurements': bool(step.collect_measurements),
|
||||
# Sub 13 — per-step opt-out of the sequential gate
|
||||
'parallel_start': bool(step.parallel_start),
|
||||
# Sub 14 — workflow milestone trigger override
|
||||
'triggers_workflow_state_id': (
|
||||
step.triggers_workflow_state_id.id
|
||||
if 'triggers_workflow_state_id' in step._fields
|
||||
and step.triggers_workflow_state_id
|
||||
else False
|
||||
),
|
||||
'measurements_badge_text': badge_text,
|
||||
'measurements_badge_class': badge_class,
|
||||
'inputs': [
|
||||
@@ -437,8 +446,29 @@ class SimpleRecipeController(http.Controller):
|
||||
|
||||
@http.route('/fp/simple_recipe/step/write', type='jsonrpc', auth='user')
|
||||
def step_write(self, node_id, vals):
|
||||
node = request.env['fusion.plating.process.node'].browse(node_id)
|
||||
node.write(vals)
|
||||
"""Update fields on an existing recipe step (operation node).
|
||||
|
||||
Whitelisted to the fields the inline edit panel actually surfaces
|
||||
— never trust client-provided node_type / parent_id / etc.
|
||||
"""
|
||||
node = request.env['fusion.plating.process.node'].browse(int(node_id))
|
||||
if not node.exists():
|
||||
return {'ok': False, 'error': 'not_found'}
|
||||
node.check_access('write')
|
||||
allowed = {
|
||||
'name', 'description', 'icon',
|
||||
'default_kind',
|
||||
'requires_signoff', 'requires_predecessor_done',
|
||||
'parallel_start', # Sub 13
|
||||
'triggers_workflow_state_id', # Sub 14
|
||||
'requires_rack_assignment',
|
||||
'requires_transition_form',
|
||||
'estimated_duration',
|
||||
'collect_measurements',
|
||||
}
|
||||
clean = {k: v for k, v in (vals or {}).items() if k in allowed}
|
||||
if clean:
|
||||
node.write(clean)
|
||||
return {'ok': True}
|
||||
|
||||
@http.route('/fp/simple_recipe/step/remove', type='jsonrpc', auth='user')
|
||||
|
||||
Reference in New Issue
Block a user