feat(fusion_plating_jobs): multi-part cert creation + requirement union
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -609,38 +609,47 @@ class FpJob(models.Model):
|
||||
matches the defensive pattern used elsewhere in this file.
|
||||
"""
|
||||
self.ensure_one()
|
||||
# ---- Step 1 — partner + part baseline ----
|
||||
req = (
|
||||
self.part_catalog_id
|
||||
and self.part_catalog_id.certificate_requirement
|
||||
) or 'inherit'
|
||||
if req == 'inherit':
|
||||
wanted = set()
|
||||
# ---- Step 1 — partner + part baseline (union across all parts) ----
|
||||
def _partner_inherit_set():
|
||||
s = set()
|
||||
p = self.partner_id
|
||||
if p:
|
||||
if p.x_fc_send_coc:
|
||||
wanted.add('coc')
|
||||
s.add('coc')
|
||||
if p.x_fc_send_thickness_report:
|
||||
wanted.add('thickness_report')
|
||||
# Three aerospace/defence partner toggles. Field guards
|
||||
# let this module load even if fusion_plating_certificates
|
||||
# is at an older version that pre-dates the new fields.
|
||||
if ('x_fc_send_nadcap_cert' in p._fields
|
||||
and p.x_fc_send_nadcap_cert):
|
||||
wanted.add('nadcap_cert')
|
||||
if ('x_fc_send_mill_test' in p._fields
|
||||
and p.x_fc_send_mill_test):
|
||||
wanted.add('mill_test')
|
||||
if ('x_fc_send_customer_specific' in p._fields
|
||||
and p.x_fc_send_customer_specific):
|
||||
wanted.add('customer_specific')
|
||||
else:
|
||||
wanted = {
|
||||
'none': set(),
|
||||
'coc': {'coc'},
|
||||
s.add('thickness_report')
|
||||
if 'x_fc_send_nadcap_cert' in p._fields and p.x_fc_send_nadcap_cert:
|
||||
s.add('nadcap_cert')
|
||||
if 'x_fc_send_mill_test' in p._fields and p.x_fc_send_mill_test:
|
||||
s.add('mill_test')
|
||||
if 'x_fc_send_customer_specific' in p._fields and p.x_fc_send_customer_specific:
|
||||
s.add('customer_specific')
|
||||
return s
|
||||
|
||||
def _explicit_set(req):
|
||||
return {
|
||||
'none': set(), 'coc': {'coc'},
|
||||
'coc_thickness': {'coc', 'thickness_report'},
|
||||
}.get(req, {'coc'})
|
||||
|
||||
parts = self._fp_cert_source_lines().mapped('x_fc_part_catalog_id')
|
||||
if not parts and self.part_catalog_id:
|
||||
parts = self.part_catalog_id
|
||||
if not parts:
|
||||
parts = [False]
|
||||
wanted = set()
|
||||
inherit = None
|
||||
for part in parts:
|
||||
req = (part.certificate_requirement
|
||||
if part and 'certificate_requirement' in part._fields
|
||||
else 'inherit') or 'inherit'
|
||||
if req == 'inherit':
|
||||
if inherit is None:
|
||||
inherit = _partner_inherit_set()
|
||||
wanted |= inherit
|
||||
else:
|
||||
wanted |= _explicit_set(req)
|
||||
|
||||
# ---- Step 2 — recipe suppression (suppress-only) ----
|
||||
recipe = self.recipe_id
|
||||
if recipe:
|
||||
@@ -2655,6 +2664,58 @@ class FpJob(models.Model):
|
||||
self.name, e,
|
||||
)
|
||||
|
||||
def _fp_cert_source_lines(self):
|
||||
"""Plating SO lines this job covers (one cert part-line each)."""
|
||||
self.ensure_one()
|
||||
lines = self.sale_order_line_ids
|
||||
if not lines and self.sale_order_id:
|
||||
lines = self.sale_order_id.order_line
|
||||
return lines.filtered(
|
||||
lambda l: not l.display_type
|
||||
and ('x_fc_part_catalog_id' in l._fields and l.x_fc_part_catalog_id))
|
||||
|
||||
def _fp_format_spec_ref(self, spec):
|
||||
"""Format 'CODE Rev X' from a customer spec (or '')."""
|
||||
if not spec:
|
||||
return ''
|
||||
ref = spec.code or ''
|
||||
if 'revision' in spec._fields and spec.revision:
|
||||
ref = (f'{ref} Rev {spec.revision}' if ref
|
||||
else f'Rev {spec.revision}')
|
||||
return ref
|
||||
|
||||
def _fp_build_cert_part_commands(self):
|
||||
"""O2M create commands for fp.certificate.part — one per line."""
|
||||
self.ensure_one()
|
||||
cmds, seq = [], 10
|
||||
for sol in self._fp_cert_source_lines():
|
||||
part = sol.x_fc_part_catalog_id
|
||||
spec = (sol.x_fc_customer_spec_id
|
||||
if 'x_fc_customer_spec_id' in sol._fields else False)
|
||||
serials = ''
|
||||
if 'x_fc_serial_ids' in sol._fields and sol.x_fc_serial_ids:
|
||||
serials = ', '.join(sol.x_fc_serial_ids.mapped('name'))
|
||||
# fp_customer_description() is a method (configurator), not a
|
||||
# field — use hasattr, not a _fields check.
|
||||
desc = (sol.fp_customer_description()
|
||||
if hasattr(sol, 'fp_customer_description')
|
||||
else (sol.name or ''))
|
||||
cmds.append((0, 0, {
|
||||
'sequence': seq,
|
||||
'sale_order_line_id': sol.id,
|
||||
'part_catalog_id': part.id if part else False,
|
||||
'part_number': (part.part_number if part else '') or '',
|
||||
'part_name': (part.name if part else '') or '',
|
||||
'description': desc,
|
||||
'serial': serials,
|
||||
'customer_spec_id': spec.id if spec else False,
|
||||
'spec_reference': self._fp_format_spec_ref(spec),
|
||||
'quantity_shipped': int(sol.product_uom_qty or 0),
|
||||
'nc_quantity': 0,
|
||||
}))
|
||||
seq += 10
|
||||
return cmds
|
||||
|
||||
def _fp_create_certificates(self):
|
||||
"""Auto-create one draft fp.certificate per type returned by
|
||||
_resolve_required_cert_types. Idempotent per type — re-running
|
||||
@@ -2742,10 +2803,7 @@ class FpJob(models.Model):
|
||||
# spec_reference is what action_issue blocks on.
|
||||
# Format spec.code + revision for the cert text.
|
||||
if spec and 'spec_reference' in Cert._fields:
|
||||
ref = spec.code or ''
|
||||
if spec.revision:
|
||||
ref = (f'{ref} Rev {spec.revision}'
|
||||
if ref else f'Rev {spec.revision}')
|
||||
ref = self._fp_format_spec_ref(spec)
|
||||
if ref:
|
||||
vals['spec_reference'] = ref
|
||||
if 'customer_spec_id' in Cert._fields:
|
||||
@@ -2781,6 +2839,10 @@ class FpJob(models.Model):
|
||||
vals['contact_partner_id'] = contact.id
|
||||
if 'entech_wo_number' in Cert._fields:
|
||||
vals['entech_wo_number'] = self.name or ''
|
||||
if 'part_line_ids' in Cert._fields:
|
||||
part_cmds = self._fp_build_cert_part_commands()
|
||||
if part_cmds:
|
||||
vals['part_line_ids'] = part_cmds
|
||||
cert = Cert.create(vals)
|
||||
self.message_post(body=Markup(_(
|
||||
'%(t)s <b>%(n)s</b> auto-created (draft). Issuer '
|
||||
|
||||
Reference in New Issue
Block a user