feat(numbering): add parent_number + counters to sale.order
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.8.21.5',
|
'version': '19.0.8.22.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.',
|
||||||
'author': 'Nexa Systems Inc.',
|
'author': 'Nexa Systems Inc.',
|
||||||
|
|||||||
@@ -32,6 +32,40 @@ class SaleOrder(models.Model):
|
|||||||
'to drill through the linked Plating Job first.',
|
'to drill through the linked Plating Job first.',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Parent-number hierarchy (2026-05-12 design)
|
||||||
|
# See docs/superpowers/specs/2026-05-12-parent-number-hierarchy-design.md
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
x_fc_parent_number = fields.Integer(
|
||||||
|
string='Parent Number',
|
||||||
|
readonly=True,
|
||||||
|
copy=False,
|
||||||
|
index=True,
|
||||||
|
help='Set on confirm. Drives every linked document\'s name '
|
||||||
|
'(WO-NNN, IN-NNN, CoC-NNN, ...). Immutable post-assignment.',
|
||||||
|
)
|
||||||
|
x_fc_quote_ref = fields.Char(
|
||||||
|
string='Originally Quoted As',
|
||||||
|
readonly=True,
|
||||||
|
copy=False,
|
||||||
|
help='The quote-stage name (e.g. Q202605-200). Preserved when '
|
||||||
|
'the SO is renamed on confirm.',
|
||||||
|
)
|
||||||
|
# Per-model counters — monotonic, never decrement. Source of truth
|
||||||
|
# for the next sibling's x_fc_doc_index. Updated via row-locked SQL
|
||||||
|
# in fp.parent.numbered.mixin so concurrent creates can't drift.
|
||||||
|
x_fc_wo_count = fields.Integer(string='WO Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_invoice_count = fields.Integer(string='Invoice Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_cn_count = fields.Integer(string='Credit Note Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_cert_count = fields.Integer(string='Certificate Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_delivery_count = fields.Integer(string='Delivery Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_receiving_count = fields.Integer(string='Receiving Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_pickup_count = fields.Integer(string='Pickup Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_ncr_count = fields.Integer(string='NCR Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_capa_count = fields.Integer(string='CAPA Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_hold_count = fields.Integer(string='Hold Count', readonly=True, copy=False, default=0)
|
||||||
|
x_fc_rma_count = fields.Integer(string='RMA Count', readonly=True, copy=False, default=0)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Phase 4 (Sub 11) — workflow-stage field + assigned-manager field
|
# Phase 4 (Sub 11) — workflow-stage field + assigned-manager field
|
||||||
# relocated from fusion_plating_bridge_mrp. Field re-declared with
|
# relocated from fusion_plating_bridge_mrp. Field re-declared with
|
||||||
@@ -278,13 +312,23 @@ class SaleOrder(models.Model):
|
|||||||
part = self.x_fc_part_catalog_id or False
|
part = self.x_fc_part_catalog_id or False
|
||||||
if not coating and 'x_fc_coating_config_id' in self._fields:
|
if not coating and 'x_fc_coating_config_id' in self._fields:
|
||||||
coating = self.x_fc_coating_config_id or False
|
coating = self.x_fc_coating_config_id or False
|
||||||
# Recipe lookup priority:
|
# Recipe lookup priority (specific → generic):
|
||||||
# 1. line.x_fc_process_variant_id — Sarah explicitly picked
|
# 1. line.x_fc_process_variant_id — Sarah explicitly picked
|
||||||
# a part-scoped variant on this order line. Always wins.
|
# a part-scoped variant on this order line. Always wins.
|
||||||
# 2. coating.recipe_id — coating-config recipe.
|
# 2. part.default_process_id — part's flagged default
|
||||||
# 3. part.default_process_id — part's flagged default.
|
# variant. This is the customer-and-part-tuned recipe
|
||||||
|
# (months of process refinement) and must beat any
|
||||||
|
# generic template attached to the coating config.
|
||||||
|
# 3. coating.recipe_id — coating-config recipe
|
||||||
|
# (generic template fallback when no part variant exists).
|
||||||
# 4. part.recipe_id — legacy fallback.
|
# 4. part.recipe_id — legacy fallback.
|
||||||
#
|
#
|
||||||
|
# Order swap 2026-05-12: before this, step (3) ran before (2),
|
||||||
|
# so jobs with both a part variant AND a coating-attached
|
||||||
|
# template silently picked the template. WO #01373 (S00063)
|
||||||
|
# is the documented case — part 9876699373 Rev A had its own
|
||||||
|
# variant but the job linked to ENP-ALUM-BASIC instead.
|
||||||
|
#
|
||||||
# If multiple lines in the same WO group have different
|
# If multiple lines in the same WO group have different
|
||||||
# variants we use the FIRST line's variant (consistent with
|
# variants we use the FIRST line's variant (consistent with
|
||||||
# everything else in this loop using `first_line`).
|
# everything else in this loop using `first_line`).
|
||||||
@@ -296,12 +340,12 @@ class SaleOrder(models.Model):
|
|||||||
)
|
)
|
||||||
if picked_variant:
|
if picked_variant:
|
||||||
recipe = picked_variant
|
recipe = picked_variant
|
||||||
if not recipe and coating and 'recipe_id' in coating._fields \
|
|
||||||
and coating.recipe_id:
|
|
||||||
recipe = coating.recipe_id
|
|
||||||
if not recipe and part and 'default_process_id' in part._fields \
|
if not recipe and part and 'default_process_id' in part._fields \
|
||||||
and part.default_process_id:
|
and part.default_process_id:
|
||||||
recipe = part.default_process_id
|
recipe = part.default_process_id
|
||||||
|
if not recipe and coating and 'recipe_id' in coating._fields \
|
||||||
|
and coating.recipe_id:
|
||||||
|
recipe = coating.recipe_id
|
||||||
if not recipe and part and 'recipe_id' in part._fields \
|
if not recipe and part and 'recipe_id' in part._fields \
|
||||||
and part.recipe_id:
|
and part.recipe_id:
|
||||||
recipe = part.recipe_id
|
recipe = part.recipe_id
|
||||||
|
|||||||
Reference in New Issue
Block a user