# FP Step Kind — User-Extensible Model Date: 2026-05-04 Status: design + implementation ## Problem `default_kind` on `fp.step.template` is a hardcoded `Selection` of 24 entries. Users can't add new kinds (e.g. "Shot Peen", "Passivation"). The same Selection list is duplicated on `fusion.plating.process.node`. Default-input seeding (`DEFAULT_INPUTS_BY_KIND`) is also locked in Python — adding a new kind would require a code change + module update. ## Solution Convert `default_kind` from `Selection` → `Many2one('fp.step.kind')`. Move the default-input templates from a Python dict into seeded data records on a new `fp.step.kind.default.input` child model. Users add new kinds through the standard Odoo CRUD form. ## Models ### `fp.step.kind` | Field | Type | Notes | |---|---|---| | `code` | Char, required, unique per company | Technical key (lowercase). Stable XML IDs use this. | | `name` | Char, required, translated | UI label (e.g. "Cleaning") | | `sequence` | Integer | Order in dropdown | | `active` | Boolean default True | Archive instead of delete | | `icon` | Selection (reuses 24-icon list from `fp.process.node`) | Optional | | `description` | Html | Optional ops note | | `company_id` | Many2one res.company | Multi-co support | | `default_input_ids` | One2many → `fp.step.kind.default.input` | The seed list | ### `fp.step.kind.default.input` Same shape as `fp.step.template.input`: `name`, `input_type`, `target_unit`, `sequence`, `required`, `hint`, `selection_options`. Plus `kind_id` parent FK. ## Field changes ### `fp.step.template` - **add** `kind_id = Many2one('fp.step.kind', ondelete='restrict', string='Step Kind')` — user-facing input - **change** existing `default_kind` from `Selection(...)` to `default_kind = fields.Char(related='kind_id.code', store=True, readonly=True)` — back-compat shim. Lets every legacy `node.default_kind == 'cleaning'` comparison keep working without touching 30+ sites. Stored=True so existing search domains (`('default_kind', '=', 'foo')`) still work. ### `fusion.plating.process.node` - Same: add `kind_id`, convert `default_kind` to `related='kind_id.code'` stored Char. ### `fp.job.workflow.state.trigger_default_kinds` - **No change in Phase 1.** Stays a Char of comma-separated codes. The codes still match `kind_id.code` after migration. Phase 2 deferred convert to m2m. ## DEFAULT_INPUTS_BY_KIND Removed from `fp_step_template.py`. Lives in seed data XML. `action_seed_default_inputs` reads `tpl.kind_id.default_input_ids` instead of the dict. ## Migration `19.0.18.13.0/post-migrate.py` 1. SQL-read existing `default_kind` text values from `fp_step_template` and `fusion_plating_process_node` BEFORE Odoo recomputes the related field. 2. Build map `code → fp.step.kind.id` from seeded records. 3. SQL UPDATE `kind_id` per row. 4. Trigger recompute of stored related `default_kind` (or leave — values are already there from step 1). 5. Log counts. ## Why not drop `default_kind` entirely? - Comma-CSV `trigger_default_kinds` on workflow state still searches it. - 30+ comparison sites and existing search domains in views. - Stored related Char keeps it a single source of truth (m2o-derived) without a churn-PR through every dependent. ## View changes - `fp_step_template_views.xml` — replace `` with ``. Same for search filter and `group_by="default_kind"` → `group_by="kind_id"`. - `fp_process_node_views.xml` — same swap on the one occurrence. - New `fp_step_kind_views.xml` — list/form/menu so admins can manage kinds. Form embeds `default_input_ids` as inline list. ## Controller / JS changes - `simple_recipe_controller.py`: - Add `kind_id` (id) and `kind_name` (label) to all step-payload responses. - Accept `kind_id` int on `_save_recipe`/template-create endpoints. - Keep returning `default_kind` (the code Char) for back-compat with deployed editor sessions until cache flushes. - `simple_recipe_editor.js`: - Replace the `` block with a `