feat(promote-customer-spec): Phase E — final removal of coating + treatment
DELETED entirely (model + view + ACL + data file + menu): - fp.coating.config (configurator) - fp.treatment (configurator + seeded data) - fp.coating.thickness (configurator) — replaced by fp.recipe.thickness in Phase A - fp.customer.price.list (configurator) — coating-keyed, no replacement Field deletions: - sale.order.x_fc_coating_config_id - sale.order.line.x_fc_coating_config_id + x_fc_treatment_ids - account.move.line.x_fc_coating_config_id - fp.part.catalog.x_fc_default_coating_config_id + x_fc_default_treatment_ids - fp.job.coating_config_id - fp.pricing.rule.coating_config_id - fp.quality.point.coating_config_ids - fp.direct.order.line.coating_config_id + treatment_ids - fp.sale.description.template.coating_config_id Refactored: - fp.quote.configurator.coating_config_id → recipe_id (now points at fusion.plating.process.node, the actual recipe). All compute, onchange, and matcher logic updated to use recipe directly. Quality inherit extends matcher with spec-tier scoring. - fp.job._fp_create_certificates now reads spec from job.customer_spec_id and formats spec_reference as "code Rev rev". Same for thickness source — bake fields read from recipe_root (Phase A). - fp.job.step.button_finish bake-window auto-spawn reads bake settings from recipe_root instead of coating. - fp.certificate auto-fill spec_min_mils/max_mils from recipe (Phase A thickness fields) instead of coating. - jobs/sale_order.py: job creation reads x_fc_customer_spec_id from line, drops coating refs and the legacy header-coating fallback. - Wizards drop coating + treatment fields and refs. - Configurator views drop x_fc_coating_config_id + x_fc_treatment_ids fields entirely. Quality inherits re-anchor on stable fields (x_fc_part_catalog_id, x_fc_internal_description, default_process_id, process_variant_id, substrate_material) so they keep working. - Reports drop coating fallback elifs; print recipe / spec. - Tablet payload drops coating_config_id from job.read fields. Skipped (deferred to backlog): - fusion_plating_bridge_mrp — module is uninstalled per Sub 11; source files retain coating refs but no runtime impact. - fusion_plating_portal — circular dep (portal → quality → certs → portal). Customer-facing portal coating picker stays for now; promote-spec polish is a separate sub-project. Verification: grep for "coating_config_id|fp.coating.config| fp.treatment|fp.coating.thickness" in live (non-bridge_mrp, non-portal, non-script, non-test) Python/XML/CSV returns 3 hits, all in module / class docstrings explaining Phase E history. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -339,11 +339,8 @@ class SaleOrder(models.Model):
|
||||
1. line.x_fc_process_variant_id — Sarah explicitly picked a
|
||||
part-scoped variant on this order line. Always wins.
|
||||
2. part.default_process_id — part's flagged default
|
||||
variant. Customer-and-part-tuned recipe; must beat any
|
||||
generic coating template.
|
||||
3. coating.recipe_id — coating-config recipe
|
||||
(generic template fallback).
|
||||
4. part.recipe_id — legacy fallback.
|
||||
variant. Customer-and-part-tuned recipe.
|
||||
3. part.recipe_id — legacy fallback.
|
||||
Returns the recipe record or an empty recordset.
|
||||
"""
|
||||
Node = self.env['fusion.plating.process.node']
|
||||
@@ -352,11 +349,6 @@ class SaleOrder(models.Model):
|
||||
) or False
|
||||
if not part and 'x_fc_part_catalog_id' in self._fields:
|
||||
part = self.x_fc_part_catalog_id or False
|
||||
coating = (
|
||||
'x_fc_coating_config_id' in line._fields and line.x_fc_coating_config_id
|
||||
) or False
|
||||
if not coating and 'x_fc_coating_config_id' in self._fields:
|
||||
coating = self.x_fc_coating_config_id or False
|
||||
picked = (
|
||||
'x_fc_process_variant_id' in line._fields
|
||||
and line.x_fc_process_variant_id
|
||||
@@ -365,8 +357,6 @@ class SaleOrder(models.Model):
|
||||
return picked
|
||||
if part and 'default_process_id' in part._fields and part.default_process_id:
|
||||
return part.default_process_id
|
||||
if coating and 'recipe_id' in coating._fields and coating.recipe_id:
|
||||
return coating.recipe_id
|
||||
if part and 'recipe_id' in part._fields and part.recipe_id:
|
||||
return part.recipe_id
|
||||
return Node
|
||||
@@ -389,22 +379,22 @@ class SaleOrder(models.Model):
|
||||
if existing:
|
||||
return
|
||||
|
||||
# Find plating lines (those with a part_catalog_id or coating_config_id)
|
||||
# Find plating lines (those with a part_catalog_id or
|
||||
# customer_spec_id).
|
||||
plating_lines = self.order_line.filtered(
|
||||
lambda l: (
|
||||
('x_fc_part_catalog_id' in l._fields and l.x_fc_part_catalog_id)
|
||||
or ('x_fc_coating_config_id' in l._fields and l.x_fc_coating_config_id)
|
||||
or ('x_fc_customer_spec_id' in l._fields and l.x_fc_customer_spec_id)
|
||||
)
|
||||
)
|
||||
# Fallback: legacy/configurator SOs that carry part+coating on the
|
||||
# header but not on the line. Treat the entire order as one
|
||||
# plating line so the planner gets an fp.job to work against.
|
||||
# Fallback: SOs that carry part on the header but not on the
|
||||
# line. Treat the entire order as one plating job so the planner
|
||||
# gets an fp.job to work against.
|
||||
if not plating_lines and self.order_line and (
|
||||
('x_fc_part_catalog_id' in self._fields and self.x_fc_part_catalog_id)
|
||||
or ('x_fc_coating_config_id' in self._fields and self.x_fc_coating_config_id)
|
||||
'x_fc_part_catalog_id' in self._fields and self.x_fc_part_catalog_id
|
||||
):
|
||||
_logger.info(
|
||||
'SO %s: no line-level part/coating but header carries one — '
|
||||
'SO %s: no line-level part but header carries one — '
|
||||
'treating all lines as a single plating job.', self.name,
|
||||
)
|
||||
plating_lines = self.order_line
|
||||
@@ -412,13 +402,12 @@ class SaleOrder(models.Model):
|
||||
_logger.info('SO %s: no plating lines, skipping job creation.', self.name)
|
||||
return
|
||||
|
||||
# Group by (recipe, part, coating, thickness, serial). Lines that
|
||||
# share ALL FIVE collapse into one WO. Same compliance reasoning
|
||||
# as part_id + coating_id: bundling lines with different thicknesses
|
||||
# or different serials under one WO would carry the first line's
|
||||
# values onto the cert + sticker — silent mis-attestation. Sub 5
|
||||
# added thickness_id + serial_id; this extends the grouping logic
|
||||
# to honour them. No-recipe lines still get their own group each.
|
||||
# Group by (recipe, part, spec, thickness, serial). Lines that
|
||||
# share ALL FIVE collapse into one WO. Bundling lines with
|
||||
# different specs / thicknesses / serials under one WO would
|
||||
# carry the first line's values onto the cert + sticker —
|
||||
# silent mis-attestation. No-recipe lines still get their own
|
||||
# group each.
|
||||
groups = {}
|
||||
unrecipe_idx = 0
|
||||
for line in plating_lines:
|
||||
@@ -427,9 +416,9 @@ class SaleOrder(models.Model):
|
||||
'x_fc_part_catalog_id' in line._fields
|
||||
and line.x_fc_part_catalog_id.id
|
||||
) or False
|
||||
coating_id = (
|
||||
'x_fc_coating_config_id' in line._fields
|
||||
and line.x_fc_coating_config_id.id
|
||||
spec_id = (
|
||||
'x_fc_customer_spec_id' in line._fields
|
||||
and line.x_fc_customer_spec_id.id
|
||||
) or False
|
||||
thickness_id = (
|
||||
'x_fc_thickness_id' in line._fields
|
||||
@@ -440,7 +429,7 @@ class SaleOrder(models.Model):
|
||||
and line.x_fc_serial_id.id
|
||||
) or False
|
||||
if recipe:
|
||||
key = (recipe.id, part_id, coating_id, thickness_id, serial_id)
|
||||
key = (recipe.id, part_id, spec_id, thickness_id, serial_id)
|
||||
else:
|
||||
unrecipe_idx += 1
|
||||
key = ('no_recipe', unrecipe_idx)
|
||||
@@ -465,11 +454,6 @@ class SaleOrder(models.Model):
|
||||
and first_line.x_fc_part_catalog_id
|
||||
or False
|
||||
)
|
||||
coating = (
|
||||
'x_fc_coating_config_id' in first_line._fields
|
||||
and first_line.x_fc_coating_config_id
|
||||
or False
|
||||
)
|
||||
customer_spec = (
|
||||
'x_fc_customer_spec_id' in first_line._fields
|
||||
and first_line.x_fc_customer_spec_id
|
||||
@@ -477,8 +461,6 @@ class SaleOrder(models.Model):
|
||||
)
|
||||
if not part and 'x_fc_part_catalog_id' in self._fields:
|
||||
part = self.x_fc_part_catalog_id or False
|
||||
if not coating and 'x_fc_coating_config_id' in self._fields:
|
||||
coating = self.x_fc_coating_config_id or False
|
||||
recipe = self._fp_resolve_recipe_for_line(first_line)
|
||||
|
||||
vals = {
|
||||
@@ -492,8 +474,6 @@ class SaleOrder(models.Model):
|
||||
}
|
||||
if part:
|
||||
vals['part_catalog_id'] = part.id
|
||||
if coating:
|
||||
vals['coating_config_id'] = coating.id
|
||||
if customer_spec:
|
||||
vals['customer_spec_id'] = customer_spec.id
|
||||
if recipe:
|
||||
|
||||
Reference in New Issue
Block a user