feat(plating): hide quality smart buttons at zero + order-scope NCR/CAPA counts
On the sale.order and fp.job forms, the Holds/Checks/NCRs/CAPAs/RMAs quality smart buttons (and the SO WO button) now hide when their count is 0, so the row shows only quality work that exists. NCR and CAPA counts are re-scoped from customer-wide to order/job via a shared _fp_quality_ncr_ids() helper (NCRs reached through the order's RMAs + the order/job's holds), so each badge and the list its button opens always agree. Also aligned the job RMA button's list domain to its (already SO-scoped) count. Reverts the Sub-12 Phase D "always-visible (zero is OK)" choice back to the module's documented hide-at-zero convention. - fusion_plating_quality 19.0.8.1.0 -> 19.0.8.2.0 - fusion_plating_jobs 19.0.12.4.0 -> 19.0.12.5.0 Deployed + verified on entech (badge == helper across sampled SOs/jobs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -460,10 +460,12 @@ invisible="x_fc_ncr_count == 0" <!-- NCRs: only when there are o
|
||||
|
||||
Add the supporting boolean / count as a stored or non-stored compute on the model. Group multiple visibility helpers in ONE compute method to keep the `_compute_smart_button_visibility` chain cheap (one pass over `order_line`).
|
||||
|
||||
**Quality buttons on `sale.order` + `fp.job` are hide-at-zero (2026-06-04).** The Holds / Checks / NCRs / CAPAs / RMAs row (and the SO `WO` button) all carry `invisible="<count> == 0"`, so the row shows only quality work that exists — the Sub-12 Phase D "always-visible (zero is OK)" choice was reverted to this convention. The **NCR and CAPA counts are order/job-scoped, NOT customer-wide**: a single `_fp_quality_ncr_ids()` helper on both `sale.order` and `fp.job` (`fusion_plating_quality/models/fp_quality_smart_buttons.py`) returns NCRs reached via the order's RMAs (`ncr.rma_id`) + NCRs spawned from the order's/job's holds (`hold.ncr_id`); the badge count AND the button's action-window domain both read that helper, so they always agree. There is no `ncr.sale_order_id` / `ncr.job_id` field — those two paths are the only honest order links, so an NCR with neither (a QA-team-logged customer NCR) stays off the order/job by design. Don't restore the customer-wide union or the always-visible buttons.
|
||||
|
||||
### Ordering / placement
|
||||
|
||||
- **Always-visible meaningful buttons go first** — they're the workflow signals an operator scans for first (Receiving, Plating Jobs, Holds, Checks).
|
||||
- **NCRs / RMAs sit in the middle** — visible only when present (so they pop only when there's actual quality work).
|
||||
- **Always-visible meaningful buttons go first** — they're the workflow signals an operator scans for first (Receiving, WO/Plating Jobs).
|
||||
- **Quality buttons (Holds, Checks, NCRs, CAPAs, RMAs) are hidden when their count is 0** — they pop only when there's actual quality work on the order/job.
|
||||
- **Conditional / multi-lens analytical buttons go LAST** (BOM Items, By Job Group). They overflow into the `More ▾` dropdown when the row is full, which is fine — they're the "I'm zooming into a complex SO" tools, not the daily-driver buttons.
|
||||
|
||||
To add a button at the end of the row regardless of where the inherited view positions things, use a second xpath:
|
||||
@@ -1204,8 +1206,8 @@ Each model gets `tag_ids`, `reason_id`, `team_id`. NCR + RMA additionally get `s
|
||||
- `fp.job.step.button_finish`: trigger `job_step_done` points.
|
||||
|
||||
### Phase D — Integration polish (~1 day)
|
||||
1. **`fp.job` form smart-button row**: add `Holds`, `Checks`, `NCRs`, `CAPAs`, `RMAs` buttons with badge counts. Always-visible (zero is OK).
|
||||
2. **`sale.order` form smart-button row**: same five, rolled up across all linked jobs.
|
||||
1. **`fp.job` form smart-button row**: add `Holds`, `Checks`, `NCRs`, `CAPAs`, `RMAs` buttons with badge counts. ~~Always-visible (zero is OK).~~ **Superseded 2026-06-04 — now hide-at-zero + order/job-scoped NCR/CAPA counts; see the Smart Buttons "Conditional visibility" note above.**
|
||||
2. **`sale.order` form smart-button row**: same five, rolled up across all linked jobs. (Also hide-at-zero as of 2026-06-04.)
|
||||
3. **`res.partner` form**: customer-level "Quality History" smart button that opens a kanban filtered to that partner across all 5 record types.
|
||||
4. **One-click cross-creation**:
|
||||
- Hold form → `Open NCR` button — pre-fills NCR with hold's part / customer / quantity / linked job.
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# Sale Order + Job Quality Smart Buttons — hide-at-zero + order-scoped counts
|
||||
|
||||
**Date:** 2026-06-04
|
||||
**Modules:** `fusion_plating_quality`, `fusion_plating_jobs`
|
||||
**Status:** approved (brainstorming sign-off)
|
||||
|
||||
## Problem
|
||||
|
||||
The `sale.order` form shows eight Fusion Plating smart buttons: Receiving, WO,
|
||||
Certificates, Holds, Checks, NCRs, CAPAs, RMAs. On a typical order only three
|
||||
carry data (Receiving / WO / Certificates) yet six of the eight render
|
||||
permanently — five quality buttons (Holds / Checks / NCRs / CAPAs / RMAs) and
|
||||
the WO button all show even at a count of zero. The row reads as noise.
|
||||
|
||||
Two of those counts are also **wrong on an order**:
|
||||
|
||||
- **NCRs** counts the *entire customer's* NCR history (`customer_partner_id ==
|
||||
partner`), not this order's. A repeat customer's brand-new order shows "NCRs 5".
|
||||
- **CAPAs** counts the CAPAs under those customer-wide NCRs — same over-count.
|
||||
|
||||
This contradicts the module's own documented smart-button convention ("don't add
|
||||
a button that always shows zero… NCRs/RMAs visible only when present"). The
|
||||
Sub-12 Phase D plan that introduced these buttons chose "always visible (zero is
|
||||
OK)"; this spec reverts to the convention.
|
||||
|
||||
The identical button row + identical over-count exists on the `fp.job` form and
|
||||
is fixed in the same change.
|
||||
|
||||
## Decision
|
||||
|
||||
1. **Hide every quality button when its count is 0**, on both the `sale.order`
|
||||
and `fp.job` forms. Also hide the **WO** button at 0. (Receiving and
|
||||
Certificates already hide at 0 — untouched.)
|
||||
2. **Re-scope the NCR and CAPA counts to the order/job**, dropping the
|
||||
customer-wide union. Use only the real order-linked paths.
|
||||
|
||||
### Order-scoped NCR set
|
||||
|
||||
There is no direct `ncr.sale_order_id` or `ncr.job_id` field. The only honest
|
||||
paths from an order to its NCRs (confirmed by model inspection) are:
|
||||
|
||||
- **RMA path:** SO → its RMAs (`rma.sale_order_id`) → NCRs (`ncr.rma_id`).
|
||||
- **Hold path:** SO → its jobs (`fp.job.sale_order_id`) → holds (`hold.job_id`)
|
||||
→ the NCR a hold spawned (`hold.ncr_id`).
|
||||
|
||||
NCR set = union of those two. CAPA set = CAPAs whose `ncr_id` is in that set.
|
||||
|
||||
For the `fp.job` form the same definition applies, narrowed to the one job: the
|
||||
job's SO's RMAs' NCRs, plus *this job's* holds' NCRs.
|
||||
|
||||
Trade-off accepted: an NCR raised with neither an RMA nor a hold link (e.g. a QA
|
||||
team logs one directly against the customer) no longer appears on any order or
|
||||
job. That is correct — such an NCR has no order/job linkage to honestly attach
|
||||
it to, and surfacing it on every one of the customer's orders was the bug.
|
||||
|
||||
## Changes
|
||||
|
||||
### `fusion_plating_quality`
|
||||
|
||||
- `models/fp_quality_smart_buttons.py`
|
||||
- Add `sale.order._fp_quality_ncr_ids()` and `fp.job._fp_quality_ncr_ids()`
|
||||
helpers returning the order/job-scoped NCR id list (single source of truth).
|
||||
- Rewrite both `_compute_*` methods to set the NCR count from the helper and
|
||||
the CAPA count from `[('ncr_id', 'in', <helper>)]`. Holds / Checks / RMAs
|
||||
counts are already correctly scoped — leave them.
|
||||
- Point `action_view_fp_ncrs`, `action_view_fp_ncrs_so`, and both
|
||||
`action_view_fp_capas` at the helper-derived ids so each opened list matches
|
||||
its badge.
|
||||
- Narrow `fp.job.action_view_fp_rmas` from SO-OR-partner to SO-only so the RMA
|
||||
list matches its (already SO-scoped) badge count (caught in review — the SO
|
||||
form's RMA button already agreed).
|
||||
- `views/fp_quality_smart_button_views.xml` — add `invisible="<count> == 0"` to
|
||||
the five SO quality buttons.
|
||||
- `__manifest__.py` — version `19.0.8.1.0` → `19.0.8.2.0`.
|
||||
|
||||
### `fusion_plating_jobs`
|
||||
|
||||
- `views/fp_job_quality_buttons.xml` — add `invisible="<count> == 0"` to the
|
||||
five job-form quality buttons.
|
||||
- `views/sale_order_views.xml` — add `invisible="x_fc_fp_job_count == 0"` to the
|
||||
WO smart button; update the now-stale "always visible" comment.
|
||||
- `__manifest__.py` — version `19.0.12.4.0` → `19.0.12.5.0`.
|
||||
|
||||
### Docs
|
||||
|
||||
- `fusion_plating/CLAUDE.md` — short note in the smart-button section recording
|
||||
that the SO/job quality buttons are hide-at-zero with order-scoped NCR/CAPA
|
||||
counts as of 2026-06-04 (reconciling the Sub-12 "always visible" record).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- The `res.partner` "Quality History" button stays as-is. It is customer-wide
|
||||
*by design*, and the user opted not to touch it.
|
||||
- No schema changes, no migration — pure view attributes + non-stored compute
|
||||
logic over existing fields.
|
||||
|
||||
## Verification
|
||||
|
||||
The plating modules cannot be installed on the local Community dev DB
|
||||
(enterprise deps). Verification is:
|
||||
|
||||
1. Static: `pyflakes` on the edited `.py`; `lxml` parse on each edited `.xml`.
|
||||
2. Behavioural (read-only, on an entech clone/shell): for a sample SO and job,
|
||||
confirm the new helper returns only RMA-/hold-linked NCRs and that the counts
|
||||
match, with `env.cr.rollback()` at the end.
|
||||
3. Deploy to entech via the revert-on-failure guard only after the above pass
|
||||
and with the owner's go-ahead (live client box).
|
||||
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
{
|
||||
'name': 'Fusion Plating — Native Jobs',
|
||||
'version': '19.0.12.4.0',
|
||||
'version': '19.0.12.5.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
||||
'author': 'Nexa Systems Inc.',
|
||||
|
||||
@@ -16,25 +16,32 @@
|
||||
<field name="model">fp.job</field>
|
||||
<field name="inherit_id" ref="view_fp_job_form_jobs_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Hidden at a count of 0 so the row shows only quality work
|
||||
that actually exists on this job (2026-06-04). -->
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="action_view_fp_holds" type="object"
|
||||
class="oe_stat_button" icon="fa-hand-paper-o">
|
||||
class="oe_stat_button" icon="fa-hand-paper-o"
|
||||
invisible="fp_qc_hold_count == 0">
|
||||
<field name="fp_qc_hold_count" widget="statinfo" string="Holds"/>
|
||||
</button>
|
||||
<button name="action_view_fp_checks" type="object"
|
||||
class="oe_stat_button" icon="fa-check-square-o">
|
||||
class="oe_stat_button" icon="fa-check-square-o"
|
||||
invisible="fp_qc_check_count == 0">
|
||||
<field name="fp_qc_check_count" widget="statinfo" string="Checks"/>
|
||||
</button>
|
||||
<button name="action_view_fp_ncrs" type="object"
|
||||
class="oe_stat_button" icon="fa-exclamation-triangle">
|
||||
class="oe_stat_button" icon="fa-exclamation-triangle"
|
||||
invisible="fp_qc_ncr_count == 0">
|
||||
<field name="fp_qc_ncr_count" widget="statinfo" string="NCRs"/>
|
||||
</button>
|
||||
<button name="action_view_fp_capas" type="object"
|
||||
class="oe_stat_button" icon="fa-wrench">
|
||||
class="oe_stat_button" icon="fa-wrench"
|
||||
invisible="fp_qc_capa_count == 0">
|
||||
<field name="fp_qc_capa_count" widget="statinfo" string="CAPAs"/>
|
||||
</button>
|
||||
<button name="action_view_fp_rmas" type="object"
|
||||
class="oe_stat_button" icon="fa-undo">
|
||||
class="oe_stat_button" icon="fa-undo"
|
||||
invisible="fp_qc_rma_count == 0">
|
||||
<field name="fp_qc_rma_count" widget="statinfo" string="RMAs"/>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- After the legacy Manufacturing button (added by bridge_mrp).
|
||||
Always visible (no invisible-on-zero) so users can navigate
|
||||
from the SO even before jobs exist. -->
|
||||
Hidden at a count of 0 (2026-06-04) so the SO button row only
|
||||
shows what has data. A confirmed plating SO always has >=1 WO,
|
||||
so this only hides the button on drafts/quotations with no job
|
||||
yet; the header "Work Order" button below still reaches jobs
|
||||
when one is open. -->
|
||||
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
|
||||
<button name="action_view_fp_jobs" type="object"
|
||||
class="oe_stat_button" icon="fa-cogs">
|
||||
class="oe_stat_button" icon="fa-cogs"
|
||||
invisible="x_fc_fp_job_count == 0">
|
||||
<field name="x_fc_fp_job_count" widget="statinfo"
|
||||
string="WO"/>
|
||||
</button>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Quality (QMS)',
|
||||
'version': '19.0.8.1.0',
|
||||
'version': '19.0.8.2.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Native QMS for plating shops: NCR, CAPA, calibration, AVL, FAIR, '
|
||||
'internal audits, customer specs, document control. CE + EE compatible.',
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
# Sub 12 Phase D — smart-button counts on fp.job, sale.order, res.partner.
|
||||
#
|
||||
# Each parent record gets badge counts for: Holds, Checks, NCRs, CAPAs,
|
||||
# RMAs. Counts always render (zero is acceptable). Action methods open
|
||||
# the relevant kanban filtered to that record.
|
||||
# RMAs. The buttons hide at a count of 0 (set in the view XML), so the row
|
||||
# only shows quality work that actually exists. NCR/CAPA counts are scoped
|
||||
# to the order/job via RMAs + holds (see _fp_quality_ncr_ids) — NOT the
|
||||
# customer's whole NCR history. Action methods open the matching list.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
@@ -35,7 +37,6 @@ class FpJobQualitySmart(models.Model):
|
||||
def _compute_fp_quality_counts(self):
|
||||
Hold = self.env['fusion.plating.quality.hold']
|
||||
Check = self.env['fusion.plating.quality.check']
|
||||
Ncr = self.env['fusion.plating.ncr']
|
||||
Capa = self.env['fusion.plating.capa']
|
||||
Rma = self.env['fusion.plating.rma']
|
||||
for job in self:
|
||||
@@ -43,23 +44,39 @@ class FpJobQualitySmart(models.Model):
|
||||
[('job_id', '=', job.id)])
|
||||
job.fp_qc_check_count = Check.search_count(
|
||||
[('job_id', '=', job.id)])
|
||||
ncr_ids = []
|
||||
capa_ids = []
|
||||
rma_ids = []
|
||||
if job.sale_order_id:
|
||||
rma_ids = Rma.search(
|
||||
[('sale_order_id', '=', job.sale_order_id.id)]).ids
|
||||
if rma_ids:
|
||||
ncr_ids = Ncr.search([('rma_id', 'in', rma_ids)]).ids
|
||||
if job.partner_id:
|
||||
ncr_ids = list(set(ncr_ids + Ncr.search([
|
||||
('customer_partner_id', '=', job.partner_id.id),
|
||||
]).ids))
|
||||
if ncr_ids:
|
||||
capa_ids = Capa.search([('ncr_id', 'in', ncr_ids)]).ids
|
||||
job.fp_qc_rma_count = Rma.search_count(
|
||||
[('sale_order_id', '=', job.sale_order_id.id)]
|
||||
) if job.sale_order_id else 0
|
||||
ncr_ids = job._fp_quality_ncr_ids()
|
||||
job.fp_qc_ncr_count = len(ncr_ids)
|
||||
job.fp_qc_capa_count = len(capa_ids)
|
||||
job.fp_qc_rma_count = len(rma_ids)
|
||||
job.fp_qc_capa_count = Capa.search_count(
|
||||
[('ncr_id', 'in', ncr_ids)]) if ncr_ids else 0
|
||||
|
||||
def _fp_quality_ncr_ids(self):
|
||||
"""Job-scoped NCR ids — single source of truth for both the badge
|
||||
count and the smart-button list.
|
||||
|
||||
There is no ncr.job_id / ncr.sale_order_id field, so the only honest
|
||||
links are:
|
||||
- NCRs reached via this job's order's RMAs (ncr.rma_id), and
|
||||
- NCRs spawned from this job's holds (hold.ncr_id).
|
||||
An NCR with neither link is customer-level history with no tie to a
|
||||
single job, so it is intentionally excluded.
|
||||
"""
|
||||
self.ensure_one()
|
||||
Ncr = self.env['fusion.plating.ncr']
|
||||
Hold = self.env['fusion.plating.quality.hold']
|
||||
Rma = self.env['fusion.plating.rma']
|
||||
ncr_ids = set()
|
||||
if self.sale_order_id:
|
||||
rma_ids = Rma.search(
|
||||
[('sale_order_id', '=', self.sale_order_id.id)]).ids
|
||||
if rma_ids:
|
||||
ncr_ids.update(Ncr.search([('rma_id', 'in', rma_ids)]).ids)
|
||||
ncr_ids.update(Hold.search([
|
||||
('job_id', '=', self.id), ('ncr_id', '!=', False),
|
||||
]).mapped('ncr_id').ids)
|
||||
return list(ncr_ids)
|
||||
|
||||
def action_view_fp_holds(self):
|
||||
self.ensure_one()
|
||||
@@ -85,43 +102,39 @@ class FpJobQualitySmart(models.Model):
|
||||
|
||||
def action_view_fp_ncrs(self):
|
||||
self.ensure_one()
|
||||
domain = [('customer_partner_id', '=', self.partner_id.id)]
|
||||
return {
|
||||
'name': _('NCRs'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fusion.plating.ncr',
|
||||
'view_mode': 'kanban,list,form',
|
||||
'domain': domain,
|
||||
'domain': [('id', 'in', self._fp_quality_ncr_ids())],
|
||||
'context': {'default_customer_partner_id': self.partner_id.id},
|
||||
}
|
||||
|
||||
def action_view_fp_capas(self):
|
||||
self.ensure_one()
|
||||
Ncr = self.env['fusion.plating.ncr']
|
||||
ncr_ids = Ncr.search([
|
||||
('customer_partner_id', '=', self.partner_id.id),
|
||||
]).ids
|
||||
return {
|
||||
'name': _('CAPAs'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fusion.plating.capa',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('ncr_id', 'in', ncr_ids)],
|
||||
'domain': [('ncr_id', 'in', self._fp_quality_ncr_ids())],
|
||||
}
|
||||
|
||||
def action_view_fp_rmas(self):
|
||||
self.ensure_one()
|
||||
domain = [('partner_id', '=', self.partner_id.id)]
|
||||
if self.sale_order_id:
|
||||
domain = ['|', ('sale_order_id', '=', self.sale_order_id.id),
|
||||
('partner_id', '=', self.partner_id.id)]
|
||||
# SO-scoped to match the badge count. RMA.sale_order_id is required,
|
||||
# so a job with no order matches nothing — same as the count's 0.
|
||||
return {
|
||||
'name': _('RMAs'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fusion.plating.rma',
|
||||
'view_mode': 'kanban,list,form',
|
||||
'domain': domain,
|
||||
'context': {'default_partner_id': self.partner_id.id},
|
||||
'domain': [('sale_order_id', '=', self.sale_order_id.id)],
|
||||
'context': {
|
||||
'default_partner_id': self.partner_id.id,
|
||||
'default_sale_order_id': self.sale_order_id.id,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +161,6 @@ class SaleOrderQualitySmart(models.Model):
|
||||
def _compute_fp_qc_counts(self):
|
||||
Hold = self.env['fusion.plating.quality.hold']
|
||||
Check = self.env['fusion.plating.quality.check']
|
||||
Ncr = self.env['fusion.plating.ncr']
|
||||
Capa = self.env['fusion.plating.capa']
|
||||
Rma = self.env['fusion.plating.rma']
|
||||
Job = self.env['fp.job']
|
||||
@@ -158,19 +170,40 @@ class SaleOrderQualitySmart(models.Model):
|
||||
[('job_id', 'in', job_ids)]) if job_ids else 0
|
||||
so.fp_qc_check_count = Check.search_count(
|
||||
[('job_id', 'in', job_ids)]) if job_ids else 0
|
||||
rma_ids = Rma.search([('sale_order_id', '=', so.id)]).ids
|
||||
so.fp_qc_rma_count = len(rma_ids)
|
||||
ncr_ids = []
|
||||
if rma_ids:
|
||||
ncr_ids = Ncr.search([('rma_id', 'in', rma_ids)]).ids
|
||||
if so.partner_id:
|
||||
ncr_ids = list(set(ncr_ids + Ncr.search([
|
||||
('customer_partner_id', '=', so.partner_id.id),
|
||||
]).ids))
|
||||
so.fp_qc_rma_count = Rma.search_count(
|
||||
[('sale_order_id', '=', so.id)])
|
||||
ncr_ids = so._fp_quality_ncr_ids()
|
||||
so.fp_qc_ncr_count_so = len(ncr_ids)
|
||||
so.fp_qc_capa_count = Capa.search_count(
|
||||
[('ncr_id', 'in', ncr_ids)]) if ncr_ids else 0
|
||||
|
||||
def _fp_quality_ncr_ids(self):
|
||||
"""Order-scoped NCR ids — single source of truth for both the badge
|
||||
count and the smart-button list.
|
||||
|
||||
There is no ncr.sale_order_id / ncr.job_id field, so the only honest
|
||||
links are:
|
||||
- NCRs reached via this order's RMAs (ncr.rma_id), and
|
||||
- NCRs spawned from holds on this order's jobs (hold.ncr_id).
|
||||
An NCR with neither link is customer-level history with no tie to a
|
||||
single order, so it is intentionally excluded.
|
||||
"""
|
||||
self.ensure_one()
|
||||
Ncr = self.env['fusion.plating.ncr']
|
||||
Hold = self.env['fusion.plating.quality.hold']
|
||||
Rma = self.env['fusion.plating.rma']
|
||||
Job = self.env['fp.job']
|
||||
ncr_ids = set()
|
||||
rma_ids = Rma.search([('sale_order_id', '=', self.id)]).ids
|
||||
if rma_ids:
|
||||
ncr_ids.update(Ncr.search([('rma_id', 'in', rma_ids)]).ids)
|
||||
job_ids = Job.search([('sale_order_id', '=', self.id)]).ids
|
||||
if job_ids:
|
||||
ncr_ids.update(Hold.search([
|
||||
('job_id', 'in', job_ids), ('ncr_id', '!=', False),
|
||||
]).mapped('ncr_id').ids)
|
||||
return list(ncr_ids)
|
||||
|
||||
def action_view_fp_holds(self):
|
||||
self.ensure_one()
|
||||
Job = self.env['fp.job']
|
||||
@@ -202,21 +235,17 @@ class SaleOrderQualitySmart(models.Model):
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fusion.plating.ncr',
|
||||
'view_mode': 'kanban,list,form',
|
||||
'domain': [('customer_partner_id', '=', self.partner_id.id)],
|
||||
'domain': [('id', 'in', self._fp_quality_ncr_ids())],
|
||||
}
|
||||
|
||||
def action_view_fp_capas(self):
|
||||
self.ensure_one()
|
||||
Ncr = self.env['fusion.plating.ncr']
|
||||
ncr_ids = Ncr.search([
|
||||
('customer_partner_id', '=', self.partner_id.id),
|
||||
]).ids
|
||||
return {
|
||||
'name': _('CAPAs'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'fusion.plating.capa',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('ncr_id', 'in', ncr_ids)],
|
||||
'domain': [('ncr_id', 'in', self._fp_quality_ncr_ids())],
|
||||
}
|
||||
|
||||
def action_view_fp_rmas(self):
|
||||
|
||||
@@ -25,24 +25,31 @@
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<!-- Hidden at a count of 0 so the row shows only quality
|
||||
work that actually exists on this order (2026-06-04). -->
|
||||
<button name="action_view_fp_holds" type="object"
|
||||
class="oe_stat_button" icon="fa-hand-paper-o">
|
||||
class="oe_stat_button" icon="fa-hand-paper-o"
|
||||
invisible="fp_qc_hold_count == 0">
|
||||
<field name="fp_qc_hold_count" widget="statinfo" string="Holds"/>
|
||||
</button>
|
||||
<button name="action_view_fp_checks" type="object"
|
||||
class="oe_stat_button" icon="fa-check-square-o">
|
||||
class="oe_stat_button" icon="fa-check-square-o"
|
||||
invisible="fp_qc_check_count == 0">
|
||||
<field name="fp_qc_check_count" widget="statinfo" string="Checks"/>
|
||||
</button>
|
||||
<button name="action_view_fp_ncrs_so" type="object"
|
||||
class="oe_stat_button" icon="fa-exclamation-triangle">
|
||||
class="oe_stat_button" icon="fa-exclamation-triangle"
|
||||
invisible="fp_qc_ncr_count_so == 0">
|
||||
<field name="fp_qc_ncr_count_so" widget="statinfo" string="NCRs"/>
|
||||
</button>
|
||||
<button name="action_view_fp_capas" type="object"
|
||||
class="oe_stat_button" icon="fa-wrench">
|
||||
class="oe_stat_button" icon="fa-wrench"
|
||||
invisible="fp_qc_capa_count == 0">
|
||||
<field name="fp_qc_capa_count" widget="statinfo" string="CAPAs"/>
|
||||
</button>
|
||||
<button name="action_view_fp_rmas" type="object"
|
||||
class="oe_stat_button" icon="fa-undo">
|
||||
class="oe_stat_button" icon="fa-undo"
|
||||
invisible="fp_qc_rma_count == 0">
|
||||
<field name="fp_qc_rma_count" widget="statinfo" string="RMAs"/>
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user