diff --git a/fusion_plating/fusion_plating_configurator/models/sale_order.py b/fusion_plating/fusion_plating_configurator/models/sale_order.py index 15b83dfe..d4428a1a 100644 --- a/fusion_plating/fusion_plating_configurator/models/sale_order.py +++ b/fusion_plating/fusion_plating_configurator/models/sale_order.py @@ -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): diff --git a/fusion_plating/fusion_plating_configurator/models/sale_order_line.py b/fusion_plating/fusion_plating_configurator/models/sale_order_line.py index 4c3e533a..408663c6 100644 --- a/fusion_plating/fusion_plating_configurator/models/sale_order_line.py +++ b/fusion_plating/fusion_plating_configurator/models/sale_order_line.py @@ -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.', + ) diff --git a/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml b/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml index 289c6229..4802b3e5 100644 --- a/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml +++ b/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml @@ -89,6 +89,8 @@ + + @@ -98,6 +100,7 @@ + diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py index 324cf7a8..d5e8017a 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py @@ -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): diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py index 9f923464..11b67c4a 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py @@ -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 diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml index 01c7f7ae..8602aeb4 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml @@ -45,6 +45,8 @@ + + @@ -69,7 +71,9 @@ - + + + @@ -115,6 +120,7 @@ options="{'currency_field': 'currency_id'}"/> +