feat(jobs): add fp.work.centre native model
Replaces mrp.workcenter for plating. Domain-specific kinds (wet_line/bake/mask/rack/inspect/other) drive release-ready validation on steps. ACLs follow existing supervisor/manager hierarchy. Note: spec listed default_oven_id Many2one to fusion.plating.oven but that model does not exist in core (the bake-oven model fusion.plating.bake.oven lives in fusion_plating_shopfloor, which core cannot depend on). Field omitted; the bridge that adds fp.job/fp.job.step can re-introduce it via _inherit later. 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:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating',
|
'name': 'Fusion Plating',
|
||||||
'version': '19.0.8.0.0',
|
'version': '19.0.8.1.0',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from . import fp_process_category
|
|||||||
from . import fp_process_type
|
from . import fp_process_type
|
||||||
from . import fp_facility
|
from . import fp_facility
|
||||||
from . import fp_work_center
|
from . import fp_work_center
|
||||||
|
from . import fp_work_centre
|
||||||
from . import fp_tank
|
from . import fp_tank
|
||||||
from . import fp_bath
|
from . import fp_bath
|
||||||
from . import fp_bath_log
|
from . import fp_bath_log
|
||||||
|
|||||||
58
fusion_plating/fusion_plating/models/fp_work_centre.py
Normal file
58
fusion_plating/fusion_plating/models/fp_work_centre.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2026 Nexa Systems Inc.
|
||||||
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
|
#
|
||||||
|
# fp.work.centre — native plating work-centre model.
|
||||||
|
#
|
||||||
|
# Replaces mrp.workcenter for the plating flow. Plating work centres
|
||||||
|
# are domain-specific (a tank line, a bake oven, a rack station — not
|
||||||
|
# assembly cells). Each centre has a 'kind' that drives release-ready
|
||||||
|
# validation on fp.job.step (e.g. wet_line -> bath+tank required).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class FpWorkCentre(models.Model):
|
||||||
|
_name = 'fp.work.centre'
|
||||||
|
_description = 'Plating Work Centre'
|
||||||
|
_order = 'sequence, code, name'
|
||||||
|
|
||||||
|
name = fields.Char(required=True)
|
||||||
|
code = fields.Char(required=True, help='Short code used on stickers and reports.')
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
facility_id = fields.Many2one(
|
||||||
|
'fusion.plating.facility',
|
||||||
|
string='Facility',
|
||||||
|
)
|
||||||
|
kind = fields.Selection(
|
||||||
|
[
|
||||||
|
('wet_line', 'Wet Line'),
|
||||||
|
('bake', 'Bake Oven'),
|
||||||
|
('mask', 'Masking'),
|
||||||
|
('rack', 'Racking'),
|
||||||
|
('inspect', 'Inspection'),
|
||||||
|
('other', 'Other'),
|
||||||
|
],
|
||||||
|
required=True,
|
||||||
|
default='other',
|
||||||
|
)
|
||||||
|
cost_per_hour = fields.Monetary(
|
||||||
|
currency_field='currency_id',
|
||||||
|
help='Used for fp.job.step cost rollups.',
|
||||||
|
)
|
||||||
|
currency_id = fields.Many2one(
|
||||||
|
'res.currency',
|
||||||
|
default=lambda self: self.env.company.currency_id,
|
||||||
|
)
|
||||||
|
default_bath_id = fields.Many2one('fusion.plating.bath')
|
||||||
|
default_tank_id = fields.Many2one('fusion.plating.tank')
|
||||||
|
# NOTE: `default_oven_id` from the spec/plan is omitted here — the
|
||||||
|
# `fusion.plating.bake.oven` model lives in fusion_plating_shopfloor,
|
||||||
|
# which the core module cannot depend on. The bridge module that
|
||||||
|
# introduces fp.job/fp.job.step (Task 1.x) can re-introduce this
|
||||||
|
# field via _inherit if/when the bake-oven coupling is needed.
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('unique_code', 'UNIQUE(code)', 'Work centre code must be unique.'),
|
||||||
|
]
|
||||||
@@ -44,3 +44,6 @@ access_fp_replenishment_suggestion_manager,fp.replenishment.suggestion.manager,m
|
|||||||
access_fp_operator_cert_operator,fp.operator.cert.operator,model_fp_operator_certification,group_fusion_plating_operator,1,0,0,0
|
access_fp_operator_cert_operator,fp.operator.cert.operator,model_fp_operator_certification,group_fusion_plating_operator,1,0,0,0
|
||||||
access_fp_operator_cert_supervisor,fp.operator.cert.supervisor,model_fp_operator_certification,group_fusion_plating_supervisor,1,1,1,0
|
access_fp_operator_cert_supervisor,fp.operator.cert.supervisor,model_fp_operator_certification,group_fusion_plating_supervisor,1,1,1,0
|
||||||
access_fp_operator_cert_manager,fp.operator.cert.manager,model_fp_operator_certification,group_fusion_plating_manager,1,1,1,1
|
access_fp_operator_cert_manager,fp.operator.cert.manager,model_fp_operator_certification,group_fusion_plating_manager,1,1,1,1
|
||||||
|
access_fp_work_centre_user,fp.work.centre.user,model_fp_work_centre,base.group_user,1,0,0,0
|
||||||
|
access_fp_work_centre_supervisor,fp.work.centre.supervisor,model_fp_work_centre,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
|
||||||
|
access_fp_work_centre_manager,fp.work.centre.manager,model_fp_work_centre,fusion_plating.group_fusion_plating_manager,1,1,1,1
|
||||||
|
|||||||
|
2
fusion_plating/fusion_plating/tests/__init__.py
Normal file
2
fusion_plating/fusion_plating/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import test_fp_work_centre
|
||||||
29
fusion_plating/fusion_plating/tests/test_fp_work_centre.py
Normal file
29
fusion_plating/fusion_plating/tests/test_fp_work_centre.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestFpWorkCentre(TransactionCase):
|
||||||
|
def test_create_work_centre_minimal(self):
|
||||||
|
wc = self.env['fp.work.centre'].create({
|
||||||
|
'name': 'Bath Line 1',
|
||||||
|
'code': 'BL1',
|
||||||
|
'kind': 'wet_line',
|
||||||
|
})
|
||||||
|
self.assertEqual(wc.name, 'Bath Line 1')
|
||||||
|
self.assertEqual(wc.kind, 'wet_line')
|
||||||
|
self.assertTrue(wc.active)
|
||||||
|
|
||||||
|
def test_facility_required_for_active_centre(self):
|
||||||
|
wc = self.env['fp.work.centre'].create({
|
||||||
|
'name': 'Test',
|
||||||
|
'code': 'T',
|
||||||
|
'kind': 'other',
|
||||||
|
})
|
||||||
|
self.assertFalse(wc.facility_id)
|
||||||
|
|
||||||
|
def test_kind_selection_values(self):
|
||||||
|
kinds = dict(
|
||||||
|
self.env['fp.work.centre']._fields['kind'].selection
|
||||||
|
)
|
||||||
|
for k in ('wet_line', 'bake', 'mask', 'rack', 'inspect', 'other'):
|
||||||
|
self.assertIn(k, kinds)
|
||||||
Reference in New Issue
Block a user