feat(jobs): add cross-module fields to fp.job via _inherit (Task 2.2)
5 of the 6 deferred fields from Phase 1 Task 1.4 land here in fusion_plating_jobs: - part_catalog_id (fp.part.catalog from configurator) - coating_config_id (fp.coating.config from configurator) - customer_spec_id (fusion.plating.customer.spec from quality) - portal_job_id (fusion.plating.portal.job from portal) - delivery_id (fusion.plating.delivery from logistics) qc_check_id deferred to Task 2.7 — its target model (fusion.plating.quality.check) still lives in fusion_plating_bridge_mrp and we don't depend on bridge_mrp from this module. Task 2.7 will address QC sourcing. 6 unit tests (5 field-presence + 1 integration creating linked records). Manifest 19.0.1.0.0 → 19.0.1.1.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:
@@ -3,7 +3,7 @@
|
|||||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating — Native Jobs',
|
'name': 'Fusion Plating — Native Jobs',
|
||||||
'version': '19.0.1.0.0',
|
'version': '19.0.1.1.0',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -3,4 +3,6 @@
|
|||||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
#
|
#
|
||||||
# Phase 2 of the native plating job model migration. Models are added
|
# Phase 2 of the native plating job model migration. Models are added
|
||||||
# task-by-task in Tasks 2.2 onwards. This file imports them as they land.
|
# task-by-task in Tasks 2.2 onwards.
|
||||||
|
|
||||||
|
from . import fp_job
|
||||||
|
|||||||
39
fusion_plating/fusion_plating_jobs/models/fp_job.py
Normal file
39
fusion_plating/fusion_plating_jobs/models/fp_job.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2026 Nexa Systems Inc.
|
||||||
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
|
#
|
||||||
|
# fp.job extension — cross-module fields that couldn't live in core
|
||||||
|
# because their target models are in dependent modules. Per spec §5.1
|
||||||
|
# this module is the umbrella that re-bundles the cross-module
|
||||||
|
# extensions for the native job flow.
|
||||||
|
#
|
||||||
|
# qc_check_id is deferred to Task 2.7 (the underlying QC model still
|
||||||
|
# lives in fusion_plating_bridge_mrp; we'll address its sourcing then).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class FpJob(models.Model):
|
||||||
|
_inherit = 'fp.job'
|
||||||
|
|
||||||
|
part_catalog_id = fields.Many2one(
|
||||||
|
'fp.part.catalog',
|
||||||
|
string='Part',
|
||||||
|
index=True,
|
||||||
|
)
|
||||||
|
coating_config_id = fields.Many2one(
|
||||||
|
'fp.coating.config',
|
||||||
|
string='Coating Configuration',
|
||||||
|
)
|
||||||
|
customer_spec_id = fields.Many2one(
|
||||||
|
'fusion.plating.customer.spec',
|
||||||
|
string='Customer Spec',
|
||||||
|
)
|
||||||
|
portal_job_id = fields.Many2one(
|
||||||
|
'fusion.plating.portal.job',
|
||||||
|
string='Portal Job',
|
||||||
|
)
|
||||||
|
delivery_id = fields.Many2one(
|
||||||
|
'fusion.plating.delivery',
|
||||||
|
string='Delivery',
|
||||||
|
)
|
||||||
2
fusion_plating/fusion_plating_jobs/tests/__init__.py
Normal file
2
fusion_plating/fusion_plating_jobs/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import test_fp_job_extensions
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestFpJobExtensions(TransactionCase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.partner = self.env['res.partner'].create({'name': 'Cust'})
|
||||||
|
self.product = self.env['product.product'].create({'name': 'Widget'})
|
||||||
|
|
||||||
|
def _make_job(self, **kw):
|
||||||
|
vals = {
|
||||||
|
'partner_id': self.partner.id,
|
||||||
|
'product_id': self.product.id,
|
||||||
|
'qty': 1.0,
|
||||||
|
}
|
||||||
|
vals.update(kw)
|
||||||
|
return self.env['fp.job'].create(vals)
|
||||||
|
|
||||||
|
def test_part_catalog_id_field_exists(self):
|
||||||
|
# Field added by fusion_plating_jobs via _inherit. Verify the
|
||||||
|
# field is registered on the model.
|
||||||
|
self.assertIn('part_catalog_id', self.env['fp.job']._fields)
|
||||||
|
self.assertEqual(
|
||||||
|
self.env['fp.job']._fields['part_catalog_id'].comodel_name,
|
||||||
|
'fp.part.catalog',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_coating_config_id_field_exists(self):
|
||||||
|
self.assertIn('coating_config_id', self.env['fp.job']._fields)
|
||||||
|
self.assertEqual(
|
||||||
|
self.env['fp.job']._fields['coating_config_id'].comodel_name,
|
||||||
|
'fp.coating.config',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_customer_spec_id_field_exists(self):
|
||||||
|
self.assertIn('customer_spec_id', self.env['fp.job']._fields)
|
||||||
|
self.assertEqual(
|
||||||
|
self.env['fp.job']._fields['customer_spec_id'].comodel_name,
|
||||||
|
'fusion.plating.customer.spec',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_portal_job_id_field_exists(self):
|
||||||
|
self.assertIn('portal_job_id', self.env['fp.job']._fields)
|
||||||
|
self.assertEqual(
|
||||||
|
self.env['fp.job']._fields['portal_job_id'].comodel_name,
|
||||||
|
'fusion.plating.portal.job',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delivery_id_field_exists(self):
|
||||||
|
self.assertIn('delivery_id', self.env['fp.job']._fields)
|
||||||
|
self.assertEqual(
|
||||||
|
self.env['fp.job']._fields['delivery_id'].comodel_name,
|
||||||
|
'fusion.plating.delivery',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_can_create_job_with_all_fields_set(self):
|
||||||
|
# End-to-end: create matching records in each target model
|
||||||
|
# and assign them to a fp.job. Verifies no schema-level issues.
|
||||||
|
catalog = self.env['fp.part.catalog'].create({
|
||||||
|
'name': 'TestPart',
|
||||||
|
'partner_id': self.partner.id,
|
||||||
|
'part_number': 'TEST-001',
|
||||||
|
})
|
||||||
|
# fp.coating.config requires a process_type_id
|
||||||
|
process_type = self.env['fusion.plating.process.type'].search([], limit=1)
|
||||||
|
if not process_type:
|
||||||
|
process_type = self.env['fusion.plating.process.type'].create({
|
||||||
|
'name': 'TestProcess',
|
||||||
|
})
|
||||||
|
coating = self.env['fp.coating.config'].create({
|
||||||
|
'name': 'TestCoat',
|
||||||
|
'process_type_id': process_type.id,
|
||||||
|
})
|
||||||
|
# fusion.plating.customer.spec requires name + code + spec_type (has default)
|
||||||
|
spec = self.env['fusion.plating.customer.spec'].create({
|
||||||
|
'name': 'TestSpec',
|
||||||
|
'code': 'TEST-SPEC-001',
|
||||||
|
})
|
||||||
|
# fusion.plating.portal.job requires name + partner_id
|
||||||
|
portal = self.env['fusion.plating.portal.job'].create({
|
||||||
|
'name': 'TestPortal',
|
||||||
|
'partner_id': self.partner.id,
|
||||||
|
})
|
||||||
|
# fusion.plating.delivery requires name (has default) + partner_id
|
||||||
|
delivery = self.env['fusion.plating.delivery'].create({
|
||||||
|
'partner_id': self.partner.id,
|
||||||
|
})
|
||||||
|
job = self._make_job(
|
||||||
|
part_catalog_id=catalog.id,
|
||||||
|
coating_config_id=coating.id,
|
||||||
|
customer_spec_id=spec.id,
|
||||||
|
portal_job_id=portal.id,
|
||||||
|
delivery_id=delivery.id,
|
||||||
|
)
|
||||||
|
self.assertEqual(job.part_catalog_id, catalog)
|
||||||
|
self.assertEqual(job.coating_config_id, coating)
|
||||||
|
self.assertEqual(job.customer_spec_id, spec)
|
||||||
|
self.assertEqual(job.portal_job_id, portal)
|
||||||
|
self.assertEqual(job.delivery_id, delivery)
|
||||||
Reference in New Issue
Block a user