This commit is contained in:
gsinghpal
2026-04-27 00:11:18 -04:00
parent d9f58b9851
commit f08f328688
116 changed files with 9891 additions and 359 deletions

View File

@@ -5,3 +5,4 @@
from . import ir_actions_report
from . import report_wo_margin
from . import report_fp_quality_monthly

View File

@@ -0,0 +1,182 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
#
# Sub 12 Phase E — backing data computation for the Monthly Quality
# Summary PDF.
from datetime import timedelta
from odoo import api, fields, models
class ReportFpQualityMonthly(models.AbstractModel):
_name = 'report.fusion_plating_reports.report_fp_quality_monthly_doc'
_description = 'Monthly Quality Summary — Backing'
@api.model
def _get_report_values(self, docids, data=None):
Company = self.env['res.company']
# Default to the user's current company when called from a menu
# action with no record selection (docids will be False/None/[]).
companies = Company.browse(docids) if docids else self.env.company
today = fields.Date.context_today(self.env.user)
period_start = today.replace(day=1)
period_label = (
f'{period_start.strftime("%B %Y")} '
f'(running through {today.strftime("%Y-%m-%d")})'
)
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'] \
if 'fusion.plating.rma' in self.env else None
def _bytype(model, label, opened_field, closed_field, open_dom,
overdue_dom):
if model is None:
return {
'label': label, 'opened': 0, 'closed': 0,
'open_total': 0, 'overdue': 0,
}
opened = model.search_count([
(opened_field, '>=', period_start),
])
closed = (
model.search_count([(closed_field, '>=', period_start)])
if closed_field else 0
)
return {
'label': label,
'opened': opened,
'closed': closed,
'open_total': model.search_count(open_dom),
'overdue': model.search_count(overdue_dom),
}
cutoff_3d = fields.Datetime.subtract(fields.Datetime.now(), days=3)
cutoff_7d = fields.Datetime.subtract(fields.Datetime.now(), days=7)
cutoff_5d = fields.Datetime.subtract(fields.Datetime.now(), days=5)
cutoff_14d = fields.Datetime.subtract(fields.Datetime.now(), days=14)
by_type = [
_bytype(
Hold, 'Quality Holds',
'create_date', None,
[('state', 'in', ('on_hold', 'under_review'))],
[('state', 'in', ('on_hold', 'under_review')),
('create_date', '<', cutoff_3d)],
),
_bytype(
Check, 'QC Checks',
'create_date', None,
[('state', '=', 'pending')] if 'state' in Check._fields else [],
[],
),
_bytype(
Ncr, 'Non-Conformance Reports',
'reported_date', 'closed_date',
[('state', 'in', ('open', 'containment', 'disposition'))],
[('state', 'in', ('open', 'containment', 'disposition')),
('reported_date', '<', cutoff_7d)],
),
_bytype(
Capa, 'CAPAs',
'create_date', None,
[('state', 'not in', ('effective', 'closed'))],
[('state', 'not in', ('effective', 'closed')),
('due_date', '<', today),
('due_date', '!=', False)],
),
]
if Rma is not None:
by_type.append(_bytype(
Rma, 'RMAs',
'create_date', None,
[('state', 'not in', ('closed', 'cancelled'))],
['|',
'&', ('state', '=', 'received'),
('create_date', '<', cutoff_5d),
'&', ('state', 'in', ('authorised', 'shipped_to_us')),
('create_date', '<', cutoff_14d)],
))
# NCR severity
ncr_severity = []
for sev_code, sev_label in [
('critical', 'Critical'), ('high', 'High'),
('medium', 'Medium'), ('low', 'Low'),
]:
ncr_severity.append({
'label': sev_label,
'count': Ncr.search_count([
('severity', '=', sev_code),
('reported_date', '>=', period_start),
]),
})
# CAPA effectiveness
closed_in_period = Capa.search_count([
('state', 'in', ('effective', 'closed', 'not_effective')),
('verification_date', '>=', period_start),
])
effective = Capa.search_count([
('state', '=', 'effective'),
('verification_date', '>=', period_start),
])
not_effective = Capa.search_count([
('state', '=', 'not_effective'),
('verification_date', '>=', period_start),
])
rate_pct = (
int(round(100.0 * effective / closed_in_period))
if closed_in_period else 0
)
# Repeat customers (≥3 NCRs in last 90 days)
cutoff_90d = today - timedelta(days=90)
# Odoo 19 — use _read_group with aggregates=['__count'].
groups = self.env['fusion.plating.ncr']._read_group(
domain=[('reported_date', '>=', cutoff_90d),
('customer_partner_id', '!=', False)],
groupby=['customer_partner_id'],
aggregates=['__count'],
)
repeat_customers = []
for partner, count in groups:
if count < 3:
continue
rma_count = (
Rma.search_count([
('partner_id', '=', partner.id),
('state', 'not in', ('closed', 'cancelled')),
]) if Rma else 0
)
repeat_customers.append({
'name': partner.display_name,
'ncr_count': count,
'rma_count': rma_count,
})
repeat_customers.sort(key=lambda r: r['ncr_count'], reverse=True)
return {
'doc_ids': companies.ids,
'doc_model': 'res.company',
'docs': companies,
'data': {
'period_label': period_label,
'generated_at': fields.Datetime.now().strftime('%Y-%m-%d %H:%M'),
'by_type': by_type,
'ncr_severity': ncr_severity,
'capa': {
'closed': closed_in_period,
'effective': effective,
'not_effective': not_effective,
'rate_pct': rate_pct,
},
'repeat_customers': repeat_customers,
},
}

View File

@@ -96,6 +96,12 @@ class ReportWoMargin(models.AbstractModel):
# ------------------------------------------------------------------
@api.model
def _get_report_values(self, docids, data=None):
# Sub 11 — MRP gone. The report is bound to fusion_plating_reports.action_report_wo_margin
# which itself was uninstalled. Returning empty docs keeps the
# AbstractModel safe to import (its sister fp.job report
# report_fp_job_margin owns the live margin path now).
if 'mrp.production' not in self.env:
return {'doc_ids': [], 'doc_model': 'mrp.production', 'docs': []}
productions = self.env['mrp.production'].browse(docids)
docs = []
for mo in productions: