Slink/sos_inventory/models/sos_ncmr.py

1117 lines
54 KiB
Python
Executable File

from odoo import models, fields, api
from odoo.exceptions import UserError
from datetime import date,datetime
from odoo.exceptions import ValidationError
import base64
import io
import xlsxwriter
class NCMR_Model(models.Model):
_name = 'sos_ncmr'
_description = 'Non-Conforming Material Report'
_rec_name="ncmr_no"
_order = 'id desc'
ncmr_no = fields.Char(string="NCMR No",default=lambda self: self._generate_id(),readonly= True, required= True)
ncmr_date = fields.Date(string="NCMR Date",default=lambda self: fields.Date.today())
fg_name = fields.Many2one('sos_fg', string='FG Name')
fg_category = fields.Many2one('sos_testing_parameters',string="FG Category", compute="_compute_fg_category", store=True)
batch_no = fields.Char(string="Batch No")
department = fields.Char(string="Department")
status = fields.Selection([ ('open', 'Open'),('closed', 'Closed')], default='open' , string="Status")
material_name = fields.Many2one('sos_material', string="Material Name")
material_code = fields.Char(string="Material Code")
material_category = fields.Many2one('sos_material_configuration',string="Material Category")
rca_required = fields.Selection([('yes', 'Yes'),('no', 'No')], string="RCA Required",default="yes")
rca_notes_text=fields.Text(string="Notes")
sfg_name = fields.Many2one('sos_sfg', string="SFG Name")
sfg_code = fields.Char(string="SFG Code",related="sfg_name.sfg_code")
sfg_category = fields.Many2one('sos_sfg_configuration',string="SFG Category")
rejected_qty = fields.Integer(string="Rejected Quantity")
material_option = fields.Boolean('Materials', default=True)
sfg_option = fields.Boolean('Semi-Finished Goods')
fg_option = fields.Boolean('Finished Goods')
ncmr_type = fields.Selection([ ('new', 'New'),('return', 'Return')], default='new' , string="NCMR Type")
incoming_doc_ref = fields.Many2one('sos_iqi',string="IQI Ref No",readonly=True)
return_incoming_doc_ref = fields.Many2one('sos_return_iqi',string="IQI Ref No",readonly=True)
fir_incoming_doc_ref = fields.Many2one('sos_fir',string="FIR ref",readonly=True)
fg_incoming_doc_ref = fields.Many2one('sos_fir_brr',string="FIR ref",readonly=True)
return_fg_incoming_doc_ref = fields.Many2one('sos_return_fir',string="Return FIR ref",readonly=True)
incoming_responsibility = fields.Text(string="Incoming Responsibility")
qa_action = fields.Selection([ ('scrap', 'Scrap'),('rework', 'Rework')], string="Action")
dispensed_doc_ref = fields.Char(string="Dispensed Doc Ref")
dispensed_responsibility = fields.Text(string="Dispensed Responsibility")
return_incoming_resposibility = fields.Char(string="Approved Doc Ref")
approved_doc_ref = fields.Char(string="Approved Doc Ref")
approved_responsibility = fields.Text(string="Approved Responsibility")
customer_complaint_doc_ref = fields.Char(string="Customer Complaint Doc Ref")
customer_complaint_responsibility = fields.Text(string="Customer Complaint Responsibility")
approved_fg_doc_ref = fields.Char(string="Approved FG Doc Ref")
approved_fg_responsibility = fields.Text(string="Approved FG Responsibility")
returned_fg_doc_ref = fields.Char(string="Approved FG Doc Ref")
returned_fg_responsibility = fields.Text(string="Approved FG Responsibility")
finished_fg_assy = fields.Char()
finished_fg_assy_responsibility = fields.Text(string="Production Assy FG Responsibility")
description_of_nc = fields.Html(string="Description of Non-Conformities")
root_cause_of_nc = fields.Html(string="Root Cause",
default="<p>Why #1 :</p><p>Why #2 :</p><p>Why #3 :</p><p>Why #4 :</p><p>Why #5 :</p>")
containment_action_of_nc = fields.Html(string="Containment Action to close the Non-Conformities")
comments_on_capa = fields.Html(string="Comments on Corrective / Preventive Action ")
qa_comments=fields.Text(string="QA Comments")
qa_done_on = fields.Date(string="Verified On")
rework_responsible_rd_user = fields.Many2one('res.users',string='R&D User')
rd_user = fields.Many2many('res.users',string='R&D User', relation='sos_ncmr_rd_user_rel')
qa_by = fields.Many2one('res.users',string='QC In-Charge',readonly=True)
qa_sign = fields.Image(related="qa_by.signature_image",string='QC In-Charge',readonly=True)
qa_tested_on = fields.Datetime(string="Approved On")
line_ids = fields.One2many('sos_ncmr_line', 'ncmr_id', string="Line Ids",copy=True)
capa_line_ids = fields.One2many('sos_ncmr_capa_line', 'ncmr_id', string="CAPA Line Ids",copy=True)
line_count = fields.Integer(string='Number of Defects', compute='_compute_line_count', store=True)
unique_defect_count = fields.Integer(string='Required NCMR', compute='_compute_line_count', store=True)
material_line_count = fields.Integer(string='Number of Defects', compute='_compute_material_line_count', store=True)
fg_line_count = fields.Integer(string='Number of Defects', compute='_compute_fg_line_count', store=True)
material_unique_defect_count = fields.Integer(string='Required NCMR', compute='_compute_material_line_count', store=True)
fg_unique_defect_count = fields.Integer(string='Required NCMR', compute='_compute_fg_line_count', store=True)
defective_status_ids = fields.One2many('sos_ncmr_defective_status', 'ncmr_id', string='Defectives with Status')
opened_issues = fields.Integer(string="Issues Open", compute="_calculate_status")
closed_issues = fields.Integer(string="Issues Closed", compute="_calculate_status")
action_group = fields.Selection(
[('production', 'Production Team'), ('scg', 'SCG Team'), ('rd', 'R&D Team')],
string="Action By",
default="scg"
)
forward_on = fields.Datetime(string="Forwarded On")
forward_by = fields.Many2one('res.users',string='Forward By',readonly=True)
forward_sign = fields.Image(related="forward_by.signature_image",string='Sign',readonly=True)
forward_to = fields.Many2many('res.users', string='Forward To')
comments=fields.Text(string="Comments")
scg_approval_by = fields.Many2one('res.users',string='SCG Approved By',readonly=True)
scg_approval_sign = fields.Image(related="scg_approval_by.signature_image",string='SCG In-Charge',readonly=True)
scg_approval_on = fields.Datetime(string="Approved On")
scg_comments=fields.Text(string="Comments")
production_approval_by = fields.Many2one('res.users',string='Production In-Charge',readonly=True)
production_approval_sign = fields.Image(related="production_approval_by.signature_image",string='Production In-Charge',readonly=True)
production_approval_on = fields.Datetime(string="Approved On")
production_comments=fields.Text(string="Comments")
rework_action = fields.Selection(
[('inhouse', 'In-House'), ('outsourcing_vendor', 'Outsourcing Vendor'), ('supplier_replacement', 'Supplier Replacement')],
string="Rework Action",
default="inhouse"
)
rework_action_by_qc = fields.Boolean(string="Rework")
rd_comments=fields.Text(string="Comments")
rd_approval_by = fields.Many2one('res.users',string='R&D In-Charge',readonly=True)
rd_approval_sign = fields.Image(related="rd_approval_by.signature_image",string='R&D In-Charge',readonly=True)
rd_approval_on = fields.Datetime(string="Approved On")
rework_rd_approval_by = fields.Many2one('res.users',string='Rework - R&D In-Charge',readonly=True)
rework_rd_approval_sign = fields.Image(related="rework_rd_approval_by.signature_image",string='Rework - R&D In-Charge',readonly=True)
rework_rd_approval_on = fields.Datetime(string="Approved On")
responsible_department = fields.Many2one('sos_departments', string='Department')
responsible_name = fields.Many2one('res.users',string='Responsible Person',domain="[('id', 'in', allowed_user_ids)]")
allowed_user_ids = fields.Many2many('res.users', compute='_compute_allowed_users')
rca_responsible_department = fields.Many2many('sos_departments', string='Department')
rca_responsible_name = fields.Many2many('res.users',string='Responsible Person',relation='sos_ncmr_rca_responsible_name_rel',domain="[('id', 'in', rca_allowed_user_ids)]")
rca_allowed_user_ids = fields.Many2many('res.users', compute='_rca_compute_allowed_users')
combined_incoming_doc_ref = fields.Reference(
selection=[
('sos_iqi', 'IQI Ref No'),
('sos_return_iqi', 'Return IQI Ref No'),
('sos_fir_brr', 'BRR Ref'),
('sos_return_fir', 'Return BRR FIR Ref'),
('sos_fir', 'FIR Ref')
],
string="Document Reference",
compute="_compute_combined_incoming_doc_ref",
store=False
)
supplier_name = fields.Many2one('sos_suppliers',string="Supplier Name",compute="_get_supplier_name")
service_provider_name = fields.Many2one('sos_service_providers',string="Service Provider Name",compute="_get_service_provider_name")
customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name",compute="_get_customer_name")
outsourcing_return_ref_no = fields.Many2one('sos_sfg_outsourcing_return_register',string="Outsourcing Return Ref No")
problem_description_line_ids = fields.One2many('sos_ncmr_problem_description_line', 'ncmr_id', string="Problem Description Line Ids",copy=True)
@api.depends('responsible_department')
def _compute_allowed_users(self):
for rec in self:
rec.allowed_user_ids = rec.responsible_department.users_line_ids.mapped('users')
@api.depends('rca_responsible_department')
def _rca_compute_allowed_users(self):
for rec in self:
rec.rca_allowed_user_ids = rec.rca_responsible_department.users_line_ids.mapped('users')
@api.model
def get_service_suppliers_by_goods_type(self,goods_type=False,startDate=False, endDate=False):
if goods_type == 'Material':
query = """
SELECT id,supplier_name
FROM sos_suppliers
WHERE id IN (
SELECT b.supplier_name
FROM sos_ncmr a, sos_iqi b
WHERE a.incoming_doc_ref = b.id
AND a.material_option = 't'
AND a.ncmr_date BETWEEN %s AND %s
)
"""
elif goods_type == 'SFG':
query = """
SELECT id,service_provider_name
FROM sos_service_providers
WHERE id IN (
SELECT b.service_provider_name
FROM sos_ncmr a, sos_iqi b
WHERE a.incoming_doc_ref = b.id
AND a.sfg_option = 't'
AND a.ncmr_date BETWEEN %s AND %s
)
"""
self.env.cr.execute(query,(startDate, endDate))
rows = self.env.cr.fetchall()
result = [{'id': '', 'supplier_name': 'Select Supplier/Service'}]
result += [{'id': row[0], 'supplier_name': row[1]} for row in rows]
return result
@api.model
def get_pareto_data(self, start_date=False, end_date=False, goodstype=False, categoryId=False, servicesupplier=False):
if not start_date or not end_date:
raise ValueError("Start date and end date must be provided.")
base_where_clause = "a.ncmr_date BETWEEN %s AND %s"
where_params = [start_date, end_date]
base_groupby_clause = "d.name"
if goodstype == 'Material':
join_clause = """
JOIN sos_material_defective_line_sos_ncmr_line_rel c ON b.id = c.sos_ncmr_line_id
JOIN sos_material_defective_line d ON c.sos_material_defective_line_id = d.id
JOIN sos_material_configuration e ON a.material_category = e.id
"""
if categoryId and categoryId != 'Select Category':
join_clause += " JOIN sos_material f ON a.material_name = f.id JOIN sos_material_types g ON f.material_type_id = g.id"
base_where_clause += " AND g.name = %s"
#base_groupby_clause += ",g.name"
where_params.append(categoryId)
if servicesupplier and servicesupplier !='Select Supplier/Service' and servicesupplier !='undefined':
join_clause += " LEFT JOIN sos_iqi h ON a.incoming_doc_ref = h.id LEFT JOIN sos_suppliers i ON h.supplier_name = i.id"
base_where_clause += " AND h.supplier_name = %s"
base_groupby_clause += ",h.supplier_name"
where_params.append(servicesupplier)
elif goodstype == 'SFG':
join_clause = """
JOIN sos_ncmr_line_sos_sfg_defective_line_rel c ON b.id = c.sos_ncmr_line_id
JOIN sos_sfg_defective_line d ON c.sos_sfg_defective_line_id = d.id
JOIN sos_sfg_configuration e ON a.sfg_category = e.id
"""
if categoryId and categoryId != 'Select Category':
join_clause += " JOIN sos_sfg f ON a.sfg_name = f.id"
base_where_clause += " AND f.category = %s"
base_groupby_clause += ",f.category"
where_params.append(categoryId)
if servicesupplier and servicesupplier !='Select Supplier/Service' and servicesupplier !='undefined':
join_clause += " LEFT JOIN sos_iqi g ON a.incoming_doc_ref = g.id LEFT JOIN sos_service_providers h ON g.service_provider_name = h.id"
base_where_clause += " AND g.service_provider_name = %s"
base_groupby_clause += ",g.service_provider_name"
where_params.append(servicesupplier)
elif goodstype == 'FG':
join_clause = """
JOIN sos_defectives_sos_ncmr_line_rel c ON b.id = c.sos_ncmr_line_id
JOIN sos_defectives d ON c.sos_defectives_id = d.id
JOIN sos_testing_parameters f ON a.fg_category = f.id
JOIN sos_fg e ON e.id = f.fg_name
"""
if categoryId and categoryId != 'Select Category':
base_where_clause += " AND e.fg_type = %s"
base_groupby_clause += ",e.fg_type"
where_params.append(categoryId)
# Build the full SQL query dynamically
query = f"""
SELECT
defect_name,
count,
ROUND(count * 100.0 / total, 2) AS individual_percent,
ROUND(SUM(count) OVER (ORDER BY count DESC ROWS UNBOUNDED PRECEDING) * 100.0 / total, 2) AS cumulative_percent
FROM (
SELECT
d.name AS defect_name,
COUNT(*) AS count,
SUM(COUNT(*)) OVER () AS total
FROM
sos_ncmr a
JOIN sos_ncmr_line b ON a.id = b.ncmr_id
{join_clause}
WHERE
{base_where_clause}
GROUP BY
{base_groupby_clause}
) AS sub
ORDER BY
count DESC;
"""
self.env.cr.execute(query, tuple(where_params))
result = self.env.cr.fetchall()
data = []
for row in result:
data.append({
'defect_name': row[0], # defect_name
'count': row[1], # count
'cumulative_percent': row[3], # cumulative_percent
})
return data
#Excel Write
def action_ncmr_report_orm_btn(self, from_date, to_date,force_download=True):
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
headers = {
'S No':'S No',
'NCMRNo' : 'NCMR No',
'Product': 'Product',
'Date': 'Date',
'Issue Location': 'Issue Location',
'Customer Name/Supplier Name/Service Provider Name': 'Customer Name/Supplier Name/Service Provider Name',
'Customer/Supplier/Service':'Customer/Supplier/Service',
'Category':'Category',
'Defective': 'Problem Definition',
'Defect Qty': 'Defect Qty',
'Required NCMR': 'Required NCMR',
'Closed Complaint': 'Closed Complaint',
'Pending Complaint': 'Pending Complaint',
'Closure Rate %': 'Closure Rate (%)',
'PPM':'PPM',
'Status':'Status',
'Reason for delay': 'Reason for delay',
'Responsible': 'Responsible',
}
if from_date and to_date:
ncmr_lines = self.env['sos_ncmr'].search([
('ncmr_date', '>=', from_date),
('ncmr_date', '<=', to_date)
])
result = []
for line in ncmr_lines:
ncmrid = line.id
ncmr_no = line.ncmr_no
mat_name = line.material_category.material_name if line.material_category.material_name else None
sfg_name = line.sfg_category.sfg_name if line.sfg_category.sfg_name else None
fg_name = line.fg_name.display_name if line.fg_name.display_name else None
product = mat_name or sfg_name or fg_name
if line.incoming_responsibility:
parts = line.incoming_responsibility.split(",")
incom_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
incom_resp_dep = ''
if line.dispensed_responsibility:
parts = line.dispensed_responsibility.split(",")
dispe_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
dispe_resp_dep = ''
if line.approved_responsibility:
parts = line.approved_responsibility.split(",")
appvd_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
appvd_resp_dep = ''
if line.customer_complaint_responsibility:
parts = line.customer_complaint_responsibility.split(",")
cusmr_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
cusmr_resp_dep = ''
if line.approved_fg_responsibility:
parts = line.approved_fg_responsibility.split(",")
appfg_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
appfg_resp_dep = ''
if line.returned_fg_responsibility:
parts = line.returned_fg_responsibility.split(",")
rtnfg_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
rtnfg_resp_dep = ''
if line.finished_fg_assy_responsibility:
parts = line.finished_fg_assy_responsibility.split(",")
ffgas_resp_dep = parts[0].strip() + "-" + parts[2].strip() if len(parts) > 2 else ''
else:
ffgas_resp_dep = ''
responsibility = incom_resp_dep or dispe_resp_dep or appvd_resp_dep or cusmr_resp_dep or appfg_resp_dep or rtnfg_resp_dep or ffgas_resp_dep
custmer_name = None
suppler_name = None
serprdr_name = None
customer = None
supplier = None
serviceprovider = None
if line.customer_name and line.customer_name.customer_name:
custmer_name = line.customer_name.customer_name
customer = 'Customer'
if line.supplier_name and line.supplier_name.supplier_name:
suppler_name = line.supplier_name.supplier_name
supplier = 'Supplier'
if line.service_provider_name.service_provider_name:
serprdr_name = line.service_provider_name.service_provider_name
serviceprovider = 'Vendor'
cust_supp_serprdr_name = custmer_name or suppler_name or serprdr_name
cust_supp_serprdr = customer or supplier or serviceprovider
mat_type = line.material_name.material_type_id.name if line.material_name.material_type_id.name else None
fg_type = line.fg_name.fg_type if line.fg_name.fg_type else None
sfg_catgry = line.sfg_name.category if line.sfg_name.category else None
category = mat_type or fg_type or sfg_catgry
defect_qty = line.rejected_qty
status = line.status if line.status else 'Open'
required_ncmr = (
line.unique_defect_count if line.unique_defect_count not in (None, 0)
else line.fg_unique_defect_count if line.fg_unique_defect_count not in (None, 0)
else line.material_unique_defect_count if line.material_unique_defect_count not in (None, 0)
else 0
)
open_count = sum(1 for rec in line.defective_status_ids if rec.status == 'open')
closed_count = sum(1 for rec in line.defective_status_ids if rec.status == 'closed')
closure_rate = (closed_count / required_ncmr) * 100 if required_ncmr else 0.0
ppm = None
recevd_qty = line.combined_incoming_doc_ref
if not recevd_qty:
ppm = None
else:
received_qty = None
rejected_qty = None
if recevd_qty and hasattr(recevd_qty,'received_qty'):
received_qty = recevd_qty.received_qty
elif recevd_qty and hasattr(recevd_qty,'batch_size'):
received_qty = recevd_qty.batch_size
if recevd_qty and hasattr(recevd_qty,'rejected_qty'):
rejected_qty = recevd_qty.rejected_qty
if received_qty and rejected_qty is not None:
ppm = (rejected_qty / received_qty) * 10**6 #issue at PPM bcoz of diff name
else:
ppm = None
issue_location = ''
if line.incoming_doc_ref:
issue_location = 'IQI'
elif line.return_incoming_doc_ref:
issue_location = 'Return-IQI'
elif line.fir_incoming_doc_ref:
issue_location = 'FIR'
elif line.fg_incoming_doc_ref:
issue_location = 'FIR BRR'
elif line.return_fg_incoming_doc_ref:
issue_location = 'Return BRR'
if line.defective_status_ids:
defective_names = []
for fgname in line.defective_status_ids:
if fgname.defective_id:
defective_names.append(fgname.defective_id.name)
if fgname.material_defective_id:
defective_names.append(fgname.material_defective_id.name)
if fgname.fg_defective_id:
defective_names.append(fgname.fg_defective_id.name)
defective_names = list(set(defective_names))
else:
query = """
SELECT
sfg_defect.name AS Problem
FROM
sos_ncmr_line nl
JOIN
sos_ncmr n ON nl.ncmr_id = n.id
LEFT JOIN sos_ncmr_defective_status ds ON ds.ncmr_id = nl.ncmr_id
LEFT JOIN sos_ncmr_line_sos_sfg_defective_line_rel sfg_rel ON nl.id = sfg_rel.sos_ncmr_line_id
LEFT JOIN sos_sfg_defective_line sfg_defect ON sfg_rel.sos_sfg_defective_line_id = sfg_defect.id
WHERE
n.id = %s ;
"""
self.env.cr.execute(query,(ncmrid,))
record_list = self.env.cr.fetchall()
defective_names = [r[0] for r in record_list if r[0]]
result.append({
'S No':'',
'NCMRNo': line.ncmr_no,
'Product': product,
'Date': line.ncmr_date.strftime('%Y-%m-%d'),
'Issue Location': issue_location,
'Customer Name/Supplier Name/Service Provider Name':cust_supp_serprdr_name,
'Customer/Supplier/Service':cust_supp_serprdr,
'Category':category,
'Defective': defective_names,
'Defect Qty': defect_qty,
'Required NCMR': required_ncmr,
'Closed Complaint':closed_count,
'Pending Complaint':open_count,
'Closure Rate %':closure_rate,
'PPM':ppm,
'Status': status,
'Reason for delay':'',
'Responsible':responsibility
})
self._write_sheet(workbook, 'NMCR', result, headers)
workbook.close()
output.seek(0)
excel_data = output.read()
attachment = self.env['ir.attachment'].create({
'name': f'NCMR_Export.xlsx',
'type': 'binary',
'datas': base64.b64encode(excel_data),
'res_model': self._name,
'res_id': self.id,
'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
return {
'type': 'ir.actions.act_url',
'url': f'/web/content/{attachment.id}?download=true',
'target': 'self',
}
def _write_sheet(self, workbook, sheet_name, result, headers):
"""Write data to each sheet based on specified fields and custom headers."""
worksheet = workbook.add_worksheet(sheet_name)
for col, header in enumerate(headers.values()):
worksheet.write(0, col, header)
# Write data rows
for row_num, row in enumerate(result, start=1):
worksheet.write(row_num, 0, row_num)
for col_num, key in enumerate(headers.keys()):
value = row.get(key, '')
if isinstance(value, list):
value = ', '.join(str(v) for v in value)
worksheet.write(row_num, col_num, str(value))
@api.depends('incoming_doc_ref', 'return_incoming_doc_ref', 'fg_incoming_doc_ref','return_fg_incoming_doc_ref','fir_incoming_doc_ref')
def _compute_combined_incoming_doc_ref(self):
for record in self:
if record.incoming_doc_ref:
record.combined_incoming_doc_ref = f'sos_iqi,{record.incoming_doc_ref.id}'
elif record.return_incoming_doc_ref:
record.combined_incoming_doc_ref = f'sos_return_iqi,{record.return_incoming_doc_ref.id}'
elif record.fg_incoming_doc_ref:
record.combined_incoming_doc_ref = f'sos_fir_brr,{record.fg_incoming_doc_ref.id}'
elif record.return_fg_incoming_doc_ref:
record.combined_incoming_doc_ref = f'sos_return_fir,{record.return_fg_incoming_doc_ref.id}'
elif record.fir_incoming_doc_ref:
record.combined_incoming_doc_ref = f'sos_fir,{record.fir_incoming_doc_ref.id}'
else:
record.combined_incoming_doc_ref = False
@api.depends('combined_incoming_doc_ref')
def _get_supplier_name(self):
for record in self:
supplier = record.combined_incoming_doc_ref
if supplier and hasattr(supplier, 'supplier_name'):
record.supplier_name = supplier.supplier_name
else:
record.supplier_name = False # or '' if it's a Char field
@api.depends('combined_incoming_doc_ref')
def _get_service_provider_name(self):
for record in self:
service_provider = record.combined_incoming_doc_ref
if service_provider and hasattr(service_provider, 'service_provider_name'):
record.service_provider_name = service_provider.service_provider_name
else:
record.service_provider_name = False # or '' if it's a Char field
@api.depends('combined_incoming_doc_ref')
def _get_customer_name(self):
for record in self:
customer = record.combined_incoming_doc_ref
if customer and hasattr(customer, 'customer_name'):
record.customer_name = customer.customer_name
else:
record.customer_name = False # or '' if it's a Char field
@api.depends('fg_name')
def _compute_fg_category(self):
for record in self:
if record.fg_name:
record.fg_category = self.env['sos_testing_parameters'].search([('fg_name', '=', record.fg_name.id)], limit=1)
else:
record.fg_category = False
@api.onchange('material_option')
def _onchange_material_option(self):
if self.material_option:
self.sfg_option = False
self.fg_option = False
@api.onchange('sfg_option')
def _onchange_sfg_option(self):
if self.sfg_option:
self.material_option = False
self.fg_option = False
@api.onchange('fg_option')
def _onchange_fg_option(self):
if self.fg_option:
self.material_option = False
self.sfg_option = False
@api.depends('defective_status_ids.status')
def _calculate_status(self):
for record in self:
if record.sfg_option:
opened_issues_count = self.env['sos_ncmr_defective_status'].search_count([
('ncmr_id', '=', record.id),
('defective_id','!=',False),
('status', '=', 'open')
])
# Count the number of closed issues
closed_issues_count = self.env['sos_ncmr_defective_status'].search_count([
('ncmr_id', '=', record.id),
('defective_id','!=',False),
('status', '=', 'closed')
])
elif record.material_option:
opened_issues_count = self.env['sos_ncmr_defective_status'].search_count([
('ncmr_id', '=', record.id),
('material_defective_id','!=',False),
('status', '=', 'open')
])
# Count the number of closed issues
closed_issues_count = self.env['sos_ncmr_defective_status'].search_count([
('ncmr_id', '=', record.id),
('material_defective_id','!=',False),
('status', '=', 'closed')
])
else:
opened_issues_count = self.env['sos_ncmr_defective_status'].search_count([
('ncmr_id', '=', record.id),
('fg_defective_id','!=',False),
('status', '=', 'open')
])
# Count the number of closed issues
closed_issues_count = self.env['sos_ncmr_defective_status'].search_count([
('ncmr_id', '=', record.id),
('fg_defective_id','!=',False),
('status', '=', 'closed')
])
if opened_issues_count == 0 and closed_issues_count > 0:
record.status = "closed"
record.opened_issues = opened_issues_count
record.closed_issues = closed_issues_count
@api.depends('line_ids', 'line_ids.defectives')
def _compute_line_count(self):
for record in self:
record.line_count = len(record.line_ids)
unique_defects = set()
for line in record.line_ids:
unique_defects.update(line.defectives.ids)
record.unique_defect_count = len(unique_defects)
@api.depends('line_ids', 'line_ids.material_defectives')
def _compute_material_line_count(self):
for record in self:
record.material_line_count = len(record.line_ids)
material_unique_defects = set()
for line in record.line_ids:
material_unique_defects.update(line.material_defectives.ids)
record.material_unique_defect_count = len(material_unique_defects)
@api.depends('line_ids', 'line_ids.fg_defectives')
def _compute_fg_line_count(self):
for record in self:
record.fg_line_count = len(record.line_ids)
fg_unique_defects = set()
for line in record.line_ids:
fg_unique_defects.update(line.fg_defectives.ids)
record.fg_unique_defect_count = len(fg_unique_defects)
def _generate_id(self):
self.qa_done_on = date.today()
sequence_util = self.env['sos_common_scripts']
return sequence_util.generate_sequence('sos_ncmr','NCMR', 'ncmr_no')
def action_forward_esign_btn(self):
send_email = self.env['sos_common_scripts']
all_closed = True
for record in self.defective_status_ids:
if record.status != 'closed':
all_closed = False
if not record.aodr_no:
raise UserError("All Status Should Be Closed")
return
if self.qa_action == "scrap":
if self.material_option:
material_type = "material"
column = "material_name"
elif self.sfg_option:
material_type = "sfg"
column = "sfg_name"
else:
material_type = "fg"
column = "fg_name"
# Creating the disposal register record
disposal_register = self.env['sos_disposal_register'].create({
'dr_no': send_email.generate_sequence('sos_disposal_register', 'DR', 'dr_no'),
'ncmr_no': self.id,
'goods_type': material_type,
column: getattr(self, column).id if getattr(self, column) else False # Dynamically assign value based on column name
})
return send_email.action_assign_signature(
self,
'forward_by',
'forward_on',
'sos_inventory.sos_qa_user'
)
else:
if all_closed or record.aodr_no:
self.status = 'closed'
if self.rework_action == "inhouse":
s_no_list = self.line_ids.mapped('s_no') # Collect all s_no values
s_no_str = ', '.join(map(str, s_no_list))
iqi_record = self.env['sos_iqi'].create({
'iqi_no': send_email.generate_sequence('sos_iqi', 'R-IQI', 'iqi_no'),
'material_option':self.material_option,
'sfg_option':self.sfg_option,
'received_qty': self.rejected_qty,
'iqi_type':'rework',
'material_name': self.material_name.id,
'material_code': self.material_name.material_code,
'sfg_name': self.sfg_name.id,
'sfg_code': self.sfg_name.sfg_code,
'supplier_name':self.incoming_doc_ref.supplier_name.id,
'service_provider_name':self.incoming_doc_ref.service_provider_name.id,
'old_iqi_ref': self.incoming_doc_ref.id,
'ir_id_unique_id':self.incoming_doc_ref.ir_id_unique_id.id,
'in_tact':self.incoming_doc_ref.in_tact,
'ncmr_ref':self.id,
'serial_no':s_no_str
# 'test_report':self.incoming_doc_ref.test_report,
# 'test_report_no':self.incoming_doc_ref.test_report_no,
# 'test_report_doc':self.incoming_doc_ref.test_report_doc,
# 'test_report_filename':self.incoming_doc_ref.test_report_filename
})
print(iqi_record)
# Rework Iteration starts
sos_record = self.env['sos_iqi'].search([('id', '=', self.incoming_doc_ref.id)], limit=1)
if sos_record:
rework_iteration = sos_record.reworked_count + 1
sos_record.write({
'reworked_count':rework_iteration
})
# Rework Iteration ends
return send_email.action_assign_signature(
self,
'forward_by',
'forward_on',
'sos_inventory.sos_qa_user'
)
else:
raise UserError("All Status Should Be Closed")
def action_report_ncmr_btn(self):
try:
action = self.env.ref("sos_inventory.action_report_ncmr").report_action(self)
return action
except ValueError as e:
print(f"Failed to find report action: {e}")
def action_scg_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
orr_no = sequence_util.generate_sequence('sos_sfg_outsourcing_return_register','ORR', 'orr_no')
send_email = self.env['sos_common_scripts']
send_email.action_assign_signature(
self,
'scg_approval_by',
'scg_approval_on',
'sos_inventory.sos_scg_group_user'
)
if self.rework_action == "inhouse":
if self.material_option:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'material_name':self.incoming_doc_ref.material_name,
'goods_type':'Materials',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'inhouse'
})
else:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'sfg_name':self.incoming_doc_ref.sfg_name,
'goods_type':'SFG',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'inhouse'
})
self.outsourcing_return_ref_no = orr_record.id
body_html = f"""
<p>Below <b>NCMR</b> is waiting for your Action</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_group_email(self.env,'sos_ncmr',self.id,"deenalaura.m@sosaley.in","NCMR Action Request",body_html,"sos_inventory.sos_production_user")
elif self.rework_action == "supplier_replacement":
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'material_name':self.incoming_doc_ref.material_name,
'supplier_name':self.incoming_doc_ref.supplier_name.id,
'goods_type':'Materials',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'outsourcing'
})
else:
if self.material_option:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'material_name':self.incoming_doc_ref.material_name,
'supplier_name':self.incoming_doc_ref.supplier_name.id,
'goods_type':'Materials',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'outsourcing'
})
else:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'service_provider_name':self.incoming_doc_ref.service_provider_name,
'sfg_name':self.incoming_doc_ref.sfg_name,
'goods_type':'SFG',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'outsourcing'
})
self.outsourcing_return_ref_no = orr_record.id
def action_production_esign_btn(self):
send_email = self.env['sos_common_scripts']
send_email.action_assign_signature(
self,
'production_approval_by',
'production_approval_on',
'sos_inventory.sos_production_user'
)
def action_rd_esign_btn(self):
send_email = self.env['sos_common_scripts']
send_email.action_assign_signature(
self,
'rd_approval_by',
'rd_approval_on'
)
def action_rework_rd_esign_btn(self):
send_email = self.env['sos_common_scripts']
send_email.action_assign_signature(
self,
'rework_rd_approval_by',
'rework_rd_approval_on'
)
def action_qa_esign_btn(self):
if self.responsible_department:
if self.material_option:
material = self.material_name.part_no
elif self.sfg_option:
material = self.sfg_name.name
else:
material = self.fg_name.name
result_col = f"NCMR Ref No :{getattr(self, 'ncmr_no', '')}"
new_action_record = self.env['sos_brm_action'].create({
'cross_dept_action': 'cross_dept',
'department': 3,
'responsible_person': getattr(self.responsible_name, 'id', False),
'assigned_by': getattr(self.qa_by, 'id', False),
'assigned_from_dept': 3,
'assigned_to_dept': getattr(self.responsible_department, 'id', False),
'name': f"NCMR Assigned : {material}",
'result': result_col
})
if self.action_group:
sequence_util = self.env['sos_common_scripts']
sequence_util.action_assign_signature(
self,
'qa_by',
'qa_tested_on',
'sos_inventory.sos_qc_user'
)
if self.rework_action_by_qc:
# Email part
body_html = f"""
<p>Below <b>NCMR</b> is waiting for your Action</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(
self.env,
"sos_ncmr",
self.id,
self.rework_responsible_rd_user.login,
"NCMR Rework Action Request",
body_html
)
if self.action_group == "rd":
# Email part
body_html = f"""
<p>Below <b>NCMR</b> is waiting for your Action</p>
"""
send_email = self.env['sos_common_scripts']
for user in self.rd_user:
if user.login:
send_email.send_direct_email(
self.env,
"sos_ncmr",
self.id,
user.login,
"NCMR Action Request",
body_html
)
else:
if self.action_group == "production":
group="sos_inventory.sos_production_user"
else:
group="sos_inventory.sos_scg_group_user"
# Email part
body_html = f"""
<p>Below <b>NCMR</b> is waiting for your Action</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_group_email(self.env,'sos_ncmr',self.id,"deenalaura.m@sosaley.in","NCMR Action Request",body_html,group)
# Email part ends
else:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': "Select Action to be taken by",
'type': 'danger',
'sticky': False
}
}
class NCMR_Defective_Status(models.Model):
_name = 'sos_ncmr_defective_status'
_description = 'NCMR Defective Status'
ncmr_id = fields.Many2one('sos_ncmr', string="NCMR Reference", ondelete="cascade")
defective_id = fields.Many2one('sos_sfg_defective_line', string="Defective")
material_defective_id = fields.Many2one('sos_material_defective_line', string="Defective")
fg_defective_id = fields.Many2one('sos_defectives', string="Defective")
aodr_no = fields.Many2one('sos_aodr',string="AODR No(If Any)")
status = fields.Selection([('open', 'Open'), ('closed', 'Closed')], string="Status", required=True, default='open')
_sql_constraints = [
('ncmr_defective_unique', 'unique(ncmr_id, defective_id)', 'Each defective can only have one status per NCMR.')
]
class NCMR_Model_CAPA_Line(models.Model):
_name = 'sos_ncmr_capa_line'
_description = 'CAPA Lines'
ncmr_id = fields.Many2one('sos_ncmr', string="NCMR Reference", ondelete="cascade")
issue = fields.Char(string="Issue")
corrective_action = fields.Html(string="D5: Permanent Corrective Action")
implement_validate_corrective_action = fields.Html(string="D6:Implement & Validate Corrective Actions")
preventive_action = fields.Html(string="D7:Preventive Recurrence")
class NCMR_Model_Line(models.Model):
_name = 'sos_ncmr_line'
_description = 'Non-Conforming Material Report Defectives'
s_no = fields.Char(string="Serial No")
ncmr_id = fields.Many2one('sos_ncmr', string="NCMR Reference", ondelete="cascade")
fg_name = fields.Many2one(related='ncmr_id.fg_name', string="FG Name", readonly=True, store=True)
remarks=fields.Char(string="Specific Remarks")
sfg_category = fields.Many2one(related='ncmr_id.sfg_category', string="SFG Category", readonly=True)
defective_count = fields.Integer(string='Number of Defectives', compute='_compute_defective_count', store=True)
defectives = fields.Many2many('sos_sfg_defective_line', string='Defectives')
defectives_domain = fields.Many2many('sos_sfg_defective_line', compute='_compute_defective_domain')
material_category = fields.Many2one(related='ncmr_id.material_category', string="Material Category", readonly=True)
material_defectives = fields.Many2many('sos_material_defective_line', string='Defectives')
material_defectives_domain = fields.Many2many('sos_material_defective_line', compute='_compute_material_defective_domain')
material_defective_count = fields.Integer(string='Number of Defectives', compute='_compute_material_defective_count', store=True)
fg_category = fields.Many2one(related='ncmr_id.fg_category', string="FG Category", readonly=True)
fg_defective_count = fields.Integer(string='Number of Defectives', compute='_compute_fg_defective_count', store=True)
fg_defectives = fields.Many2many('sos_defectives', string='Defectives')
fg_defectives_domain = fields.Many2many('sos_defectives', compute='_compute_fg_defective_domain')
@api.depends('ncmr_id', 'ncmr_id.fg_category')
def _compute_fg_defective_domain(self):
for record in self:
if record.fg_category:
sos_defectives_records = self.env['sos_defectives'].search([('defective_id', '=', record.fg_category.id)])
defective_names = sos_defectives_records.mapped('id')
matching_config_ids = self.env['sos_defectives'].search([
('id', 'in', defective_names)
]).ids
if matching_config_ids:
record.fg_defectives_domain = matching_config_ids if matching_config_ids else [] # Use [0] to prevent errors
else:
record.fg_defectives_domain = []
else:
record.fg_defectives_domain = []
@api.depends('ncmr_id', 'ncmr_id.material_category')
def _compute_material_defective_domain(self):
for record in self:
if record.material_category:
defective_names = record.material_category.defective_ids.mapped('id')
matching_config_ids = self.env['sos_material_defective_line'].search([
('id', 'in', defective_names)
]).ids
if matching_config_ids:
record.material_defectives_domain = matching_config_ids if matching_config_ids else [] # Use [0] to prevent errors
else:
record.material_defectives_domain = []
else:
record.material_defectives_domain = []
@api.depends('ncmr_id', 'ncmr_id.sfg_category')
def _compute_defective_domain(self):
for record in self:
if record.sfg_category:
defective_names = record.sfg_category.defective_ids.mapped('id')
matching_config_ids = self.env['sos_sfg_defective_line'].search([
('id', 'in', defective_names)
]).ids
if matching_config_ids:
record.defectives_domain = matching_config_ids if matching_config_ids else [] # Use [0] to prevent errors
else:
record.defectives_domain = []
else:
record.defectives_domain = []
@api.model
def create(self, vals):
record = super(NCMR_Model_Line, self).create(vals)
record._update_defective_status_ids()
return record
def write(self, vals):
result = super(NCMR_Model_Line, self).write(vals)
self._update_defective_status_ids()
return result
def _update_defective_status_ids(self):
for record in self:
if record.ncmr_id:
if record.ncmr_id.sfg_option:
existing_defective_ids = record.ncmr_id.defective_status_ids.mapped('defective_id.id')
new_lines = []
for defective in record.defectives:
if defective.id not in existing_defective_ids:
new_lines.append((0, 0, {
'defective_id': defective.id,
'ncmr_id': record.ncmr_id.id,
}))
elif record.ncmr_id.material_option:
existing_defective_ids = record.ncmr_id.defective_status_ids.mapped('material_defective_id.id')
new_lines = []
for defective in record.material_defectives:
if defective.id not in existing_defective_ids:
new_lines.append((0, 0, {
'material_defective_id': defective.id,
'ncmr_id': record.ncmr_id.id,
}))
else:
existing_defective_ids = record.ncmr_id.defective_status_ids.mapped('fg_defective_id.id')
new_lines = []
for defective in record.fg_defectives:
if defective.id not in existing_defective_ids:
new_lines.append((0, 0, {
'fg_defective_id': defective.id,
'ncmr_id': record.ncmr_id.id,
}))
if new_lines:
record.ncmr_id.write({
'defective_status_ids': new_lines
})
@api.depends('defectives')
def _compute_defective_count(self):
for record in self:
record.defective_count = len(record.defectives)
@api.depends('material_defectives')
def _compute_material_defective_count(self):
for record in self:
record.material_defective_count = len(record.material_defectives)
@api.depends('fg_defectives')
def _compute_fg_defective_count(self):
for record in self:
record.fg_defective_count = len(record.fg_defectives)
class NCMR_Model_PROBLEM_DESCRIPTION_Line(models.Model):
_name = 'sos_ncmr_problem_description_line'
_description = 'Problem Description Lines'
ncmr_id = fields.Many2one('sos_ncmr', string="NCMR Reference", ondelete="cascade")
clear_statement = fields.Html(string="Clear Statement")
photos = fields.Html(string="Photos")
what = fields.Text(string="What")
where = fields.Text(string="Where")
when = fields.Text(string="When")
why = fields.Text(string="Why")
who = fields.Text(string="Who")
how = fields.Text(string="How")
how_many = fields.Text(string="How many")