fix(jobs): split fp.jobs by thickness + serial on SO confirm
The _fp_auto_create_job grouping key was (recipe, part, coating).
Lines that shared all three but differed in thickness (or serial)
silently collapsed into one fp.job — the second line's thickness/SN
was lost, and any downstream cert printed the first line's values
across both batches. Silent mis-attestation = compliance hole.
Extended the key tuple to (recipe, part, coating, thickness, serial).
Single-line SOs and same-(thickness, SN) multi-line SOs collapse
identically to before. Only lines that previously merged when they
shouldn't have now split into their own fp.jobs.
TDD via test_so_confirm_splits_by_thickness:
- seeds the part with default_process_id so both lines hit the
`if recipe:` branch (where the bug lived — the no_recipe branch
already split correctly per line)
- confirms 2 jobs after action_confirm with each carrying its
own thickness via the linked SO line
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -412,15 +412,13 @@ class SaleOrder(models.Model):
|
||||
_logger.info('SO %s: no plating lines, skipping job creation.', self.name)
|
||||
return
|
||||
|
||||
# Group by (recipe, part, coating). Lines that share ALL THREE
|
||||
# collapse into one WO. Sharing only the recipe is not enough —
|
||||
# the WO header captures part_id and coating_config_id from
|
||||
# first_line, and downstream the CoC prints the WO header's
|
||||
# part_number on the customer-facing cert. Bundling Part A +
|
||||
# Part B under one WO because they happen to share a recipe
|
||||
# would put Part A's number on a cert covering both, which is
|
||||
# a compliance bug (silent mis-attestation).
|
||||
# No-recipe lines get their own group each.
|
||||
# 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.
|
||||
groups = {}
|
||||
unrecipe_idx = 0
|
||||
for line in plating_lines:
|
||||
@@ -433,8 +431,16 @@ class SaleOrder(models.Model):
|
||||
'x_fc_coating_config_id' in line._fields
|
||||
and line.x_fc_coating_config_id.id
|
||||
) or False
|
||||
thickness_id = (
|
||||
'x_fc_thickness_id' in line._fields
|
||||
and line.x_fc_thickness_id.id
|
||||
) or False
|
||||
serial_id = (
|
||||
'x_fc_serial_id' in line._fields
|
||||
and line.x_fc_serial_id.id
|
||||
) or False
|
||||
if recipe:
|
||||
key = (recipe.id, part_id, coating_id)
|
||||
key = (recipe.id, part_id, coating_id, thickness_id, serial_id)
|
||||
else:
|
||||
unrecipe_idx += 1
|
||||
key = ('no_recipe', unrecipe_idx)
|
||||
|
||||
Reference in New Issue
Block a user