changes
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Certificates',
|
||||
'version': '19.0.7.0.0',
|
||||
'version': '19.0.7.7.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Certificate registry for CoC, thickness reports, and quality documents.',
|
||||
'description': """
|
||||
|
||||
@@ -106,6 +106,81 @@ class FpCertificate(models.Model):
|
||||
string='Fischerscope PDF filename',
|
||||
)
|
||||
|
||||
# Non-PDF Fischerscope uploads (.doc / .docx / .xlsx / images) — the
|
||||
# Issue Certs wizard stashes them here so the thickness-required gate
|
||||
# can still pass. Unlike `x_fc_local_thickness_pdf`, this attachment
|
||||
# is NOT merged into the CoC PDF as page 2 (we can't rasterize .doc
|
||||
# server-side without LibreOffice). It rides along as a separate
|
||||
# evidence attachment on the cert and on any email/portal delivery.
|
||||
x_fc_local_thickness_evidence_id = fields.Many2one(
|
||||
'ir.attachment',
|
||||
string='Fischerscope Evidence (non-PDF)',
|
||||
copy=False,
|
||||
help='Original Fischerscope/XRF upload when not a PDF. Counts '
|
||||
'as valid thickness evidence for the cert-issue gate but '
|
||||
'is delivered as a separate attachment, not merged into '
|
||||
'the CoC PDF.',
|
||||
)
|
||||
|
||||
# Report-level Fischerscope metadata — populated by the Issue Certs
|
||||
# wizard when parsing an RTF/.docx upload. Rendered on the CoC so
|
||||
# the printed cert shows the same context an auditor would see on
|
||||
# the original XDAL 600 export (equipment, operator, calibration,
|
||||
# product/application, measuring time, date/time). Per-reading
|
||||
# values (mils, Ni%, P%) live on fp.thickness.reading.
|
||||
x_fc_thickness_equipment = fields.Char(
|
||||
string='Thickness Equipment',
|
||||
help='XRF/thickness gauge model (e.g. "Fischerscope XDAL 600").',
|
||||
)
|
||||
x_fc_thickness_operator = fields.Char(
|
||||
string='Thickness Operator',
|
||||
help='Operator initials/name as recorded by the gauge.',
|
||||
)
|
||||
x_fc_thickness_datetime = fields.Datetime(
|
||||
string='Thickness Reading Date/Time',
|
||||
help='When the readings were taken on the gauge.',
|
||||
)
|
||||
x_fc_thickness_product = fields.Char(
|
||||
string='Thickness Product Profile',
|
||||
help='XDAL 600 product line + part-family reference '
|
||||
'(e.g. "2805031 / NiP/Al-alloys 2805030").',
|
||||
)
|
||||
x_fc_thickness_application = fields.Char(
|
||||
string='Thickness Application',
|
||||
help='XDAL 600 application profile '
|
||||
'(e.g. "16 / NiP/Al-alloys").',
|
||||
)
|
||||
x_fc_thickness_directory = fields.Char(
|
||||
string='Thickness Directory',
|
||||
help='XDAL 600 directory the measurements were saved into.',
|
||||
)
|
||||
x_fc_thickness_measuring_time_sec = fields.Integer(
|
||||
string='Thickness Measuring Time (sec)',
|
||||
help='Per-reading measuring time configured on the gauge.',
|
||||
)
|
||||
x_fc_thickness_source_filename = fields.Char(
|
||||
string='Thickness Source File',
|
||||
help='Filename of the Fischerscope upload the readings were '
|
||||
'parsed from.',
|
||||
)
|
||||
# Two paths populate this field, with operator upload winning:
|
||||
# 1. RTF auto-extraction — Issue Certs wizard runs libwmf
|
||||
# (wmf2svg) on the embedded WMF blocks and picks the
|
||||
# largest raster (header banners filtered by area threshold).
|
||||
# 2. Manual PNG/JPEG upload via the wizard's "Measurement
|
||||
# Image" field — operator override path when the
|
||||
# auto-extracted image is wrong, missing, or low-quality.
|
||||
# See _apply_to_cert and _apply_image_to_cert in the wizard.
|
||||
x_fc_thickness_image_id = fields.Many2one(
|
||||
'ir.attachment',
|
||||
string='Thickness Microscope Image',
|
||||
copy=False,
|
||||
help='Microscope photo of the measurement site. Auto-extracted '
|
||||
'from the Fischerscope RTF export when libwmf can parse '
|
||||
'the embedded WMF; operator can also upload a PNG/JPEG '
|
||||
'directly via the Issue Certs wizard to override.',
|
||||
)
|
||||
|
||||
# ---- Material traceability (T2.3) ----
|
||||
batch_ids = fields.Many2many(
|
||||
'fusion.plating.batch', compute='_compute_batch_ids',
|
||||
@@ -456,7 +531,11 @@ class FpCertificate(models.Model):
|
||||
if 'x_fc_thickness_pdf_id' in rec._fields else False
|
||||
)
|
||||
has_local_pdf = bool(rec.x_fc_local_thickness_pdf)
|
||||
if not (has_readings or has_qc_fischer_pdf or has_local_pdf):
|
||||
has_local_evidence = bool(
|
||||
rec.x_fc_local_thickness_evidence_id
|
||||
)
|
||||
if not (has_readings or has_qc_fischer_pdf
|
||||
or has_local_pdf or has_local_evidence):
|
||||
type_label = (
|
||||
_('Thickness Report')
|
||||
if rec.certificate_type == 'thickness_report'
|
||||
@@ -685,6 +764,32 @@ class FpCertificate(models.Model):
|
||||
) % source)
|
||||
return merged
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
"""Move an issued/voided cert back to draft so the manager can
|
||||
correct typos in the thickness metadata, swap the microscope
|
||||
image, re-pick the void reason, etc. — then re-Issue.
|
||||
|
||||
Wipes the existing `attachment_id` so the next render picks up
|
||||
whatever was changed. The original PDF stays around as a
|
||||
regular ir.attachment on the cert (for audit) since we only
|
||||
clear the FK, not the attachment record itself. Re-issue
|
||||
creates a fresh PDF.
|
||||
"""
|
||||
for rec in self:
|
||||
if rec.state == 'draft':
|
||||
raise UserError(_(
|
||||
'Certificate %s is already a draft.'
|
||||
) % rec.name)
|
||||
rec.state = 'draft'
|
||||
old_att = rec.attachment_id
|
||||
if old_att:
|
||||
rec.attachment_id = False
|
||||
rec.message_post(body=_(
|
||||
'Reset to draft for edits. The previously-issued PDF '
|
||||
'%s remains attached for audit; a fresh PDF will be '
|
||||
'generated on re-issue.'
|
||||
) % (old_att.name if old_att else '(none)'))
|
||||
|
||||
def action_void(self):
|
||||
for rec in self:
|
||||
if rec.state != 'issued':
|
||||
|
||||
@@ -42,12 +42,27 @@
|
||||
<button name="action_issue" string="Issue"
|
||||
type="object" class="btn-primary"
|
||||
invisible="state != 'draft'"/>
|
||||
<!-- Print = the same EN report action the gear-menu
|
||||
Print > Certificate of Conformance (English)
|
||||
calls. Routes through fusion_pdf_preview's
|
||||
report interceptor automatically. For the
|
||||
French variant or any other language report,
|
||||
use the gear menu. -->
|
||||
<button name="%(fusion_plating_reports.action_report_coc_en)d"
|
||||
string="Print"
|
||||
type="action" class="btn-secondary"
|
||||
icon="fa-print"/>
|
||||
<button name="action_open_void_wizard" string="Void"
|
||||
type="object" class="btn-danger"
|
||||
invisible="state != 'issued'"/>
|
||||
<button name="action_send_to_customer" string="Send to Customer"
|
||||
type="object"
|
||||
invisible="state != 'issued'"/>
|
||||
<button name="action_reset_to_draft" string="Reset to Draft"
|
||||
type="object" class="btn-secondary"
|
||||
icon="fa-undo"
|
||||
confirm="Reset this certificate to draft? You'll be able to edit and re-issue. The previously-issued PDF stays attached for audit."
|
||||
invisible="state == 'draft'"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,issued"/>
|
||||
</header>
|
||||
@@ -67,48 +82,52 @@
|
||||
<field name="name" readonly="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<!-- Main info — collapsed from 3 separate groups
|
||||
into 1 to eliminate the dead rows that
|
||||
appeared when one sub-group ran shorter than
|
||||
the other. Left column is identity / signer /
|
||||
dates; right column is part / process / qty /
|
||||
derived stats. Reorganized 2026-05-21. -->
|
||||
<group>
|
||||
<group>
|
||||
<field name="certificate_type"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="sale_order_id"/>
|
||||
<field name="portal_job_id"/>
|
||||
<field name="issue_date"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="part_number"/>
|
||||
<field name="po_number"/>
|
||||
<field name="entech_wo_number"/>
|
||||
<field name="customer_job_no"/>
|
||||
<field name="process_description"/>
|
||||
<field name="spec_reference"/>
|
||||
<field name="quantity_shipped"/>
|
||||
<field name="nc_quantity"/>
|
||||
<field name="contact_partner_id"
|
||||
options="{'no_create': True}"
|
||||
invisible="not partner_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="sale_order_id"/>
|
||||
<field name="entech_wo_number"/>
|
||||
<field name="portal_job_id"/>
|
||||
<field name="issue_date"/>
|
||||
<field name="issued_by_id"/>
|
||||
<field name="certified_by_id"/>
|
||||
<field name="body_style"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="part_number"/>
|
||||
<field name="process_description"/>
|
||||
<field name="spec_reference"/>
|
||||
<field name="po_number"/>
|
||||
<field name="customer_job_no"/>
|
||||
<field name="quantity_shipped"/>
|
||||
<field name="nc_quantity"/>
|
||||
<field name="reading_count" readonly="1"/>
|
||||
<field name="mean_nip_mils" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<!-- SPC rebalanced — spec/min/max on the left,
|
||||
derived stats on the right; trend_explanation
|
||||
spans both columns so the long message doesn't
|
||||
get cropped. -->
|
||||
<group string="SPC — Statistical Process Control">
|
||||
<group>
|
||||
<field name="spec_min_mils"/>
|
||||
<field name="spec_max_mils"/>
|
||||
<field name="min_reading_mils" readonly="1"/>
|
||||
<field name="max_reading_mils" readonly="1"/>
|
||||
<field name="std_dev_mils" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="std_dev_mils" readonly="1"/>
|
||||
<field name="cpk" readonly="1"/>
|
||||
<field name="cpk_status" readonly="1" widget="badge"
|
||||
decoration-success="cpk_status in ('capable','excellent')"
|
||||
@@ -119,9 +138,9 @@
|
||||
decoration-success="trend_alert == 'ok'"
|
||||
decoration-warning="trend_alert == 'warning'"
|
||||
decoration-danger="trend_alert == 'alert'"/>
|
||||
<field name="trend_explanation" readonly="1"
|
||||
invisible="trend_alert == 'ok'"/>
|
||||
</group>
|
||||
<field name="trend_explanation" readonly="1" colspan="2"
|
||||
invisible="trend_alert == 'ok'"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Thickness Readings" name="readings">
|
||||
|
||||
Reference in New Issue
Block a user