feat(jobs): add equipment, audit, plating-spec fields to fp.job.step

Equipment: bath_id, tank_id, rack_id (all in core). oven_id deferred
to a bridge module — fusion.plating.bake.oven lives in shopfloor and
core can't depend on it. masking_material_id deferred too — model
fusion.plating.masking.material does not yet exist anywhere; will be
added when the masking model lands.

Audit: signoff_user_id (readonly), facility_id (related from
work_centre_id, stored).

Plating spec: thickness_target, thickness_uom (um/mil/in),
dwell_time_minutes, bake_setpoint_temp, bake_actual_duration,
bake_chart_recorder_ref (Nadcap audit trail).

Recipe-related: requires_signoff, auto_complete, is_manual,
customer_visible (all related from recipe_node_id, stored, so
operator sees current values without re-querying process.node).

Cost rollup: cost_per_hour related from work_centre_id, cost_total
computed (duration_actual / 60 x rate), currency_id related too.
Full rollup-from-timelogs lands in Task 1.7.

Tests cover: facility_id related-field, thickness_uom default,
cost_total zero/non-zero paths.

Manifest 19.0.8.4.1 -> 19.0.8.5.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-24 22:15:26 -04:00
parent 688fe8317c
commit 91767f9f03
3 changed files with 110 additions and 1 deletions

View File

@@ -71,3 +71,35 @@ class TestFpJobStepStateMachine(TransactionCase):
self.job.invalidate_recordset(['step_done_count', 'step_progress_pct'])
self.assertEqual(self.job.step_done_count, 1)
self.assertAlmostEqual(self.job.step_progress_pct, 33.33, places=1)
def test_facility_id_related_from_work_centre(self):
# Work centre with a facility -> step inherits via related field.
facility = self.env['fusion.plating.facility'].create({
'name': 'Test Facility',
'code': 'TFAC',
})
wc = self.env['fp.work.centre'].create({
'name': 'WC2', 'code': 'WC2', 'kind': 'wet_line',
'facility_id': facility.id,
})
step = self._make_step(work_centre_id=wc.id)
self.assertEqual(step.facility_id, facility)
def test_thickness_uom_default(self):
step = self._make_step()
self.assertEqual(step.thickness_uom, 'um')
def test_cost_total_zero_when_no_duration(self):
step = self._make_step()
self.assertEqual(step.cost_total, 0.0)
def test_cost_total_with_duration_and_rate(self):
wc = self.env['fp.work.centre'].create({
'name': 'WC3', 'code': 'WC3', 'kind': 'wet_line',
'cost_per_hour': 60.0, # $1/min
})
step = self._make_step(work_centre_id=wc.id)
# Force duration_actual since we don't have timelogs in 1.6
step.duration_actual = 30.0
# Recompute happens on read after a write to a depends field
self.assertEqual(step.cost_total, 30.0)