feat(sub12a): post_init_hook — backfill kind + seed step library
Extends the existing post_init_hook to also do (idempotent) Sub 12a work on first install / upgrade: 1. Backfill kind='step_input' on existing fusion_plating_process_node_input rows where kind IS NULL. 2. Seed fp.step.template from the ENP-ALUM-BASIC recipe's child nodes if the library is currently empty. Uses _STARTER_KIND_BY_NAME to map recipe-step names to default_kind values, then calls _seed_default_inputs() to populate the per-kind input rows. 3. Falls back to a 15-entry hard-coded minimal seed list if ENP-ALUM-BASIC doesn't exist on the target DB. All three operations no-op when the relevant state already exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,14 +12,23 @@ _logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def post_init_hook(env):
|
||||
"""Auto-detect a sensible default timezone on first install.
|
||||
"""Run on first install / module upgrade. Idempotent.
|
||||
|
||||
Sets ``res.company.x_fc_default_tz`` to the admin user's timezone
|
||||
(Odoo populates that from the browser on first login), falling back
|
||||
to the host server's timezone, then to ``America/Toronto`` as a
|
||||
last resort. Only writes when the field is still empty so re-installs
|
||||
never clobber a user's choice.
|
||||
Does several things, each guarded by an "is this already done?"
|
||||
check so re-running the hook doesn't clobber state:
|
||||
1. Auto-detect a sensible default timezone (original behavior).
|
||||
2. Sub 12a — backfill `kind='step_input'` on existing
|
||||
fusion.plating.process.node.input rows that pre-date the
|
||||
`kind` field.
|
||||
3. Sub 12a — seed fp.step.template with starter library entries
|
||||
derived from ENP-ALUM-BASIC if the library is currently empty.
|
||||
"""
|
||||
_seed_default_timezone(env)
|
||||
_backfill_node_input_kind(env)
|
||||
_seed_step_library_if_empty(env)
|
||||
|
||||
|
||||
def _seed_default_timezone(env):
|
||||
from .models.fp_tz import detect_default_tz
|
||||
|
||||
detected = detect_default_tz(env)
|
||||
@@ -30,3 +39,149 @@ def post_init_hook(env):
|
||||
'Fusion Plating: set default timezone for company %s -> %s',
|
||||
company.name, detected,
|
||||
)
|
||||
|
||||
|
||||
def _backfill_node_input_kind(env):
|
||||
"""Sub 12a — set kind='step_input' on rows that have NULL kind."""
|
||||
cr = env.cr
|
||||
cr.execute(
|
||||
"UPDATE fusion_plating_process_node_input "
|
||||
"SET kind = 'step_input' WHERE kind IS NULL"
|
||||
)
|
||||
if cr.rowcount:
|
||||
_logger.info(
|
||||
"Fusion Plating: backfilled kind='step_input' on %s "
|
||||
"fusion.plating.process.node.input rows", cr.rowcount,
|
||||
)
|
||||
|
||||
|
||||
# Mapping of recipe-step name → default_kind. Drives sane-default
|
||||
# input seeding on the starter library entries.
|
||||
_STARTER_KIND_BY_NAME = {
|
||||
'soak clean': 'cleaning',
|
||||
'electroclean': 'cleaning',
|
||||
'solvent clean': 'cleaning',
|
||||
'rinse': 'rinse',
|
||||
'primary rinse': 'rinse',
|
||||
'secondary rinse': 'rinse',
|
||||
'hot rinse': 'rinse',
|
||||
'final rinse': 'rinse',
|
||||
'etch': 'etch',
|
||||
'desmut': 'etch',
|
||||
'zincate': 'etch',
|
||||
'strip zincate': 'etch',
|
||||
'acid dip': 'etch',
|
||||
'water break test': 'wbf_test',
|
||||
'issue panels': 'mask',
|
||||
'racking': 'racking',
|
||||
'rack': 'racking',
|
||||
'e-nickel plate': 'plate',
|
||||
'electroless nickel plate': 'plate',
|
||||
'electroless nickel plating': 'plate',
|
||||
'drying': 'dry',
|
||||
'dry': 'dry',
|
||||
'de-rack': 'derack',
|
||||
'de-racking': 'derack',
|
||||
'inspection': 'inspect',
|
||||
'final inspection': 'final_inspect',
|
||||
'shipping': 'ship',
|
||||
}
|
||||
|
||||
|
||||
def _seed_step_library_if_empty(env):
|
||||
"""Sub 12a — seed fp.step.template starter library.
|
||||
|
||||
Source priority:
|
||||
1. ENP-ALUM-BASIC recipe's child nodes (best — reuses the
|
||||
author-curated step set).
|
||||
2. Hard-coded minimal list (fallback for fresh DBs).
|
||||
"""
|
||||
Tpl = env['fp.step.template']
|
||||
if Tpl.search_count([]):
|
||||
_logger.info(
|
||||
'Fusion Plating: step library already populated, skip seed',
|
||||
)
|
||||
return
|
||||
|
||||
Node = env['fusion.plating.process.node']
|
||||
src = Node.search([
|
||||
('node_type', '=', 'recipe'),
|
||||
'|', ('code', '=', 'ENP-ALUM-BASIC'),
|
||||
('name', 'ilike', 'ENP-ALUM-BASIC'),
|
||||
], limit=1)
|
||||
|
||||
if not src:
|
||||
_seed_minimal_library(env)
|
||||
return
|
||||
|
||||
seen = set()
|
||||
for child in src.child_ids:
|
||||
if child.node_type == 'step':
|
||||
_create_template_from_node(env, child, seen)
|
||||
else:
|
||||
for grandchild in child.child_ids:
|
||||
_create_template_from_node(env, grandchild, seen)
|
||||
|
||||
_logger.info(
|
||||
"Fusion Plating: seeded step library with %s entries from %s",
|
||||
len(seen), src.name,
|
||||
)
|
||||
|
||||
|
||||
def _create_template_from_node(env, node, seen):
|
||||
if not node.name or node.name.lower() in seen:
|
||||
return
|
||||
seen.add(node.name.lower())
|
||||
|
||||
kind = _STARTER_KIND_BY_NAME.get(node.name.lower())
|
||||
vals = {
|
||||
'name': node.name,
|
||||
'description': node.description or False,
|
||||
'icon': node.icon or 'fa-cog',
|
||||
'process_type_id': node.process_type_id.id,
|
||||
'requires_signoff': node.requires_signoff,
|
||||
'requires_predecessor_done': node.requires_predecessor_done,
|
||||
'default_kind': kind,
|
||||
}
|
||||
# Snapshot tank_ids if the node has them (added by Sub 12a;
|
||||
# existing nodes may not).
|
||||
if 'tank_ids' in node._fields and node.tank_ids:
|
||||
vals['tank_ids'] = [(6, 0, node.tank_ids.ids)]
|
||||
# Snapshot any time/temp targets the node may already carry.
|
||||
for f in ('time_min_target', 'time_max_target', 'time_unit',
|
||||
'temp_min_target', 'temp_max_target', 'temp_unit'):
|
||||
if f in node._fields:
|
||||
vals[f] = node[f] or vals.get(f)
|
||||
|
||||
tpl = env['fp.step.template'].create(vals)
|
||||
if kind:
|
||||
tpl._seed_default_inputs()
|
||||
|
||||
|
||||
def _seed_minimal_library(env):
|
||||
"""Hard-coded minimal seed when ENP-ALUM-BASIC isn't on the target DB."""
|
||||
Tpl = env['fp.step.template']
|
||||
minimal = [
|
||||
('Soak Clean', 'cleaning'),
|
||||
('Electroclean', 'cleaning'),
|
||||
('Rinse', 'rinse'),
|
||||
('Etch', 'etch'),
|
||||
('Desmut', 'etch'),
|
||||
('Zincate', 'etch'),
|
||||
('Acid Dip', 'etch'),
|
||||
('Water Break Test', 'wbf_test'),
|
||||
('Racking', 'racking'),
|
||||
('De-Racking', 'derack'),
|
||||
('E-Nickel Plate', 'plate'),
|
||||
('Drying', 'dry'),
|
||||
('Inspection', 'inspect'),
|
||||
('Final Inspection', 'final_inspect'),
|
||||
('Shipping', 'ship'),
|
||||
]
|
||||
for name, kind in minimal:
|
||||
tpl = Tpl.create({'name': name, 'default_kind': kind})
|
||||
tpl._seed_default_inputs()
|
||||
_logger.info(
|
||||
'Fusion Plating: seeded minimal step library (%s entries)',
|
||||
len(minimal),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user