fix(fusion_plating): bug review fixes + progress/net-terms invoicing + formal CoC rebuild
Bug review fixes (found by code review + live QWeb error):
- report_fp_sale.xml: product_uom → product_uom_id (Odoo 19 renamed;
was raising KeyError during PDF render, blocking all sale-order prints)
- mrp_production.button_mark_done: add idempotency guard on delivery
auto-create (was duplicating on every re-close)
- fp.certificate._compute_batch_ids: use empty recordset instead of
False for Many2many computed fields
- fp_notification_template._collect_attachments: collapse attach_quotation
+ attach_sale_order into a single render so email doesn't double-attach
the same PDF
- fp.operator.certification: SQL unique on computed state was unreliable;
added explicit `revoked` boolean, made state pure-compute, replaced
SQL constraint with @api.constrains that checks active-only uniqueness;
has_active_cert now reads revoked + expires_date directly (no stale
stored state between nightly recomputes)
Two missing invoice strategies implemented + 1 pre-existing deposit bug fix:
- Progress Billing: new x_fc_progress_initial_percent field on sale.order;
_create_progress_initial_invoice bills the configured % on SO confirm
via down-payment wizard, _create_final_balance_invoice bills the
remainder on delivery
- Net Terms: no invoice on confirm; full invoice auto-created when
fusion.plating.delivery.action_mark_delivered fires
- Fix for deposit (pre-existing, silent): sale.advance.payment.inv
reads active_ids at wizard-create time, not on create_invoices();
context was being set on the wrong call, so every deposit attempt
raised "Expected singleton" and message-posted to chatter instead
of actually invoicing
- New fusion_plating_invoicing/models/fp_delivery.py hooks
action_mark_delivered to dispatch final invoice for progress/net_terms
- fp.direct.order.wizard + SO form surface the progress_initial_percent
field (conditional on strategy)
Report styling cleanup:
- Hide DISCOUNT column from sale + invoice landscape reports unless at
least one line has a non-zero discount; colspan auto-adjusts
- Replace hardcoded #0066a1 in all reports with company.primary_color
driven by doc.company_id → company → user.company_id fallback chain,
with #1d1f1e as ultimate fallback; new .fp-header-primary class
exposes the colour for inline section headers (CARGO DESCRIPTION,
PAYMENT DETAILS, OPERATOR SIGN-OFF, etc.) so they retint with the
company theme without template edits
Certificate of Conformance — formal ENTECH-style rebuild:
- New res.company fields: x_fc_owner_user_id (default signer, sig from
hr.employee.signature), x_fc_coc_signature_override (manual upload),
x_fc_{nadcap,as9100,cgp}_logo + _active toggles for accreditation
badges
- New res.config.settings section "Fusion Plating" exposing the above
as configurable blocks; manager-only menu under Configuration →
Fusion Plating Settings
- New fp.certificate fields: nc_quantity, customer_job_no,
contact_partner_id (child contact for Name / Email / Phone block)
- New report_coc_en + report_coc_fr templates (primary): custom header
(company contact | accreditations | company logo), bilingual labels
per variant, customer info block with customer logo, 3-column cert
info table, 6-column line-item table (Part # | Process | Customer
PO | Shipped | NC Qty | Customer Job No.), signature image + bordered
certification statement, footer "Fusion Plating by Nexa Systems"
- Legacy report_coc + report_coc_portrait kept for existing portal-job
bindings (no behaviour change)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ Includes Fischerscope thickness measurement data capture.
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/fp_certificate_sequence_data.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/fp_certificate_views.xml',
|
||||
'views/fp_certificates_menu.xml',
|
||||
],
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
|
||||
from . import fp_thickness_reading
|
||||
from . import fp_certificate
|
||||
from . import res_config_settings
|
||||
|
||||
@@ -45,6 +45,20 @@ class FpCertificate(models.Model):
|
||||
po_number = fields.Char(string='Customer PO #')
|
||||
entech_wo_number = fields.Char(string='Entech WO #')
|
||||
quantity_shipped = fields.Integer(string='Qty Shipped')
|
||||
nc_quantity = fields.Integer(
|
||||
string='NC Qty',
|
||||
help='Non-conforming quantity — parts that failed inspection / rework.',
|
||||
)
|
||||
customer_job_no = fields.Char(
|
||||
string='Customer Job No.',
|
||||
help="Customer's internal job / traveler reference.",
|
||||
)
|
||||
contact_partner_id = fields.Many2one(
|
||||
'res.partner', string='Customer Contact',
|
||||
domain="[('parent_id', '=', partner_id)]",
|
||||
help="Specific contact person at the customer for this certificate. "
|
||||
'Their name, email, and phone are printed on the CoC.',
|
||||
)
|
||||
issued_by_id = fields.Many2one(
|
||||
'res.users', string='Issued By', default=lambda self: self.env.user,
|
||||
)
|
||||
@@ -73,6 +87,8 @@ class FpCertificate(models.Model):
|
||||
@api.depends('production_id')
|
||||
def _compute_batch_ids(self):
|
||||
Batch = self.env.get('fusion.plating.batch')
|
||||
Bath = self.env['fusion.plating.bath']
|
||||
empty_batch = self.env['fusion.plating.batch']
|
||||
for rec in self:
|
||||
if Batch is not None and rec.production_id:
|
||||
batches = Batch.search([
|
||||
@@ -82,9 +98,9 @@ class FpCertificate(models.Model):
|
||||
rec.batch_count = len(batches)
|
||||
rec.bath_ids = batches.mapped('bath_id')
|
||||
else:
|
||||
rec.batch_ids = False
|
||||
rec.batch_ids = empty_batch
|
||||
rec.batch_count = 0
|
||||
rec.bath_ids = False
|
||||
rec.bath_ids = Bath
|
||||
state = fields.Selection(
|
||||
[('draft', 'Draft'), ('issued', 'Issued'), ('voided', 'Voided')],
|
||||
string='Status', default='draft', tracking=True, required=True,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
"""Expose Fusion Plating CoC settings on the standard Settings page
|
||||
so they show up under a Fusion Plating section that the owner can
|
||||
edit like any other Odoo settings."""
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
x_fc_owner_user_id = fields.Many2one(
|
||||
related='company_id.x_fc_owner_user_id', readonly=False,
|
||||
)
|
||||
x_fc_coc_signature_override = fields.Binary(
|
||||
related='company_id.x_fc_coc_signature_override', readonly=False,
|
||||
)
|
||||
x_fc_nadcap_logo = fields.Binary(
|
||||
related='company_id.x_fc_nadcap_logo', readonly=False,
|
||||
)
|
||||
x_fc_nadcap_active = fields.Boolean(
|
||||
related='company_id.x_fc_nadcap_active', readonly=False,
|
||||
)
|
||||
x_fc_as9100_logo = fields.Binary(
|
||||
related='company_id.x_fc_as9100_logo', readonly=False,
|
||||
)
|
||||
x_fc_as9100_active = fields.Boolean(
|
||||
related='company_id.x_fc_as9100_active', readonly=False,
|
||||
)
|
||||
x_fc_cgp_logo = fields.Binary(
|
||||
related='company_id.x_fc_cgp_logo', readonly=False,
|
||||
)
|
||||
x_fc_cgp_active = fields.Boolean(
|
||||
related='company_id.x_fc_cgp_active', readonly=False,
|
||||
)
|
||||
@@ -80,9 +80,14 @@
|
||||
<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>
|
||||
|
||||
@@ -41,4 +41,12 @@
|
||||
action="action_fp_certificate_thickness"
|
||||
sequence="30"/>
|
||||
|
||||
<!-- Settings menu under Configuration, manager-only -->
|
||||
<menuitem id="menu_fp_settings"
|
||||
name="Fusion Plating Settings"
|
||||
parent="fusion_plating.menu_fp_config"
|
||||
action="action_fp_settings"
|
||||
sequence="1"
|
||||
groups="fusion_plating.group_fusion_plating_manager"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="res_config_settings_view_form_fp" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.fusion.plating</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<app data-string="Fusion Plating" string="Fusion Plating"
|
||||
name="fusion_plating" groups="fusion_plating.group_fusion_plating_manager">
|
||||
<block title="Certificate of Conformance"
|
||||
name="fp_coc_settings"
|
||||
help="Branding, accreditation logos, and default signer for Certificates of Conformance.">
|
||||
<setting id="fp_coc_owner"
|
||||
string="Certificate Owner (Default Signer)"
|
||||
help="Their HR Employee signature appears on issued certificates by default.">
|
||||
<field name="x_fc_owner_user_id"
|
||||
options="{'no_create': True, 'no_open': True}"/>
|
||||
</setting>
|
||||
<setting id="fp_coc_sig_override"
|
||||
string="Signature Override Image"
|
||||
help="Upload a scanned signature here to override the owner user's employee signature (useful if they don't have an HR record).">
|
||||
<field name="x_fc_coc_signature_override"
|
||||
widget="image" class="oe_avatar"/>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<block title="Accreditation Logos"
|
||||
name="fp_accreditation_logos"
|
||||
help="Upload the logos and toggle each on to display it in the CoC header. Sized automatically in the PDF.">
|
||||
<setting id="fp_nadcap"
|
||||
string="Nadcap Accredited"
|
||||
help="Administered by PRI. Upload the official Nadcap Accredited logo.">
|
||||
<field name="x_fc_nadcap_active"/>
|
||||
<field name="x_fc_nadcap_logo"
|
||||
widget="image" class="oe_avatar"
|
||||
invisible="not x_fc_nadcap_active"/>
|
||||
</setting>
|
||||
<setting id="fp_as9100"
|
||||
string="AS9100 / ISO 9001"
|
||||
help="AS9100D / ISO 9001 certified. Upload the combined logo.">
|
||||
<field name="x_fc_as9100_active"/>
|
||||
<field name="x_fc_as9100_logo"
|
||||
widget="image" class="oe_avatar"
|
||||
invisible="not x_fc_as9100_active"/>
|
||||
</setting>
|
||||
<setting id="fp_cgp"
|
||||
string="Controlled Goods Program (CGP)"
|
||||
help="Registered with Canada's Controlled Goods Program.">
|
||||
<field name="x_fc_cgp_active"/>
|
||||
<field name="x_fc_cgp_logo"
|
||||
widget="image" class="oe_avatar"
|
||||
invisible="not x_fc_cgp_active"/>
|
||||
</setting>
|
||||
</block>
|
||||
</app>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fp_settings" model="ir.actions.act_window">
|
||||
<field name="name">Fusion Plating Settings</field>
|
||||
<field name="res_model">res.config.settings</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">current</field>
|
||||
<field name="context">{'module': 'fusion_plating'}</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user