diff --git a/fusion-plating/fusion_plating_receiving/models/mrp_production.py b/fusion-plating/fusion_plating_receiving/models/mrp_production.py
index 0c8fe912..e2fc38f1 100644
--- a/fusion-plating/fusion_plating_receiving/models/mrp_production.py
+++ b/fusion-plating/fusion_plating_receiving/models/mrp_production.py
@@ -2,4 +2,54 @@
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
-# Placeholder — implemented in a later task.
+
+import logging
+
+from odoo import models, _
+
+_logger = logging.getLogger(__name__)
+
+
+class MrpProduction(models.Model):
+ _inherit = 'mrp.production'
+
+ def action_confirm(self):
+ """Soft gate: warn if parts haven't been received yet.
+
+ Checks the linked sale order's receiving status. If parts
+ are not yet received, logs a warning. This is informational
+ only -- it does not block confirmation. The gate is soft
+ because handshake deals and urgent jobs need flexibility.
+ """
+ for production in self:
+ so = production._get_source_sale_order()
+ if so and so.x_fc_receiving_status in ('not_received', False):
+ _logger.warning(
+ 'MO %s: parts not yet received for SO %s (receiving status: %s). '
+ 'Proceeding with confirmation.',
+ production.name, so.name, so.x_fc_receiving_status,
+ )
+ production.message_post(
+ body=_(
+ 'Warning: Parts not yet received for sale order '
+ '%s. '
+ 'Manufacturing confirmed without receiving verification.'
+ ) % (so.id, so.name),
+ )
+ return super().action_confirm()
+
+ def _get_source_sale_order(self):
+ """Find the sale order linked to this MO via origin field."""
+ self.ensure_one()
+ if not self.origin:
+ return False
+ # origin may contain SO name like "S00001" or configurator ref "CFG-00001"
+ so = self.env['sale.order'].search(
+ [('name', '=', self.origin)], limit=1,
+ )
+ if not so:
+ # Try matching by origin containing the SO name
+ so = self.env['sale.order'].search(
+ [('name', 'ilike', self.origin)], limit=1,
+ )
+ return so or False
diff --git a/fusion-plating/fusion_plating_receiving/models/sale_order.py b/fusion-plating/fusion_plating_receiving/models/sale_order.py
index 0c8fe912..e9267a1b 100644
--- a/fusion-plating/fusion_plating_receiving/models/sale_order.py
+++ b/fusion-plating/fusion_plating_receiving/models/sale_order.py
@@ -2,4 +2,62 @@
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
-# Placeholder — implemented in a later task.
+
+from odoo import api, fields, models
+
+
+class SaleOrder(models.Model):
+ _inherit = 'sale.order'
+
+ x_fc_receiving_ids = fields.One2many(
+ 'fp.receiving', 'sale_order_id', string='Receiving Records',
+ )
+ x_fc_receiving_count = fields.Integer(
+ string='Receiving Count', compute='_compute_receiving_count',
+ )
+
+ @api.depends('x_fc_receiving_ids')
+ def _compute_receiving_count(self):
+ for rec in self:
+ rec.x_fc_receiving_count = len(rec.x_fc_receiving_ids)
+
+ def action_confirm(self):
+ """Override to auto-create receiving record on SO confirmation."""
+ res = super().action_confirm()
+ for order in self:
+ # Only create if no receiving record exists yet
+ if not order.x_fc_receiving_ids:
+ total_qty = sum(order.order_line.mapped('product_uom_qty'))
+ receiving_vals = {
+ 'sale_order_id': order.id,
+ 'expected_qty': int(total_qty),
+ 'line_ids': [],
+ }
+ # Auto-create lines from SO lines
+ for line in order.order_line:
+ receiving_vals['line_ids'].append((0, 0, {
+ 'part_number': order.x_fc_part_catalog_id.part_number if order.x_fc_part_catalog_id else '',
+ 'description': line.name or '',
+ 'expected_qty': int(line.product_uom_qty),
+ }))
+ self.env['fp.receiving'].create(receiving_vals)
+ return res
+
+ def action_view_receiving(self):
+ """Smart button action to view receiving records."""
+ self.ensure_one()
+ if self.x_fc_receiving_count == 1:
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'fp.receiving',
+ 'res_id': self.x_fc_receiving_ids[0].id,
+ 'view_mode': 'form',
+ 'target': 'current',
+ }
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'fp.receiving',
+ 'view_mode': 'list,form',
+ 'domain': [('sale_order_id', '=', self.id)],
+ 'target': 'current',
+ }
diff --git a/fusion-plating/fusion_plating_receiving/views/fp_receiving_menu.xml b/fusion-plating/fusion_plating_receiving/views/fp_receiving_menu.xml
index 85d8c23e..10f0ee28 100644
--- a/fusion-plating/fusion_plating_receiving/views/fp_receiving_menu.xml
+++ b/fusion-plating/fusion_plating_receiving/views/fp_receiving_menu.xml
@@ -1,2 +1,49 @@
-
+
+
+
+
+
+ Pending Inspection
+ fp.receiving
+ list,form
+ [('state', '=', 'draft')]
+
+
+
+ Discrepancies
+ fp.receiving
+ list,form
+ [('state', '=', 'discrepancy')]
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fusion-plating/fusion_plating_receiving/views/sale_order_views.xml b/fusion-plating/fusion_plating_receiving/views/sale_order_views.xml
index 85d8c23e..d4a77f95 100644
--- a/fusion-plating/fusion_plating_receiving/views/sale_order_views.xml
+++ b/fusion-plating/fusion_plating_receiving/views/sale_order_views.xml
@@ -1,2 +1,25 @@
-
+
+
+
+
+
+ sale.order.form.fp.receiving
+ sale.order
+
+
+
+
+
+
+
+
+