feat(configurator): blanket/block-partial flags + WO group + per-line missing indicator
Phase B partial landing (B1, B2, B3, B5): - B1/B2: x_fc_is_blanket_order and x_fc_block_partial_shipments on sale.order; matching booleans on the wizard header. - B3: x_fc_wo_group_tag Char on sale.order.line and wo_group_tag on wizard line. Free-text tag; bridge_mrp will batch lines sharing a tag into one MO in a follow-up. - B5: is_missing_info computed Boolean on fp.direct.order.line; tree uses decoration-warning to highlight incomplete rows in amber. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,18 @@ class SaleOrder(models.Model):
|
||||
x_fc_internal_deadline = fields.Date(
|
||||
string='Internal Deadline', tracking=True,
|
||||
)
|
||||
x_fc_is_blanket_order = fields.Boolean(
|
||||
string='Is Blanket Sales Order',
|
||||
help='Blanket orders release parts in quantities over time, '
|
||||
'often with a negotiated price and a fixed expiry.',
|
||||
tracking=True,
|
||||
)
|
||||
x_fc_block_partial_shipments = fields.Boolean(
|
||||
string='Block Partial Shipments',
|
||||
help='If set, the order must ship all-or-nothing. '
|
||||
'Partial pickings are blocked.',
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
@api.onchange('upload_rfq_file')
|
||||
def _onchange_upload_rfq_file(self):
|
||||
|
||||
@@ -20,3 +20,8 @@ class SaleOrderLine(models.Model):
|
||||
)
|
||||
x_fc_part_deadline = fields.Date(string='Part Deadline')
|
||||
x_fc_rush_order = fields.Boolean(string='Rush')
|
||||
x_fc_wo_group_tag = fields.Char(
|
||||
string='Work Order Group',
|
||||
help='Lines sharing a tag (e.g. "WO#1") will be batched into one '
|
||||
'manufacturing order when bridge_mrp generates MOs.',
|
||||
)
|
||||
|
||||
@@ -89,6 +89,8 @@
|
||||
<field name="x_fc_planned_start_date"/>
|
||||
<field name="x_fc_internal_deadline"/>
|
||||
<field name="commitment_date" string="Customer Deadline"/>
|
||||
<field name="x_fc_is_blanket_order"/>
|
||||
<field name="x_fc_block_partial_shipments"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
@@ -98,6 +100,7 @@
|
||||
<field name="x_fc_coating_config_id" optional="show"/>
|
||||
<field name="x_fc_treatment_ids" widget="many2many_tags" optional="hide"/>
|
||||
<field name="x_fc_part_deadline" optional="hide"/>
|
||||
<field name="x_fc_wo_group_tag" optional="hide"/>
|
||||
<field name="x_fc_rush_order" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -79,6 +79,11 @@ class FpDirectOrderLine(models.TransientModel):
|
||||
help='Per-line deadline. Defaults to SO customer deadline if blank.',
|
||||
)
|
||||
rush_order = fields.Boolean(string='Rush')
|
||||
wo_group_tag = fields.Char(
|
||||
string='WO Group',
|
||||
help='Free-text tag. Lines sharing a tag (e.g. "WO#1", "WO#2") '
|
||||
'will be batched into one manufacturing order.',
|
||||
)
|
||||
|
||||
# ---- Description ----
|
||||
description_template_id = fields.Many2one(
|
||||
@@ -91,12 +96,28 @@ class FpDirectOrderLine(models.TransientModel):
|
||||
'Edit freely — your changes override the template.',
|
||||
)
|
||||
|
||||
# ---- Missing info per line ----
|
||||
is_missing_info = fields.Boolean(
|
||||
string='Missing Info',
|
||||
compute='_compute_is_missing_info',
|
||||
)
|
||||
|
||||
# ---- Computes ----
|
||||
@api.depends('quantity', 'unit_price')
|
||||
def _compute_line_subtotal(self):
|
||||
for rec in self:
|
||||
rec.line_subtotal = (rec.quantity or 0) * (rec.unit_price or 0.0)
|
||||
|
||||
@api.depends('part_catalog_id', 'coating_config_id', 'unit_price', 'quantity')
|
||||
def _compute_is_missing_info(self):
|
||||
for rec in self:
|
||||
rec.is_missing_info = not (
|
||||
rec.part_catalog_id
|
||||
and rec.coating_config_id
|
||||
and rec.unit_price
|
||||
and rec.quantity
|
||||
)
|
||||
|
||||
# ---- Onchange ----
|
||||
@api.onchange('coating_config_id', 'quantity', 'part_catalog_id')
|
||||
def _onchange_lookup_price(self):
|
||||
|
||||
@@ -45,6 +45,16 @@ class FpDirectOrderWizard(models.TransientModel):
|
||||
internal_deadline = fields.Date(string='Internal Deadline')
|
||||
customer_deadline = fields.Date(string='Customer Deadline')
|
||||
|
||||
# ---- Order flags (Phase B) ----
|
||||
is_blanket_order = fields.Boolean(
|
||||
string='Blanket Sales Order',
|
||||
help='Blanket orders release parts in quantities over time.',
|
||||
)
|
||||
block_partial_shipments = fields.Boolean(
|
||||
string='Block Partial Shipments',
|
||||
help='Ship all-or-nothing; partial pickings are blocked.',
|
||||
)
|
||||
|
||||
# ---- PO (required — that's what makes this a "direct" order) ----
|
||||
po_number = fields.Char(string='Customer PO #', required=True)
|
||||
po_attachment_file = fields.Binary(string='PO Document', required=True)
|
||||
@@ -184,6 +194,8 @@ class FpDirectOrderWizard(models.TransientModel):
|
||||
'x_fc_deposit_percent': self.deposit_percent,
|
||||
'x_fc_progress_initial_percent': self.progress_initial_percent,
|
||||
'x_fc_delivery_method': self.delivery_method,
|
||||
'x_fc_is_blanket_order': self.is_blanket_order,
|
||||
'x_fc_block_partial_shipments': self.block_partial_shipments,
|
||||
'origin': 'Direct Order',
|
||||
'note': self.notes or False,
|
||||
'order_line': [],
|
||||
@@ -213,6 +225,7 @@ class FpDirectOrderWizard(models.TransientModel):
|
||||
'x_fc_treatment_ids': [(6, 0, line.treatment_ids.ids)],
|
||||
'x_fc_part_deadline': line.part_deadline,
|
||||
'x_fc_rush_order': line.rush_order,
|
||||
'x_fc_wo_group_tag': line.wo_group_tag or False,
|
||||
}))
|
||||
|
||||
# 5. Create + confirm
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
<field name="planned_start_date"/>
|
||||
<field name="internal_deadline"/>
|
||||
<field name="customer_deadline"/>
|
||||
<field name="is_blanket_order"/>
|
||||
<field name="block_partial_shipments"/>
|
||||
</group>
|
||||
<group string="Fulfilment & Invoicing">
|
||||
<field name="delivery_method"/>
|
||||
@@ -69,7 +71,9 @@
|
||||
<notebook>
|
||||
<page string="Lines" name="lines">
|
||||
<field name="line_ids">
|
||||
<list editable="bottom">
|
||||
<list editable="bottom"
|
||||
decoration-warning="is_missing_info">
|
||||
<field name="is_missing_info" column_invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="part_catalog_id"
|
||||
context="{'default_partner_id': parent.partner_id}"
|
||||
@@ -88,6 +92,7 @@
|
||||
options="{'currency_field': 'currency_id'}"
|
||||
sum="Total"/>
|
||||
<field name="part_deadline"/>
|
||||
<field name="wo_group_tag" optional="show"/>
|
||||
<field name="rush_order" optional="hide"/>
|
||||
<field name="currency_id" column_invisible="1"/>
|
||||
</list>
|
||||
@@ -115,6 +120,7 @@
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="part_deadline"/>
|
||||
<field name="rush_order"/>
|
||||
<field name="wo_group_tag"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user