changes
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating',
|
'name': 'Fusion Plating',
|
||||||
'version': '19.0.18.8.0',
|
'version': '19.0.18.11.0',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -546,15 +546,23 @@ export class RecipeTreeEditor extends Component {
|
|||||||
// ---- Navigation ---------------------------------------------------------
|
// ---- Navigation ---------------------------------------------------------
|
||||||
|
|
||||||
onBackToList() {
|
onBackToList() {
|
||||||
// If the editor was opened from the part-scoped Process Composer
|
// Pop this editor off the action stack and restore the
|
||||||
// (context carried part_id), return to that part's form instead
|
// previous controller — which is whatever opened the editor:
|
||||||
// of the generic Recipes list.
|
// * Recipes list → recipe form → editor ⇒ back to recipe form
|
||||||
|
// * Part form → composer → editor ⇒ back to composer
|
||||||
|
// * Part form → editor (direct link) ⇒ back to part form
|
||||||
//
|
//
|
||||||
// clearBreadcrumbs: this is a semantic RETURN, not a forward
|
// restore() preserves the full breadcrumb trail.
|
||||||
// navigation. Without it, every round-trip (part → composer →
|
// clearBreadcrumbs: true (the old behaviour) would wipe parent
|
||||||
// editor → back) leaves its intermediate pages on the breadcrumb
|
// crumbs and isolate the user on a single-crumb page.
|
||||||
// stack, so a second visit shows "…/Process Composer/Process
|
try {
|
||||||
// Editor/Process Composer/Process Editor/Part" nonsense.
|
this.action.restore();
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
// No prior controller — fall through to a sensible default.
|
||||||
|
}
|
||||||
|
// Fallback: when opened directly via URL with no prior crumb,
|
||||||
|
// pick the most contextual landing page we have.
|
||||||
if (this._partId) {
|
if (this._partId) {
|
||||||
this.action.doAction({
|
this.action.doAction({
|
||||||
type: "ir.actions.act_window",
|
type: "ir.actions.act_window",
|
||||||
|
|||||||
@@ -196,6 +196,17 @@ export class FpSimpleRecipeEditor extends Component {
|
|||||||
* breadcrumb stack so a second visit shows nonsense.
|
* breadcrumb stack so a second visit shows nonsense.
|
||||||
*/
|
*/
|
||||||
onBackToList() {
|
onBackToList() {
|
||||||
|
// Pop this editor off the action stack and restore the
|
||||||
|
// previous controller — preserves the full breadcrumb trail
|
||||||
|
// (Recipes > LGPS1104 > Editor → back keeps "Recipes"
|
||||||
|
// visible; Part > Composer > Editor → back returns to the
|
||||||
|
// Composer with crumbs intact).
|
||||||
|
try {
|
||||||
|
this.action.restore();
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
// No prior controller — fall through to a sensible default.
|
||||||
|
}
|
||||||
if (this._partId) {
|
if (this._partId) {
|
||||||
this.action.doAction(
|
this.action.doAction(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,7 +51,15 @@
|
|||||||
class="oe_stat_button" icon="fa-sitemap"
|
class="oe_stat_button" icon="fa-sitemap"
|
||||||
invisible="node_type != 'recipe'">
|
invisible="node_type != 'recipe'">
|
||||||
<field name="child_count" widget="statinfo"
|
<field name="child_count" widget="statinfo"
|
||||||
string="Steps"/>
|
string="Tree Editor"/>
|
||||||
|
</button>
|
||||||
|
<button name="action_open_simple_editor" type="object"
|
||||||
|
class="oe_stat_button" icon="fa-list-ol"
|
||||||
|
invisible="node_type != 'recipe'">
|
||||||
|
<div class="o_stat_info">
|
||||||
|
<span class="o_stat_text">Simple</span>
|
||||||
|
<span class="o_stat_text">Editor</span>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<widget name="web_ribbon" title="Archived"
|
<widget name="web_ribbon" title="Archived"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating — Configurator',
|
'name': 'Fusion Plating — Configurator',
|
||||||
'version': '19.0.18.6.0',
|
'version': '19.0.18.8.0',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
|
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ to depend on the configurator. Any field that references a model defined
|
|||||||
in configurator — like fp.pricing.rule, fp.part.catalog — must be
|
in configurator — like fp.pricing.rule, fp.part.catalog — must be
|
||||||
declared here.
|
declared here.
|
||||||
"""
|
"""
|
||||||
from odoo import fields, models
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
class FpProcessNode(models.Model):
|
class FpProcessNode(models.Model):
|
||||||
@@ -73,3 +73,47 @@ class FpProcessNode(models.Model):
|
|||||||
help='Friendly label shown in the variant picker '
|
help='Friendly label shown in the variant picker '
|
||||||
'(e.g. "Standard ENP", "Selective Masking", "Rework").',
|
'(e.g. "Standard ENP", "Selective Masking", "Rework").',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---- Linked Parts (cloned recipes) --------------------------------------
|
||||||
|
# On a shared template recipe, count + open all part-cloned recipe
|
||||||
|
# roots that were copied from this template (cloned_from_id == self).
|
||||||
|
# Only meaningful on shared templates (part_catalog_id IS NULL,
|
||||||
|
# node_type='recipe').
|
||||||
|
cloned_recipe_count = fields.Integer(
|
||||||
|
string='Linked Part Recipes',
|
||||||
|
compute='_compute_cloned_recipe_count',
|
||||||
|
)
|
||||||
|
|
||||||
|
def _compute_cloned_recipe_count(self):
|
||||||
|
Node = self.env['fusion.plating.process.node']
|
||||||
|
groups = Node._read_group(
|
||||||
|
domain=[
|
||||||
|
('cloned_from_id', 'in', self.ids),
|
||||||
|
('node_type', '=', 'recipe'),
|
||||||
|
('part_catalog_id', '!=', False),
|
||||||
|
],
|
||||||
|
groupby=['cloned_from_id'],
|
||||||
|
aggregates=['__count'],
|
||||||
|
)
|
||||||
|
counts = {src.id: count for src, count in groups}
|
||||||
|
for rec in self:
|
||||||
|
rec.cloned_recipe_count = counts.get(rec.id, 0)
|
||||||
|
|
||||||
|
def action_open_cloned_recipes(self):
|
||||||
|
"""Open the list of part-cloned recipe roots that came from this
|
||||||
|
template (i.e. cloned_from_id == self)."""
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': _('Linked Parts — %s', self.name),
|
||||||
|
'res_model': 'fusion.plating.process.node',
|
||||||
|
'view_mode': 'list,form',
|
||||||
|
'domain': [
|
||||||
|
('cloned_from_id', '=', self.id),
|
||||||
|
('node_type', '=', 'recipe'),
|
||||||
|
('part_catalog_id', '!=', False),
|
||||||
|
],
|
||||||
|
'context': {
|
||||||
|
'search_default_group_part': 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -217,6 +217,18 @@ export class FpPartProcessComposer extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
backToPart() {
|
backToPart() {
|
||||||
|
// Pop this composer off the action stack and restore the
|
||||||
|
// previous controller (the part form the user came from).
|
||||||
|
// Preserves the full breadcrumb trail — clearBreadcrumbs: true
|
||||||
|
// would wipe parent crumbs (e.g. "Parts > 2144A6201-105").
|
||||||
|
// Falls back to the part form only when restore() throws (e.g.
|
||||||
|
// composer opened directly via URL with no prior crumb).
|
||||||
|
try {
|
||||||
|
this.action.restore();
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
// No prior controller — fall through to the part form.
|
||||||
|
}
|
||||||
this.action.doAction({
|
this.action.doAction({
|
||||||
type: "ir.actions.act_window",
|
type: "ir.actions.act_window",
|
||||||
res_model: "fp.part.catalog",
|
res_model: "fp.part.catalog",
|
||||||
|
|||||||
@@ -42,6 +42,28 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- ========== Extend form view: Linked Parts smart button ========== -->
|
||||||
|
<!-- Smart button visible only on shared template recipes (no
|
||||||
|
part_catalog_id). Opens the list of part-cloned recipe roots
|
||||||
|
that were copied from this template. -->
|
||||||
|
<record id="view_fp_process_node_form_linked_parts"
|
||||||
|
model="ir.ui.view">
|
||||||
|
<field name="name">fusion.plating.process.node.form.linked.parts</field>
|
||||||
|
<field name="model">fusion.plating.process.node</field>
|
||||||
|
<field name="inherit_id"
|
||||||
|
ref="fusion_plating.view_fp_process_node_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='button_box']" position="inside">
|
||||||
|
<button name="action_open_cloned_recipes" type="object"
|
||||||
|
class="oe_stat_button" icon="fa-link"
|
||||||
|
invisible="node_type != 'recipe' or part_catalog_id">
|
||||||
|
<field name="cloned_recipe_count" widget="statinfo"
|
||||||
|
string="Linked Parts"/>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<!-- ========== Extend list view: surface part column ========== -->
|
<!-- ========== Extend list view: surface part column ========== -->
|
||||||
<record id="view_fp_process_node_tree_part_scoped"
|
<record id="view_fp_process_node_tree_part_scoped"
|
||||||
model="ir.ui.view">
|
model="ir.ui.view">
|
||||||
|
|||||||
Reference in New Issue
Block a user