This commit is contained in:
gsinghpal
2026-05-04 02:14:34 -04:00
parent 3cc393454d
commit 586f05d567
43 changed files with 3656 additions and 112 deletions

View File

@@ -26,7 +26,7 @@ _SNAPSHOT_FIELDS = [
'parallel_start',
'triggers_workflow_state_id', # Sub 14 — workflow milestone trigger
'requires_rack_assignment', 'requires_transition_form',
'default_kind',
'kind_id', # Sub 14b — replaces default_kind (now a related Char)
]
# Fields on fp.step.template.input that copy 1:1 into
@@ -90,6 +90,8 @@ class SimpleRecipeController(http.Controller):
'sequence': step.sequence,
'icon': step.icon,
'default_kind': step.default_kind,
'kind_id': step.kind_id.id if step.kind_id else False,
'kind_name': step.kind_id.name if step.kind_id else '',
'requires_signoff': step.requires_signoff,
'requires_rack_assignment': step.requires_rack_assignment,
'requires_transition_form': step.requires_transition_form,
@@ -150,6 +152,8 @@ class SimpleRecipeController(http.Controller):
'code': t.code,
'icon': t.icon,
'default_kind': t.default_kind,
'kind_id': t.kind_id.id if t.kind_id else False,
'kind_name': t.kind_id.name if t.kind_id else '',
'station_count': len(t.tank_ids),
}
for t in records
@@ -201,6 +205,8 @@ class SimpleRecipeController(http.Controller):
'code': tpl.code or '',
'icon': tpl.icon or 'fa-cog',
'default_kind': tpl.default_kind or '',
'kind_id': tpl.kind_id.id if tpl.kind_id else False,
'kind_name': tpl.kind_id.name if tpl.kind_id else '',
'description': tpl.description or '',
'requires_signoff': tpl.requires_signoff,
'requires_predecessor_done': tpl.requires_predecessor_done,
@@ -245,8 +251,11 @@ class SimpleRecipeController(http.Controller):
"""
Tpl = request.env['fp.step.template']
# Whitelist — never trust client-provided write_uid / id / etc.
# Sub 14b: `default_kind` is now a related read-only Char. The
# client may still send it as a string code for back-compat — we
# translate it to kind_id below.
allowed = {
'name', 'code', 'icon', 'default_kind', 'description',
'name', 'code', 'icon', 'kind_id', 'description',
'requires_signoff', 'requires_predecessor_done',
'parallel_start',
'triggers_workflow_state_id', # Sub 14
@@ -254,6 +263,11 @@ class SimpleRecipeController(http.Controller):
'tank_ids',
}
clean = {k: v for k, v in (vals or {}).items() if k in allowed}
# Back-compat: accept default_kind (string code) and resolve to kind_id.
if 'kind_id' not in clean and (vals or {}).get('default_kind'):
clean['kind_id'] = self._resolve_kind_id_from_code(
vals['default_kind'],
)
# tank_ids comes in as a plain list of ids from the OWL form;
# translate into the Odoo (6, 0, ids) command form.
if 'tank_ids' in clean:
@@ -266,6 +280,15 @@ class SimpleRecipeController(http.Controller):
tpl = Tpl.create(clean)
return {'ok': True, 'template': self._library_payload(tpl)}
def _resolve_kind_id_from_code(self, code):
"""Look up fp.step.kind id by code. Empty string → False."""
if not code:
return False
rec = request.env['fp.step.kind'].search(
[('code', '=', code)], limit=1,
)
return rec.id or False
@http.route('/fp/simple_recipe/library/seed_defaults', type='jsonrpc', auth='user')
def library_seed_defaults(self, template_id):
"""Run action_seed_default_inputs on this template. Idempotent —
@@ -340,6 +363,55 @@ class SimpleRecipeController(http.Controller):
],
}
@http.route('/fp/simple_recipe/kinds/list',
type='jsonrpc', auth='user')
def kinds_list(self):
"""Sub 14b — Step Kind dropdown options for the inline library
form. User-extensible via /fp/simple_recipe/kinds/create."""
Kind = request.env['fp.step.kind']
return {
'kinds': [
{
'id': k.id,
'code': k.code or '',
'name': k.name or '',
'icon': k.icon or '',
'sequence': k.sequence,
}
for k in Kind.search(
[('active', '=', True)], order='sequence, name',
)
],
}
@http.route('/fp/simple_recipe/kinds/create',
type='jsonrpc', auth='user')
def kinds_create(self, name, code=''):
"""Sub 14b — Inline create for "+ New kind…" in the library
form. Auto-derives a code from the name if blank."""
Kind = request.env['fp.step.kind']
if not name or not name.strip():
return {'ok': False, 'error': 'name_required'}
# check_access via create attempt — supervisors+ allowed (ACL).
if not code:
code = name.strip().lower().replace(' ', '_').replace('/', '_')
existing = Kind.search([('code', '=', code)], limit=1)
if existing:
return {
'ok': True, 'id': existing.id,
'name': existing.name, 'code': existing.code,
'duplicate': True,
}
rec = Kind.create({
'name': name.strip(),
'code': code,
})
return {
'ok': True, 'id': rec.id,
'name': rec.name, 'code': rec.code,
'duplicate': False,
}
@http.route('/fp/simple_recipe/workflow_states/list',
type='jsonrpc', auth='user')
def workflow_states_list(self):
@@ -457,7 +529,7 @@ class SimpleRecipeController(http.Controller):
node.check_access('write')
allowed = {
'name', 'description', 'icon',
'default_kind',
'kind_id', # Sub 14b — replaces default_kind
'requires_signoff', 'requires_predecessor_done',
'parallel_start', # Sub 13
'triggers_workflow_state_id', # Sub 14
@@ -467,6 +539,11 @@ class SimpleRecipeController(http.Controller):
'collect_measurements',
}
clean = {k: v for k, v in (vals or {}).items() if k in allowed}
# Back-compat: accept default_kind (string code) and resolve.
if 'kind_id' not in clean and (vals or {}).get('default_kind'):
clean['kind_id'] = self._resolve_kind_id_from_code(
vals['default_kind'],
)
if clean:
node.write(clean)
return {'ok': True}