changes
Some checks failed
fusion_accounting CI / test (fusion_accounting_ai) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_core) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_migration) (push) Has been cancelled

This commit is contained in:
gsinghpal
2026-05-17 03:20:33 -04:00
parent f8586611c9
commit d3c5c25865
30 changed files with 712 additions and 183 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Quality (QMS)',
'version': '19.0.6.2.0',
'version': '19.0.6.6.0',
'category': 'Manufacturing/Plating',
'summary': 'Native QMS for plating shops: NCR, CAPA, calibration, AVL, FAIR, '
'internal audits, customer specs, document control. CE + EE compatible.',
@@ -128,6 +128,10 @@ Copyright (c) 2026 Nexa Systems Inc. All rights reserved.
'fusion_plating_quality/static/src/scss/fp_quality_dashboard.scss',
'fusion_plating_quality/static/src/xml/fp_quality_dashboard.xml',
'fusion_plating_quality/static/src/js/fp_quality_dashboard.js',
# Contract Review flow — forced redirect into QA-005 after
# an estimator creates a part under a toggle-on customer.
# Pairs with the bus.bus push in fp_part_catalog.py:create.
'fusion_plating_quality/static/src/js/contract_review_redirect.js',
],
},
'installable': True,

View File

@@ -23,11 +23,30 @@ class FpDirectOrderLine(models.Model):
@api.onchange('part_catalog_id')
def _onchange_part_default_spec(self):
"""Pre-fill the line's specification from the part's default."""
"""Pre-fill the line's specification.
Priority (first non-empty result wins, matches the same
"remember last entered" pattern used by process / unit_price /
tax / thickness in the base wizard line):
1. What the operator already typed on this line — never clobber
2. Most recent SO line for (part, customer) where a spec
was set — the "repeat order" carry-over
3. Part's stored default — x_fc_default_customer_spec_id
"""
for rec in self:
if (rec.part_catalog_id
and rec.part_catalog_id.x_fc_default_customer_spec_id
and not rec.customer_spec_id):
if not rec.part_catalog_id or rec.customer_spec_id:
continue
partner = rec.wizard_id.partner_id if rec.wizard_id else False
if partner:
recent = self.env['sale.order.line'].search([
('x_fc_part_catalog_id', '=', rec.part_catalog_id.id),
('order_id.partner_id', '=', partner.id),
('x_fc_customer_spec_id', '!=', False),
], order='create_date desc', limit=1)
if recent:
rec.customer_spec_id = recent.x_fc_customer_spec_id
continue
if rec.part_catalog_id.x_fc_default_customer_spec_id:
rec.customer_spec_id = (
rec.part_catalog_id.x_fc_default_customer_spec_id
)
@@ -37,12 +56,22 @@ class FpDirectOrderWizard(models.Model):
_inherit = 'fp.direct.order.wizard'
def action_create_order(self):
"""Carry customer_spec_id from each wizard line to its SO line.
"""Carry customer_spec_id from each wizard line to its SO line,
and (when the operator opted in via push_to_defaults) save the
spec back to the part as its new default so the next order
auto-fills without relying on a SO-line history lookup.
The base method (in configurator) builds the SO with all the
coating/treatment/process fields. We can't insert spec into the
vals dict from here without a circular dep, so post-create we
pair wizard lines to SO lines by sequence and patch.
The push-to-defaults block mirrors the base wizard's thickness
push (action_create_order's "6. Push-to-defaults" loop) — spec
lives here in the quality module so the back-write lives here
too. Only fills when the part default is currently empty so we
never clobber an existing default that the part-form user set
explicitly.
"""
result = super().action_create_order()
if self.sale_order_id:
@@ -55,4 +84,15 @@ class FpDirectOrderWizard(models.Model):
for wiz_line, so_line in zip(wiz_lines, so_lines):
if wiz_line.customer_spec_id and not so_line.x_fc_customer_spec_id:
so_line.x_fc_customer_spec_id = wiz_line.customer_spec_id.id
# Spec push-to-default — only on first-time parts that
# had the toggle auto-ticked (or manually ticked). Skip
# one-off parts and parts that already have a default.
if (wiz_line.push_to_defaults
and not wiz_line.is_one_off
and wiz_line.customer_spec_id
and wiz_line.part_catalog_id
and not wiz_line.part_catalog_id.x_fc_default_customer_spec_id):
wiz_line.part_catalog_id.x_fc_default_customer_spec_id = (
wiz_line.customer_spec_id.id
)
return result

View File

@@ -208,11 +208,8 @@ class FpPartCatalog(models.Model):
% part_label,
'message': _(
'Customer %(c)s requires a Contract Review '
'(QA-005) on new parts. The review record '
'has been pre-created — open it using the '
'Contract Review smart button at the top '
'of the part form, or from your Activities '
'inbox.'
'(QA-005) on new parts. Opening the review '
'form now…'
) % {'c': customer_label},
'type': 'warning',
'sticky': True,
@@ -224,6 +221,30 @@ class FpPartCatalog(models.Model):
'review notification', part.id, exc_info=True,
)
# 3) Forced redirect into the QA-005 form. The frontend
# listener (see fusion_plating_quality/static/src/js/
# contract_review_redirect.js) intercepts this bus
# notification and calls action.doAction() to navigate.
# Rule 3 of the contract-review flow: customer-toggle-on
# parts force the QA-005 to open right after save so the
# estimator can complete (or visibly defer) the review
# before moving on.
try:
Bus._sendone(
self.env.user.partner_id,
'fusion_plating.contract_review_redirect',
{
'review_id': review.id,
'part_id': part.id,
'part_label': part_label,
},
)
except Exception:
_logger.warning(
'fp.part.catalog %s: could not push contract-'
'review redirect bus message', part.id, exc_info=True,
)
# ---- Actions -------------------------------------------------------------
def action_start_contract_review(self):

View File

@@ -19,7 +19,7 @@ class ResCompany(models.Model):
'res_company_qa_assistant_rel',
'company_id',
'user_id',
string='QA Assistant Signers',
string='Planning Signers',
domain=[('share', '=', False)],
help='Users authorised to sign Section 2.0 (Planning / Production '
'Review) on a Contract Review. Plating Managers can sign '
@@ -30,11 +30,13 @@ class ResCompany(models.Model):
'res_company_qa_manager_rel',
'company_id',
'user_id',
string='QA Manager Signers',
string='QA Manager',
domain=[('share', '=', False)],
help='Users authorised to sign Section 3.0 (Quality Review) on a '
'Contract Review. Plating Managers can sign regardless of '
'this list.',
help='QA Manager(s) for the shop. Signs Section 3.0 (Quality '
'Review) on a Contract Review AND is the default Certified '
'By signer on the Work Order Detail report (first user in '
'the list is used for the cert signature). Plating Managers '
'can sign Contract Reviews regardless of this list.',
)
def _fp_get_qa_signers(self, section):

View File

@@ -15,10 +15,10 @@ class ResConfigSettings(models.TransientModel):
x_fc_qa_assistant_user_ids = fields.Many2many(
related='company_id.x_fc_qa_assistant_user_ids',
readonly=False,
string='QA Assistant Signers',
string='Planning Signers',
)
x_fc_qa_manager_user_ids = fields.Many2many(
related='company_id.x_fc_qa_manager_user_ids',
readonly=False,
string='QA Manager Signers',
string='QA Manager',
)

View File

@@ -0,0 +1,51 @@
/** @odoo-module **/
/*
* Copyright 2026 Nexa Systems Inc.
* License OPL-1 (Odoo Proprietary License v1.0)
* Part of the Fusion Plating product family.
*
* Forces the QA-005 Contract Review form to open right after an
* estimator creates a new fp.part.catalog under a customer that has
* x_fc_contract_review_required = True. Rule 3 of the contract-review
* flow.
*
* Server side (fusion_plating_quality/models/fp_part_catalog.py):
* after auto-creating the fp.contract.review record on part create,
* push a bus notification of type "fusion_plating.contract_review_redirect"
* with payload {review_id, part_id, part_label}.
*
* Frontend side (this file): a service subscribes to that bus type
* on session start. When a payload arrives, dispatch an ir.actions.
* act_window opening the review's form so the user lands on the QA-005
* automatically. They can still close it and come back later — the
* WO-step gate (rule 5) is the backstop.
*/
import { registry } from "@web/core/registry";
const contractReviewRedirectService = {
dependencies: ["bus_service", "action"],
start(env, { bus_service: bus, action }) {
bus.subscribe(
"fusion_plating.contract_review_redirect",
(payload) => {
if (!payload || !payload.review_id) {
return;
}
action.doAction({
type: "ir.actions.act_window",
res_model: "fp.contract.review",
res_id: payload.review_id,
view_mode: "form",
views: [[false, "form"]],
target: "current",
});
},
);
bus.start();
},
};
registry
.category("services")
.add("fusion_plating_contract_review_redirect", contractReviewRedirectService);

View File

@@ -22,7 +22,7 @@
permissions are affected. Plating Managers can
always sign regardless of these lists.">
<setting id="fp_qa_assistant_signers"
string="QA Assistant Signers"
string="Planning Signers"
help="Users authorised to sign Section 2.0
(Planning / Production Review). Typically
one or two people. Leave empty if any
@@ -33,10 +33,13 @@
widget="many2many_tags"/>
</setting>
<setting id="fp_qa_manager_signers"
string="QA Manager Signers"
help="Users authorised to sign Section 3.0
(Quality Review). Typically the QA
Manager for the shop.">
string="QA Manager"
help="QA Manager(s) for the shop. Signs
Section 3.0 (Quality Review) of the
Contract Review, AND appears as the
Certified By signer on the Work Order
Detail report. The first user in the
list is used for the cert signature.">
<field name="x_fc_qa_manager_user_ids"
widget="many2many_tags"/>
</setting>