feat(jobs): add x_fc_use_native_jobs flag + SO confirm hook (Task 2.5)

Settings flag controls which SO confirm path runs. Default False
keeps the legacy bridge_mrp / mrp.production flow on entech.
Setting True diverts confirm into fp.job creation.

Both hooks coexist — bridge_mrp's _fp_auto_create_mo and the new
_fp_auto_create_job — but only one creates records per SO confirm
(controlled by the flag).

The new _fp_auto_create_job mirrors bridge_mrp's grouping logic
(x_fc_wo_group_tag), recipe resolution (coating → part), and
traceability fields (origin, sale_order_line_ids).

Settings UI shows the flag in a 'Fusion Plating Jobs' app section
of the standard Configuration menu.

3 new tests cover: flag off no-op, flag on creates job, idempotency.

Manifest 19.0.1.3.0 → 19.0.1.4.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 23:22:41 -04:00
parent 3b7eae9b78
commit 294cea0e50
6 changed files with 243 additions and 1 deletions

View File

@@ -260,3 +260,75 @@ class TestFpJobStepsGenerator(TransactionCase):
self.assertEqual(first_step.duration_expected, 30.0)
# Work centre resolved by code from legacy model
self.assertEqual(first_step.work_centre_id, self.wc)
class TestSoConfirmHook(TransactionCase):
def setUp(self):
super().setUp()
self.partner = self.env['res.partner'].create({'name': 'C'})
self.product = self.env['product.product'].create({'name': 'P'})
self.ICP = self.env['ir.config_parameter'].sudo()
def _make_so_with_plating_line(self, **line_vals):
# client_order_ref satisfies the fusion_plating_invoicing PO# gate.
so_vals = {
'partner_id': self.partner.id,
'client_order_ref': 'TEST-PO-001',
}
so = self.env['sale.order'].create(so_vals)
line_defaults = {
'order_id': so.id,
'product_id': self.product.id,
'product_uom_qty': 5.0,
'price_unit': 10.0,
}
line_defaults.update(line_vals)
self.env['sale.order.line'].create(line_defaults)
return so
def test_flag_off_no_job_created(self):
# Default flag is False
self.ICP.set_param('fusion_plating_jobs.use_native_jobs', 'False')
so = self._make_so_with_plating_line()
so.action_confirm()
jobs = self.env['fp.job'].search([('sale_order_id', '=', so.id)])
self.assertFalse(jobs)
def test_flag_on_creates_job(self):
self.ICP.set_param('fusion_plating_jobs.use_native_jobs', 'True')
# Need a plating line — add x_fc_part_catalog_id if available
if 'x_fc_part_catalog_id' in self.env['sale.order.line']._fields:
partner_for_part = self.env['res.partner'].create({'name': 'PartOwner'})
part = self.env['fp.part.catalog'].create({
'name': 'TPart', 'part_number': 'TP-1',
'partner_id': partner_for_part.id,
})
so = self._make_so_with_plating_line(x_fc_part_catalog_id=part.id)
so.action_confirm()
jobs = self.env['fp.job'].search([('sale_order_id', '=', so.id)])
self.assertEqual(len(jobs), 1)
self.assertEqual(jobs.qty, 5.0)
self.assertEqual(jobs.part_catalog_id, part)
self.assertEqual(jobs.origin, so.name)
else:
self.skipTest('x_fc_part_catalog_id field not present on sale.order.line')
def test_flag_on_idempotent(self):
self.ICP.set_param('fusion_plating_jobs.use_native_jobs', 'True')
if 'x_fc_part_catalog_id' in self.env['sale.order.line']._fields:
partner_for_part = self.env['res.partner'].create({'name': 'PO'})
part = self.env['fp.part.catalog'].create({
'name': 'IdemPart', 'part_number': 'IP-1',
'partner_id': partner_for_part.id,
})
so = self._make_so_with_plating_line(x_fc_part_catalog_id=part.id)
so.action_confirm()
count_after_first = self.env['fp.job'].search_count(
[('sale_order_id', '=', so.id)])
# Calling action_confirm again should NOT create a duplicate
so._fp_auto_create_job()
count_after_second = self.env['fp.job'].search_count(
[('sale_order_id', '=', so.id)])
self.assertEqual(count_after_first, count_after_second)
else:
self.skipTest('x_fc_part_catalog_id field not present')