feat(jobs): persist deadlines + planned start + notes on fp.job
Tier 2 of the SO->fp.job persistence audit. Four operational metadata fields mirrored from sale.order: - x_fc_internal_deadline (Date) - shop's internal target finish date, ahead of the customer-facing deadline. Kept separate from date_deadline (which scheduling code may adjust). - x_fc_planned_start_date (Date) - customer-quoted planned start date. Kept separate from date_planned_start (Datetime, capacity-adjusted). - x_fc_internal_note (Text) - shop-internal notes from the order. - x_fc_external_note (Text) - customer-facing notes, printed on traveller / BoL / cert. All four populate at SO confirm via _fp_auto_create_job, and surface on sale.order.line as stored related fields for per-line visibility. fp.job form view gets a Notes group alongside the Customer References group from Tier 1. 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.18.15.6',
|
'version': '19.0.18.15.7',
|
||||||
'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': """
|
||||||
|
|||||||
@@ -289,6 +289,37 @@ class FpJob(models.Model):
|
|||||||
'job form to know it\'s rush.',
|
'job form to know it\'s rush.',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---- Scheduling targets mirrored from sale.order -----------------
|
||||||
|
# These are kept separate from the operational date_planned_start /
|
||||||
|
# date_deadline fields (which may be tweaked by scheduling logic) —
|
||||||
|
# this preserves the ORIGINAL customer-facing dates entered on the SO.
|
||||||
|
x_fc_internal_deadline = fields.Date(
|
||||||
|
string='Internal Deadline',
|
||||||
|
tracking=True,
|
||||||
|
help='Shop\'s internal target finish date, copied from sale.order. '
|
||||||
|
'Buffer ahead of the customer-facing deadline.',
|
||||||
|
)
|
||||||
|
x_fc_planned_start_date = fields.Date(
|
||||||
|
string='Planned Start (SO)',
|
||||||
|
tracking=True,
|
||||||
|
help='Customer-quoted planned start date copied from sale.order. '
|
||||||
|
'Different from date_planned_start (which scheduling logic '
|
||||||
|
'may adjust based on shop capacity).',
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- Operational notes mirrored from sale.order ------------------
|
||||||
|
x_fc_internal_note = fields.Text(
|
||||||
|
string='Internal Note',
|
||||||
|
tracking=True,
|
||||||
|
help='Shop-internal notes from the order. Not shown to customer.',
|
||||||
|
)
|
||||||
|
x_fc_external_note = fields.Text(
|
||||||
|
string='External Note',
|
||||||
|
tracking=True,
|
||||||
|
help='Customer-facing notes copied from the sale order. Printed '
|
||||||
|
'on traveller / BoL / cert.',
|
||||||
|
)
|
||||||
|
|
||||||
qty_received = fields.Integer(
|
qty_received = fields.Integer(
|
||||||
string='Qty Received',
|
string='Qty Received',
|
||||||
help='Paper traveller "Qty Rec." column.',
|
help='Paper traveller "Qty Rec." column.',
|
||||||
|
|||||||
@@ -41,6 +41,12 @@
|
|||||||
<field name="x_fc_customer_job_number"/>
|
<field name="x_fc_customer_job_number"/>
|
||||||
<field name="x_fc_po_number"/>
|
<field name="x_fc_po_number"/>
|
||||||
<field name="x_fc_rush_order"/>
|
<field name="x_fc_rush_order"/>
|
||||||
|
<field name="x_fc_planned_start_date"/>
|
||||||
|
<field name="x_fc_internal_deadline"/>
|
||||||
|
</group>
|
||||||
|
<group name="x_fc_notes" string="Notes">
|
||||||
|
<field name="x_fc_internal_note" nolabel="1" placeholder="Internal note (not shown to customer)…"/>
|
||||||
|
<field name="x_fc_external_note" nolabel="1" placeholder="External note (printed on customer paperwork)…"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating — Configurator',
|
'name': 'Fusion Plating — Configurator',
|
||||||
'version': '19.0.18.10.0',
|
'version': '19.0.18.10.1',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
|
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -659,3 +659,28 @@ class SaleOrderLine(models.Model):
|
|||||||
readonly=True,
|
readonly=True,
|
||||||
store=True,
|
store=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
x_fc_internal_deadline = fields.Date(
|
||||||
|
related='order_id.x_fc_internal_deadline',
|
||||||
|
string='Internal Deadline',
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
x_fc_planned_start_date = fields.Date(
|
||||||
|
related='order_id.x_fc_planned_start_date',
|
||||||
|
string='Planned Start',
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
x_fc_internal_note = fields.Text(
|
||||||
|
related='order_id.x_fc_internal_note',
|
||||||
|
string='Internal Note',
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
x_fc_external_note = fields.Text(
|
||||||
|
related='order_id.x_fc_external_note',
|
||||||
|
string='External Note',
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
|||||||
@@ -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.2',
|
'version': '19.0.8.21.3',
|
||||||
'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.',
|
||||||
|
|||||||
@@ -332,6 +332,22 @@ class SaleOrder(models.Model):
|
|||||||
if 'x_fc_rush_order' in self._fields:
|
if 'x_fc_rush_order' in self._fields:
|
||||||
vals['x_fc_rush_order'] = bool(self.x_fc_rush_order)
|
vals['x_fc_rush_order'] = bool(self.x_fc_rush_order)
|
||||||
|
|
||||||
|
# Scheduling targets — mirror the SO's customer-facing dates.
|
||||||
|
if 'x_fc_internal_deadline' in self._fields \
|
||||||
|
and self.x_fc_internal_deadline:
|
||||||
|
vals['x_fc_internal_deadline'] = self.x_fc_internal_deadline
|
||||||
|
if 'x_fc_planned_start_date' in self._fields \
|
||||||
|
and self.x_fc_planned_start_date:
|
||||||
|
vals['x_fc_planned_start_date'] = self.x_fc_planned_start_date
|
||||||
|
|
||||||
|
# Operational notes — mirror so the shop has them on the WO.
|
||||||
|
if 'x_fc_internal_note' in self._fields \
|
||||||
|
and self.x_fc_internal_note:
|
||||||
|
vals['x_fc_internal_note'] = self.x_fc_internal_note
|
||||||
|
if 'x_fc_external_note' in self._fields \
|
||||||
|
and self.x_fc_external_note:
|
||||||
|
vals['x_fc_external_note'] = self.x_fc_external_note
|
||||||
|
|
||||||
# Customer spec / facility / manager — copy from SO if present
|
# Customer spec / facility / manager — copy from SO if present
|
||||||
if 'x_fc_customer_spec_id' in self._fields and self.x_fc_customer_spec_id:
|
if 'x_fc_customer_spec_id' in self._fields and self.x_fc_customer_spec_id:
|
||||||
vals['customer_spec_id'] = self.x_fc_customer_spec_id.id
|
vals['customer_spec_id'] = self.x_fc_customer_spec_id.id
|
||||||
|
|||||||
Reference in New Issue
Block a user