This commit is contained in:
gsinghpal
2026-04-27 00:11:18 -04:00
parent d9f58b9851
commit f08f328688
116 changed files with 9891 additions and 359 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Receiving & Inspection',
'version': '19.0.3.3.0',
'version': '19.0.3.5.0',
'category': 'Manufacturing/Plating',
'summary': 'Parts receiving, inspection, damage logging, and manufacturing gate.',
'description': """

View File

@@ -7,6 +7,5 @@ from . import fp_receiving_damage
from . import fp_receiving_line
from . import fp_receiving
from . import fp_racking_inspection
from . import fp_receiving_racking_link
from . import sale_order
# Phase 6 (Sub 11) — mrp_production hook retired.
# from . import mrp_production

View File

@@ -125,6 +125,7 @@ class FpReceiving(models.Model):
rec.state = 'counted'
rec.received_by_id = self.env.user
rec.received_date = fields.Datetime.now()
rec._update_so_receiving_status()
rec.message_post(body=_(
'%(user)s counted %(n)d box(es) at receiving.'
) % {'user': self.env.user.name, 'n': rec.box_count_in})
@@ -219,12 +220,28 @@ class FpReceiving(models.Model):
rec.message_post(body=_('Discrepancy resolved.'))
def _update_so_receiving_status(self):
"""Update the linked sale order's receiving status."""
"""Update the linked sale order's receiving status.
Sub 8 maps the new box-count-only states (`counted`, `staged`,
`closed`) onto the SO's `x_fc_receiving_status`:
- draft -> not_received (no rows or just-created)
- counted / staged -> partial (boxes on dock, parts not yet
racked / inspected)
- closed -> received (all boxes opened, racking done)
Legacy states (inspecting / accepted / discrepancy / resolved) keep
their original mapping for back-compat with pre-Sub-8 records.
"""
for rec in self:
if rec.sale_order_id:
if rec.state in ('accepted', 'resolved'):
rec.sale_order_id.x_fc_receiving_status = 'received'
elif rec.state == 'discrepancy':
rec.sale_order_id.x_fc_receiving_status = 'partial'
elif rec.state == 'inspecting':
rec.sale_order_id.x_fc_receiving_status = 'partial'
if not rec.sale_order_id:
continue
if rec.state == 'closed':
rec.sale_order_id.x_fc_receiving_status = 'received'
elif rec.state in ('counted', 'staged'):
rec.sale_order_id.x_fc_receiving_status = 'partial'
# Legacy states preserved.
elif rec.state in ('accepted', 'resolved'):
rec.sale_order_id.x_fc_receiving_status = 'received'
elif rec.state in ('discrepancy', 'inspecting'):
rec.sale_order_id.x_fc_receiving_status = 'partial'
elif rec.state == 'draft':
rec.sale_order_id.x_fc_receiving_status = 'not_received'

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
#
# Sub 12 audit fix — discoverable handoff from fp.receiving (boxes
# counted) to fp.racking.inspection (parts inspected by the racking
# crew). The racking inspection is auto-created on fp.job.action_confirm
# but until now there was no smart-button on the receiving form to find
# it — racking crew had to navigate via a separate menu.
from odoo import _, fields, models
class FpReceivingRackingLink(models.Model):
_inherit = 'fp.receiving'
racking_inspection_count = fields.Integer(
string='Racking Inspections', compute='_compute_racking_inspection_count',
)
def _compute_racking_inspection_count(self):
Inspection = self.env['fp.racking.inspection'] \
if 'fp.racking.inspection' in self.env else None
for rec in self:
if Inspection is None or not rec.sale_order_id:
rec.racking_inspection_count = 0
continue
rec.racking_inspection_count = Inspection.search_count([
('sale_order_id', '=', rec.sale_order_id.id),
])
def action_view_racking_inspections(self):
"""Open the racking inspection(s) for this receiving's SO. If
none exists yet, default-create context lets the user spawn one
with the SO context pre-filled.
"""
self.ensure_one()
Inspection = self.env['fp.racking.inspection']
domain = [('sale_order_id', '=', self.sale_order_id.id)] \
if self.sale_order_id else []
return {
'type': 'ir.actions.act_window',
'name': _('Racking Inspections'),
'res_model': 'fp.racking.inspection',
'view_mode': 'list,form',
'domain': domain,
'context': {
'default_sale_order_id': self.sale_order_id.id
if self.sale_order_id else False,
},
}

View File

@@ -1,55 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
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 '
'<a href="/odoo/sale-order/%s">%s</a>. '
'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

View File

@@ -76,13 +76,24 @@
statusbar_visible="draft,counted,staged,closed"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_racking_inspections"
type="object"
class="oe_stat_button"
icon="fa-search-plus">
<field name="racking_inspection_count"
widget="statinfo"
string="Racking Inspections"/>
</button>
</div>
<div class="alert alert-info" role="alert">
<i class="fa fa-info-circle me-2"/>
<strong>Receiving = box count only.</strong>
Count the boxes the truck dropped off, set the number
below, and stage them for racking. The racking crew
opens the boxes and inspects each part — see
<em>Plating → Operations → Racking Inspection</em>.
opens the boxes and inspects each part — click
<strong>Racking Inspections</strong> above to jump
straight to the open inspection for this SO.
</div>
<div class="oe_title">
<h1>