folder rename
This commit is contained in:
81
fusion_plating/fusion_plating_bridge_quality/README.md
Normal file
81
fusion_plating/fusion_plating_bridge_quality/README.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Fusion Plating — Quality Bridge (EE)
|
||||
|
||||
Part of the Fusion Plating product family by Nexa Systems Inc.
|
||||
|
||||
One-way sync from Fusion Plating NCRs to Odoo Enterprise `quality.alert`
|
||||
records. Lets plating shops running Odoo EE keep using the Fusion Plating
|
||||
QMS (CE + EE compatible) while getting the benefit of EE's native quality
|
||||
dashboards on the same data.
|
||||
|
||||
## Status
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Odoo version | 19.0 |
|
||||
| License | OPL-1 |
|
||||
| Auto-install | Yes — when `fusion_plating_quality` AND EE `quality` are both installed |
|
||||
| Sync direction | One-way: NCR → quality.alert |
|
||||
|
||||
## How it works
|
||||
|
||||
1. Creating or updating a `fusion.plating.ncr` record triggers
|
||||
`_sync_to_quality_alert()` in `models/fp_ncr.py`.
|
||||
2. The bridge builds a vals dict from the NCR and creates (or updates) the
|
||||
linked `quality.alert` record under a dedicated **Plating Quality** team.
|
||||
3. The bridge is defensive: every field write is gated on the live
|
||||
`quality.alert` schema so a minor EE release that renames or drops a
|
||||
field does not break NCR saves.
|
||||
4. If `quality.alert` is not available for any reason, the sync silently
|
||||
no-ops — NCRs continue to work normally in the base QMS module.
|
||||
|
||||
## Field mapping
|
||||
|
||||
| Fusion Plating NCR | Odoo EE quality.alert |
|
||||
| --- | --- |
|
||||
| `description` (first line) | `name` / `title` |
|
||||
| `description` (full html) | `description` (if html) or stripped text |
|
||||
| `containment` | `action_corrective` (if present) |
|
||||
| `root_cause` | `action_preventive` (if present) |
|
||||
| `severity` | `priority` (0/1/2/3 star) |
|
||||
| `state` | `stage_id` (best-effort name match) |
|
||||
| `customer_partner_id` | `partner_id` |
|
||||
| `company_id` | `company_id` |
|
||||
| — | `team_id` (always "Plating Quality") |
|
||||
|
||||
## Per-record controls
|
||||
|
||||
Each NCR gains three `x_fc_*` fields (visible under the **Quality Alert
|
||||
Sync** notebook page on the NCR form):
|
||||
|
||||
- `x_fc_auto_sync` — toggle per-record sync; set to False to freeze the
|
||||
mirror on a specific NCR
|
||||
- `x_fc_quality_alert_id` — read-only pointer to the mirrored alert
|
||||
- `x_fc_quality_alert_synced` — read-only "has been synced at least once"
|
||||
flag
|
||||
|
||||
## Manual sync
|
||||
|
||||
Use the **Sync to Quality** header button on the NCR form to push the
|
||||
latest values on demand. The mirrored alert's **Quality Alert** smart
|
||||
button jumps straight to the linked EE record.
|
||||
|
||||
## What the bridge does NOT do
|
||||
|
||||
- **No write-back.** Edits to the EE `quality.alert` record are not
|
||||
propagated back into Fusion Plating. NCR is the source of truth.
|
||||
- **No CAPA mirroring.** Fusion Plating CAPAs remain in
|
||||
`fusion.plating.capa`; they are not copied into `quality.point` or
|
||||
`quality.check`.
|
||||
- **No historical backfill.** Existing NCRs created before this module was
|
||||
installed are not synced automatically — run the **Sync to Quality**
|
||||
button manually on the ones you want mirrored.
|
||||
|
||||
## Notes for deployment
|
||||
|
||||
- If your EE release uses `quality.team` instead of `quality.alert.team`,
|
||||
edit `data/quality_alert_team_data.xml` to match and reinstall the
|
||||
module. No Python changes required.
|
||||
- The sync runs with `sudo()` so shop operators without direct write
|
||||
access to the EE quality module still produce mirrored alerts cleanly.
|
||||
|
||||
Copyright (c) 2026 Nexa Systems Inc. All rights reserved.
|
||||
6
fusion_plating/fusion_plating_bridge_quality/__init__.py
Normal file
6
fusion_plating/fusion_plating_bridge_quality/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from . import models
|
||||
65
fusion_plating/fusion_plating_bridge_quality/__manifest__.py
Normal file
65
fusion_plating/fusion_plating_bridge_quality/__manifest__.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Quality Bridge (EE)',
|
||||
'version': '19.0.1.0.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Enterprise bridge: mirrors Fusion Plating NCRs into Odoo EE '
|
||||
'quality.alert for native dashboard integration. Auto-installs '
|
||||
'when EE quality and fusion_plating_quality are both present.',
|
||||
'description': """
|
||||
Fusion Plating — Quality Bridge (Enterprise)
|
||||
============================================
|
||||
|
||||
Part of the Fusion Plating product family by Nexa Systems Inc.
|
||||
|
||||
One-way sync from Fusion Plating NCRs to Odoo EE quality.alert records.
|
||||
|
||||
Shops on Odoo Enterprise running both modules continue to work primarily
|
||||
in Fusion Plating's QMS (which is CE + EE compatible), while getting the
|
||||
benefit of EE's native quality dashboards on the same data.
|
||||
|
||||
How it works
|
||||
------------
|
||||
* When a Fusion Plating NCR is created or updated, a mirrored
|
||||
``quality.alert`` record is created (or updated) automatically.
|
||||
* The sync is ONE-WAY — NCR is the source of truth. Changes made
|
||||
directly to the EE quality.alert are NOT pushed back into the NCR.
|
||||
* A dedicated "Plating Quality" team is created in the EE quality
|
||||
module so mirrored alerts land in their own distinct team and do
|
||||
not pollute the default quality dashboards.
|
||||
* Per-NCR opt-out is available via the ``x_fc_auto_sync`` field.
|
||||
|
||||
Installation
|
||||
------------
|
||||
This module has ``auto_install = True``: it installs itself automatically
|
||||
as soon as both ``fusion_plating_quality`` and ``quality`` (EE) are
|
||||
present in the database. Community Edition users who do not have the EE
|
||||
``quality`` module will never see this bridge — the Fusion Plating QMS
|
||||
remains fully self-contained.
|
||||
|
||||
Copyright (c) 2026 Nexa Systems Inc. All rights reserved.
|
||||
""",
|
||||
'author': 'Nexa Systems Inc.',
|
||||
'website': 'https://www.nexasystems.ca',
|
||||
'maintainer': 'Nexa Systems Inc.',
|
||||
'support': 'support@nexasystems.ca',
|
||||
'license': 'OPL-1',
|
||||
'price': 0.00,
|
||||
'currency': 'CAD',
|
||||
'depends': [
|
||||
'fusion_plating_quality',
|
||||
'quality',
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/quality_alert_team_data.xml',
|
||||
'views/fp_ncr_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': True,
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
|
||||
Creates a dedicated "Plating Quality" team in the Odoo EE quality module
|
||||
so mirrored NCR alerts land in their own bucket and do not pollute the
|
||||
shop's default quality dashboards.
|
||||
|
||||
NOTE ON MODEL NAME:
|
||||
In recent Odoo 19 EE releases the team model on quality.alert is
|
||||
``quality.alert.team``. Older EE releases used ``quality.team``. We
|
||||
target the current name here; if a shop is on an older release and
|
||||
gets a parsing error loading this file, swap the model to ``quality.team``
|
||||
and reinstall. No Python changes are required because the bridge resolves
|
||||
the team purely by XML ID.
|
||||
-->
|
||||
<odoo noupdate="1">
|
||||
|
||||
<record id="quality_team_plating" model="quality.alert.team">
|
||||
<field name="name">Plating Quality</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from . import fp_ncr
|
||||
289
fusion_plating/fusion_plating_bridge_quality/models/fp_ncr.py
Normal file
289
fusion_plating/fusion_plating_bridge_quality/models/fp_ncr.py
Normal file
@@ -0,0 +1,289 @@
|
||||
# -*- 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 _, api, fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Map Fusion Plating NCR states to a human readable stage name we will
|
||||
# try to match (case-insensitive, partial match) against any available
|
||||
# quality.alert.stage records. If no match is found, the alert is left
|
||||
# with its default stage and Odoo's own onchange will pick one.
|
||||
_STATE_TO_STAGE_HINT = {
|
||||
'draft': 'new',
|
||||
'open': 'in progress',
|
||||
'containment': 'in progress',
|
||||
'disposition': 'in progress',
|
||||
'closed': 'done',
|
||||
}
|
||||
|
||||
# Fusion Plating severity -> quality.alert priority (0..3 star scale
|
||||
# used across mrp/quality). Kept defensive: priority is written via
|
||||
# setdefault, so if a shop has customised the field we don't clobber it.
|
||||
_SEVERITY_TO_PRIORITY = {
|
||||
'low': '0',
|
||||
'medium': '1',
|
||||
'high': '2',
|
||||
'critical': '3',
|
||||
}
|
||||
|
||||
|
||||
class FpNcr(models.Model):
|
||||
"""Extend Fusion Plating NCR so each record is mirrored into the
|
||||
Odoo EE ``quality.alert`` model. One-way sync, NCR is the source
|
||||
of truth.
|
||||
"""
|
||||
_inherit = 'fusion.plating.ncr'
|
||||
|
||||
x_fc_quality_alert_id = fields.Many2one(
|
||||
'quality.alert',
|
||||
string='Quality Alert',
|
||||
copy=False,
|
||||
readonly=True,
|
||||
help='Mirrored Odoo EE quality.alert record created from this NCR.',
|
||||
)
|
||||
x_fc_quality_alert_synced = fields.Boolean(
|
||||
string='Synced to Quality',
|
||||
copy=False,
|
||||
readonly=True,
|
||||
help='True once this NCR has been mirrored to quality.alert at '
|
||||
'least once.',
|
||||
)
|
||||
x_fc_auto_sync = fields.Boolean(
|
||||
string='Auto-sync to Quality',
|
||||
default=True,
|
||||
copy=False,
|
||||
help='When enabled, creating or updating this NCR automatically '
|
||||
'pushes changes to the mirrored Odoo EE quality.alert record.',
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ------------------------------------------------------------------
|
||||
def _fp_bridge_strip_html(self, html_value):
|
||||
"""Convert an HTML field value to plain text for quality.alert
|
||||
fields that are plain Text. Kept tiny on purpose so it has no
|
||||
dependencies beyond what's already in core Odoo."""
|
||||
if not html_value:
|
||||
return ''
|
||||
try:
|
||||
from odoo.tools import html2plaintext
|
||||
return html2plaintext(html_value) or ''
|
||||
except Exception: # pragma: no cover - extremely defensive
|
||||
return str(html_value)
|
||||
|
||||
def _fp_bridge_team(self):
|
||||
"""Return the dedicated Plating team record, if it exists."""
|
||||
return self.env.ref(
|
||||
'fusion_plating_bridge_quality.quality_team_plating',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
|
||||
def _fp_bridge_stage_for_state(self, state_value):
|
||||
"""Best-effort mapping of our NCR state to a quality.alert.stage.
|
||||
|
||||
The EE quality module ships with a handful of default stages
|
||||
(``New``, ``In Progress``, ``Confirmed``, ``Solved``, ``Cancelled``)
|
||||
but shops often rename these. We do a case-insensitive partial
|
||||
match against the hint and fall back to False so Odoo picks the
|
||||
default stage itself.
|
||||
"""
|
||||
Stage = self.env.get('quality.alert.stage')
|
||||
if Stage is None:
|
||||
return False
|
||||
hint = _STATE_TO_STAGE_HINT.get(state_value or '', '')
|
||||
if not hint:
|
||||
return False
|
||||
stage = Stage.sudo().search(
|
||||
[('name', 'ilike', hint)],
|
||||
limit=1,
|
||||
)
|
||||
return stage.id if stage else False
|
||||
|
||||
def _prepare_quality_alert_vals(self):
|
||||
"""Build the vals dict used to create/update a quality.alert from
|
||||
this NCR. Written defensively — every field is checked against
|
||||
the live ``quality.alert`` model before being added, so the bridge
|
||||
keeps working even if a minor EE release renames or removes a
|
||||
field.
|
||||
"""
|
||||
self.ensure_one()
|
||||
Alert = self.env.get('quality.alert')
|
||||
if Alert is None:
|
||||
return {}
|
||||
alert_fields = Alert._fields
|
||||
|
||||
# Build a short, dashboard-friendly title from the NCR description
|
||||
# (falling back to the reference).
|
||||
desc_plain = self._fp_bridge_strip_html(self.description)
|
||||
title_source = desc_plain or (self.name or '')
|
||||
title = (title_source or '').strip().splitlines()[0] if title_source else self.name
|
||||
if title and len(title) > 200:
|
||||
title = title[:197] + '...'
|
||||
if not title:
|
||||
title = self.name
|
||||
|
||||
vals = {}
|
||||
|
||||
# --- Identity / title ------------------------------------------------
|
||||
if 'name' in alert_fields:
|
||||
# quality.alert ``name`` is usually the short title in EE.
|
||||
vals['name'] = title or self.name
|
||||
if 'title' in alert_fields:
|
||||
vals['title'] = title or self.name
|
||||
|
||||
# --- Team ------------------------------------------------------------
|
||||
team = self._fp_bridge_team()
|
||||
if team and 'team_id' in alert_fields:
|
||||
vals['team_id'] = team.id
|
||||
|
||||
# --- Company ---------------------------------------------------------
|
||||
if 'company_id' in alert_fields and self.company_id:
|
||||
vals['company_id'] = self.company_id.id
|
||||
|
||||
# --- Description -----------------------------------------------------
|
||||
# quality.alert.description has historically been Html in EE; write
|
||||
# the raw NCR html straight through when the target is Html, and
|
||||
# strip to plaintext otherwise.
|
||||
if 'description' in alert_fields:
|
||||
desc_field = alert_fields['description']
|
||||
if getattr(desc_field, 'type', None) == 'html':
|
||||
vals['description'] = self.description or False
|
||||
else:
|
||||
vals['description'] = desc_plain or False
|
||||
|
||||
# --- Partner (customer) ---------------------------------------------
|
||||
if 'partner_id' in alert_fields and self.customer_partner_id:
|
||||
vals['partner_id'] = self.customer_partner_id.id
|
||||
|
||||
# --- Stage / state mapping ------------------------------------------
|
||||
if 'stage_id' in alert_fields:
|
||||
stage_id = self._fp_bridge_stage_for_state(self.state)
|
||||
if stage_id:
|
||||
vals['stage_id'] = stage_id
|
||||
# Some EE versions also expose a simple state selection on
|
||||
# quality.alert. Only touch it if it exists.
|
||||
if 'action_corrective' in alert_fields:
|
||||
# Mirror our containment narrative into corrective actions
|
||||
# when that html field exists on EE quality.alert.
|
||||
if alert_fields['action_corrective'].type == 'html':
|
||||
vals['action_corrective'] = self.containment or False
|
||||
else:
|
||||
vals['action_corrective'] = self._fp_bridge_strip_html(
|
||||
self.containment
|
||||
) or False
|
||||
if 'action_preventive' in alert_fields:
|
||||
if alert_fields['action_preventive'].type == 'html':
|
||||
vals['action_preventive'] = self.root_cause or False
|
||||
else:
|
||||
vals['action_preventive'] = self._fp_bridge_strip_html(
|
||||
self.root_cause
|
||||
) or False
|
||||
|
||||
# --- Priority --------------------------------------------------------
|
||||
if 'priority' in alert_fields:
|
||||
prio = _SEVERITY_TO_PRIORITY.get(self.severity)
|
||||
if prio is not None:
|
||||
vals['priority'] = prio
|
||||
|
||||
# --- Reason (root cause dropdown) -----------------------------------
|
||||
# quality.alert may expose ``reason_id`` pointing at quality.reason.
|
||||
# We do not create reason records — shops curate those themselves —
|
||||
# but we leave the mapping point here for future use.
|
||||
|
||||
return vals
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Sync engine
|
||||
# ------------------------------------------------------------------
|
||||
def _fp_bridge_quality_available(self):
|
||||
"""Cheap capability check: is the EE quality.alert model loaded
|
||||
in this database?"""
|
||||
return self.env.get('quality.alert') is not None
|
||||
|
||||
def _sync_to_quality_alert(self):
|
||||
"""Create or update the mirrored quality.alert for every NCR in
|
||||
``self`` that has ``x_fc_auto_sync`` enabled. Silent no-op when
|
||||
the EE quality module isn't available."""
|
||||
if not self._fp_bridge_quality_available():
|
||||
return
|
||||
Alert = self.env['quality.alert'].sudo()
|
||||
for ncr in self:
|
||||
if not ncr.x_fc_auto_sync:
|
||||
continue
|
||||
try:
|
||||
vals = ncr._prepare_quality_alert_vals()
|
||||
if not vals:
|
||||
continue
|
||||
if ncr.x_fc_quality_alert_id:
|
||||
ncr.x_fc_quality_alert_id.sudo().write(vals)
|
||||
else:
|
||||
alert = Alert.create(vals)
|
||||
# Bypass normal write to avoid re-triggering sync.
|
||||
ncr.with_context(
|
||||
fp_bridge_skip_sync=True,
|
||||
).write({
|
||||
'x_fc_quality_alert_id': alert.id,
|
||||
'x_fc_quality_alert_synced': True,
|
||||
})
|
||||
except Exception as exc:
|
||||
_logger.warning(
|
||||
'Fusion Plating bridge: failed to sync NCR %s to '
|
||||
'quality.alert: %s',
|
||||
ncr.name, exc,
|
||||
)
|
||||
# Non-fatal — never break the NCR save just because the
|
||||
# mirror failed.
|
||||
|
||||
def action_sync_to_quality(self):
|
||||
"""Manual "Sync to Quality" header button."""
|
||||
self._sync_to_quality_alert()
|
||||
return {'type': 'ir.actions.client', 'tag': 'reload'}
|
||||
|
||||
def action_view_quality_alert(self):
|
||||
self.ensure_one()
|
||||
if not self.x_fc_quality_alert_id:
|
||||
return False
|
||||
return {
|
||||
'name': _('Quality Alert'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'quality.alert',
|
||||
'res_id': self.x_fc_quality_alert_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# CRUD overrides
|
||||
# ------------------------------------------------------------------
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
records = super().create(vals_list)
|
||||
if not self.env.context.get('fp_bridge_skip_sync'):
|
||||
records._sync_to_quality_alert()
|
||||
return records
|
||||
|
||||
# Fields whose changes should trigger a resync.
|
||||
_FP_BRIDGE_SYNC_FIELDS = {
|
||||
'description',
|
||||
'root_cause',
|
||||
'containment',
|
||||
'disposition',
|
||||
'state',
|
||||
'severity',
|
||||
'customer_partner_id',
|
||||
'x_fc_auto_sync',
|
||||
}
|
||||
|
||||
def write(self, vals):
|
||||
result = super().write(vals)
|
||||
if self.env.context.get('fp_bridge_skip_sync'):
|
||||
return result
|
||||
if self._FP_BRIDGE_SYNC_FIELDS & set(vals.keys()):
|
||||
self._sync_to_quality_alert()
|
||||
return result
|
||||
@@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_fp_bridge_quality_alert_manager,fp.bridge.quality.alert.manager,quality.model_quality_alert,fusion_plating.group_fusion_plating_manager,1,1,1,0
|
||||
access_fp_bridge_quality_alert_supervisor,fp.bridge.quality.alert.supervisor,quality.model_quality_alert,fusion_plating.group_fusion_plating_supervisor,1,0,0,0
|
||||
access_fp_bridge_quality_alert_stage_manager,fp.bridge.quality.alert.stage.manager,quality.model_quality_alert_stage,fusion_plating.group_fusion_plating_manager,1,0,0,0
|
||||
access_fp_bridge_quality_alert_team_manager,fp.bridge.quality.alert.team.manager,quality.model_quality_alert_team,fusion_plating.group_fusion_plating_manager,1,0,0,0
|
||||
|
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!--
|
||||
Extend the Fusion Plating NCR form to surface the mirrored EE
|
||||
quality.alert record. Adds:
|
||||
* A header "Sync to Quality" button
|
||||
* A smart button jumping to the mirrored alert
|
||||
* A "Quality Alert Sync" notebook page with the raw sync fields
|
||||
-->
|
||||
<record id="view_fp_ncr_form_bridge_quality" model="ir.ui.view">
|
||||
<field name="name">fp.ncr.form.bridge.quality</field>
|
||||
<field name="model">fusion.plating.ncr</field>
|
||||
<field name="inherit_id" ref="fusion_plating_quality.view_fp_ncr_form"/>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<!-- Header: manual sync button -->
|
||||
<xpath expr="//header" position="inside">
|
||||
<button name="action_sync_to_quality"
|
||||
string="Sync to Quality"
|
||||
type="object"
|
||||
class="btn-secondary"
|
||||
invisible="not x_fc_auto_sync"
|
||||
help="Push this NCR's latest values into the mirrored Odoo EE quality.alert record."/>
|
||||
</xpath>
|
||||
|
||||
<!-- Smart button: jump to mirrored alert -->
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_view_quality_alert"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-bell"
|
||||
invisible="not x_fc_quality_alert_id">
|
||||
<div class="o_stat_info">
|
||||
<span class="o_stat_text">Quality</span>
|
||||
<span class="o_stat_text">Alert</span>
|
||||
</div>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<!-- Notebook page: raw sync fields -->
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Quality Alert Sync" name="quality_alert_sync">
|
||||
<group>
|
||||
<group string="Sync Status">
|
||||
<field name="x_fc_auto_sync"/>
|
||||
<field name="x_fc_quality_alert_synced" readonly="1"/>
|
||||
<field name="x_fc_quality_alert_id" readonly="1"/>
|
||||
</group>
|
||||
<group string="About">
|
||||
<div colspan="2" class="text-muted">
|
||||
<p>
|
||||
This NCR is mirrored into the Odoo Enterprise
|
||||
<strong>Quality</strong> module so EE users can monitor it
|
||||
from the native quality dashboards.
|
||||
</p>
|
||||
<p>
|
||||
The sync is <strong>one-way</strong>: Fusion Plating is the
|
||||
source of truth. Edits made directly to the mirrored
|
||||
<em>quality.alert</em> record will be overwritten the next
|
||||
time this NCR changes.
|
||||
</p>
|
||||
<p>
|
||||
Turn off <strong>Auto-sync to Quality</strong> on an individual
|
||||
NCR to freeze its mirror, or use the
|
||||
<strong>Sync to Quality</strong> header button to push changes
|
||||
on demand.
|
||||
</p>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user