feat(jobs): Phase 7 — migration script + legacy id fields

Adds legacy_mrp_production_id (Integer index) on fp.job and
legacy_mrp_workorder_id on fp.job.step. Used as the idempotency
key during cutover migration.

Three scripts under fusion_plating_jobs/scripts/:
- audit_pre_migration.py   — counts and data-quality concerns BEFORE
- migrate_to_fp_jobs.py    — copies MO->fp.job, WO->fp.job.step, time
                             logs, rebinds cross-refs (batches,
                             holds, certs, readings, portals,
                             inspections, deliveries). Idempotent.
- audit_post_migration.py  — counts and verifies AFTER

Migration is run manually from \`odoo shell\` at cutover (not as
auto post-migration hook, for safety). README explains usage.

Tests verify the legacy id fields exist and the migration script
files are well-formed Python.

Manifest 19.0.1.9.0 -> 19.0.2.0.0.

Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-25 00:15:23 -04:00
parent 71376228cb
commit f9fab699d4
8 changed files with 901 additions and 1 deletions

View File

@@ -574,3 +574,63 @@ class TestPhase6Controllers(TransactionCase):
step_by_node = {s.recipe_node_id.id: s for s in self.job.step_ids if s.recipe_node_id}
self.assertIn(op.id, step_by_node)
self.assertEqual(step_by_node[op.id].name, 'Op1')
class TestPhase7Migration(TransactionCase):
"""Phase 7 — verify the migration script idempotency-key fields are
in place and the script files are present + parse as valid Python.
We cannot run the migration end-to-end in a unit test (it would need
a populated MO/WO snapshot). Instead we assert the scaffolding is
solid: fields exist, files are well-formed.
"""
def test_legacy_id_field_on_fp_job(self):
self.assertIn(
'legacy_mrp_production_id',
self.env['fp.job']._fields,
)
# Should be Integer (we store the raw db id, not a Many2one — the
# source MO may be archived later without breaking the link).
self.assertEqual(
self.env['fp.job']._fields['legacy_mrp_production_id'].type,
'integer',
)
def test_legacy_id_field_on_fp_job_step(self):
self.assertIn(
'legacy_mrp_workorder_id',
self.env['fp.job.step']._fields,
)
self.assertEqual(
self.env['fp.job.step']._fields['legacy_mrp_workorder_id'].type,
'integer',
)
def test_migration_script_files_exist_and_parse(self):
# Sanity check that the script files we ship are valid Python.
# Catches syntax errors that would otherwise only surface on the
# cutover engineer's screen at the worst possible moment.
import ast
from pathlib import Path
scripts_dir = (
Path(__file__).parent.parent / 'scripts'
)
for script in (
'audit_pre_migration.py',
'migrate_to_fp_jobs.py',
'audit_post_migration.py',
):
path = scripts_dir / script
self.assertTrue(path.exists(), '%s missing' % script)
with open(path) as f:
ast.parse(f.read()) # Will raise SyntaxError if invalid
def test_scripts_dir_is_a_python_package(self):
# __init__.py exists so Odoo's autodiscovery doesn't trip and the
# dir is importable for hypothetical future post-migration hooks.
from pathlib import Path
init = (
Path(__file__).parent.parent / 'scripts' / '__init__.py'
)
self.assertTrue(init.exists())