diff --git a/fusion_plating/fusion_plating_quality/__manifest__.py b/fusion_plating/fusion_plating_quality/__manifest__.py index 81c42446..7b52d161 100644 --- a/fusion_plating/fusion_plating_quality/__manifest__.py +++ b/fusion_plating/fusion_plating_quality/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating — Quality (QMS)', - 'version': '19.0.7.0.0', + 'version': '19.0.8.0.0', 'category': 'Manufacturing/Plating', 'summary': 'Native QMS for plating shops: NCR, CAPA, calibration, AVL, FAIR, ' 'internal audits, customer specs, document control. CE + EE compatible.', diff --git a/fusion_plating/fusion_plating_quality/scripts/bt_quality_dashboard_redesign.py b/fusion_plating/fusion_plating_quality/scripts/bt_quality_dashboard_redesign.py new file mode 100644 index 00000000..27f2e48e --- /dev/null +++ b/fusion_plating/fusion_plating_quality/scripts/bt_quality_dashboard_redesign.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +"""Quality Dashboard redesign — entech smoke. + +Spec: docs/superpowers/specs/2026-05-25-quality-dashboard-redesign-design.md +Plan: docs/superpowers/plans/2026-05-25-quality-dashboard-redesign-plan.md + +Run on entech via odoo-shell: + ssh pve-worker5 "pct exec 111 -- bash -c 'echo \\\" + exec(open(\\\\\\\"/mnt/extra-addons/custom/fusion_plating_quality/scripts/bt_quality_dashboard_redesign.py\\\\\\\").read()) + \\\" | su - odoo -s /bin/bash -c \\\"/usr/bin/odoo shell -c /etc/odoo/odoo.conf -d admin --no-http\\\"'" + +Validates: + 1. FpQualityDashboardSnapshot helper class exists and builds + 2. Response shape (banner + sections + computed_at) + 3. Section order is canonical (cert, hold, ncr, rma, capa, check) + 4. Each section has all required keys + 5. Each section's open_kanban_xmlid resolves to a real act_window + 6. Banner items shape includes open_action that resolves to a model +""" +from odoo.addons.fusion_plating_quality.controllers.fp_quality_dashboard \ + import FpQualityDashboardSnapshot, SECTION_ORDER + + +def _ok(cond, label): + if cond: + print('OK -', label) + else: + print('FAIL -', label) + raise SystemExit(1) + + +# ---- 1. Build snapshot ---- +snap = FpQualityDashboardSnapshot(env).build() +_ok(isinstance(snap, dict), 'snapshot is a dict') + +# ---- 2. Response shape ---- +for k in ('banner', 'sections', 'computed_at'): + _ok(k in snap, f'snapshot has key {k!r}') +_ok(isinstance(snap['sections'], list), 'sections is a list') +_ok(isinstance(snap['banner'], dict), 'banner is a dict') + +# ---- 3. Section order ---- +types = [s['type'] for s in snap['sections']] +canonical_present = [t for t in SECTION_ORDER if t in types] +_ok(types == canonical_present, + f'section order canonical: got {types}, expected subset of {SECTION_ORDER}') + +# ---- 4. Section keys ---- +for sec in snap['sections']: + for k in ('type', 'label', 'icon', 'open', 'overdue', 'items', + 'open_kanban_xmlid'): + _ok(k in sec, f'section {sec["type"]} has key {k!r}') + +# ---- 5. open_kanban_xmlid resolves ---- +for sec in snap['sections']: + try: + act = env.ref(sec['open_kanban_xmlid'], raise_if_not_found=False) + _ok(bool(act), f'section {sec["type"]} kanban xmlid resolves') + except Exception as e: + _ok(False, f'section {sec["type"]} xmlid: {e}') + +# ---- 6. Banner items shape ---- +print(f' banner.all_clear = {snap["banner"]["all_clear"]}') +print(f' banner.items = {len(snap["banner"]["items"])}') +print(f' banner.total_matching = {snap["banner"]["total_matching"]}') +if snap['banner']['items']: + item = snap['banner']['items'][0] + for k in ('type', 'id', 'name', 'customer', 'subtitle', + 'urgency', 'critical_badge', 'open_action'): + _ok(k in item, f'banner item has key {k!r}') + # open_action.res_model resolves to a model + _ok(item['open_action']['res_model'] in env, + f'banner item res_model {item["open_action"]["res_model"]!r} is installed') + +# ---- Summary ---- +print() +print('--- bt_quality_dashboard_redesign: ALL PASS ---') +print(f' Sections present: {len(snap["sections"])}') +for sec in snap['sections']: + print(f' {sec["icon"]} {sec["label"]}: {sec["open"]} open ' + f'({sec["overdue"]} overdue), top-{len(sec["items"])} listed') +print(f' Banner: {len(snap["banner"]["items"])} items ' + f'(of {snap["banner"]["total_matching"]} matching), ' + f'all_clear={snap["banner"]["all_clear"]}')