# -*- coding: utf-8 -*- from odoo import models, fields, api import time from odoo.exceptions import UserError from odoo.exceptions import ValidationError class sos_iqi(models.Model): _name = 'sos_iqi' _description = 'Incoming Quality Inspection' _rec_name = 'iqi_no' _order = 'id desc' iqi_no = fields.Char(string="IQI No", readonly= True, required= True, default=lambda self: self._generate_id()) iqi_date = fields.Datetime(string="IQI Date", default=fields.Datetime.now) ir_id_unique_id=fields.Many2one('sos_ir', string='Inward Ref No') test_report = fields.Selection([('yes','YES'),('no','NO')],string='Test Report Received',help="If No, action taken (Collect Material inspection report") in_tact = fields.Selection([('yes','YES'),('no','NO')],string='Boxes/Bag/Barrels/Rolls Sealing shall be in-tact',help="If No, Is opened Boxes / Bags / Barrels/Rolls are identified and stored separately, intimate to QC and Supplier/Service provider.") inspection_label = fields.Selection([('yes','YES'),('no','NO'),('na','NA')],string="Identification Label affixed post inspection") review_test_report = fields.Selection([('yes','YES'),('no','NO')],string="Reviewed the Material Test reports.Any changes in the material specification",help="If Yes, is it Adequate / Needs More Information / Updated Quality Plan") test_report_no = fields.Char(string="Material Test Report No") test_report_doc = fields.Binary(string="Test Report") test_report_filename=fields.Char(string="Test Report Name") batch_no = fields.Text(string="Batch No") serial_no = fields.Text(string="Serial No") fg_name = fields.Many2one('sos_fg', string='FG Name') iqi_type = fields.Selection([('new','New'),('rework','Reworked')],string='IQI Type',default='new') material_name = fields.Many2one('sos_material', string="Material Name") material_code = fields.Char(string="Material Code") supplier_name = fields.Many2one('sos_suppliers',string="Supplier Name") material_category = fields.Many2one('sos_material_configuration',string="Material Category") uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], string="UOM") wo_planned_at = fields.Selection([('inhouse', 'In-House'),('outsource', 'Out-Sourcing')],default="outsource",string="W.O Planned at") rca_required = fields.Selection([('yes', 'Yes'),('no', 'No')], string="RCA Required") rca_notes_text=fields.Text(string="Notes",default="The requirement for a Root Cause Analysis (RCA) is determined by defect severity, recurrence, or a defect rate below 10,000 parts per million (PPM).") sfg_name = fields.Many2one('sos_sfg', string="SFG Name") sfg_code = fields.Char(string="SFG Code",related="sfg_name.sfg_code") service_provider_name = fields.Many2one('sos_service_providers',string="Service Provider Name") sfg_category = fields.Many2one('sos_sfg_configuration',string="SFG Category") invoice_no = fields.Char(string="Invoice No") invoice_date = fields.Datetime(string="Invoice date") received_qty = fields.Integer(string="Received Quantity") approved_qty = fields.Integer(string="Approved Quantity") rejected_qty = fields.Integer(string="Rejected Quantity") rejection_percentage = fields.Float(string="Rejected Percentage", compute="_compute_rejected_percentage", store=True) rejection_percentage_display = fields.Char('Rejection Percentage', compute='_compute_rejected_percentage_display') testing_line_ids = fields.One2many('sos_testing_parameters_line', 'ref_id', ondelete='cascade') calibration_line_ids = fields.One2many('sos_iqi_calibration_line','calibration_id', ondelete='cascade') material_option = fields.Boolean('Materials', default=True) sfg_option = fields.Boolean('Semi-Finished Goods') prepared_by_name = fields.Many2one('res.users', string='Stores Provided by') prepared_by_image = fields.Image(related="prepared_by_name.signature_image",string='Prepared by Sign',readonly=True) prepared_on = fields.Date(string="Prepared On") products = fields.Selection( [ ('BHMS 1.2V', 'BHMS 1.2V'), ('BHMS 2V', 'BHMS 2V'), ('BHMS 12V', 'BHMS 12V'), ('BHMS 48V', 'BHMS 48V'), ('BMS-HV', 'BMS-HV'), ('BMS-LV 100A', 'BMS-LV 100A'), ('BMS-LV 40A', 'BMS-LV 40A'), ('SBMS 55A', 'SBMS 55A'), ('MC 250W', 'MC 250W'), ('HeartTarang', 'HeartTarang') ], string="Products",required=True) qc_by_name = fields.Many2one('res.users', string='QC Tested by') qc_by_image = fields.Image(related="qc_by_name.signature_image",string='QC Tested Sign',readonly=True) qc_tested_on = fields.Date(string="QC Tested On") stores_received_by = fields.Many2one('res.users', string='Stores Received by') stores_received_image = fields.Image(related="stores_received_by.signature_image",string='Stores Received by Sign',readonly=True) stores_received_on = fields.Date(string="Stores Received On") qa_by_name = fields.Many2one('res.users', string='QA Approved By') qa_by_image = fields.Image(related="qa_by_name.signature_image",string='QA Approved by Sign',readonly=True) qa_tested_on = fields.Date(string="QA Approved On") qa_comments = fields.Text(string="QA Comments") ppm = fields.Integer(string="PPM",compute="_compute_ppm") reworked_count = fields.Integer(string="Reworked Count",default=0) old_iqi_ref = fields.Many2one('sos_iqi',string="Rework References") status = fields.Selection([ ('open', 'Open'),('closed', 'Closed')], default='open' , string="Status") batch_allotmnet = fields.Boolean(string="Allot Batch") batch_allotmnet_field = fields.Char(string="Other Field", compute='_compute_batch_allotmnet', store=True) ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Reference (If any Rejected)") from_origin = fields.Selection([('Vendor','Vendor'),('In-House','In-House')],string="Received From",default="Vendor") currency_id = fields.Many2one('res.currency', string='Currency') unit_price = fields.Monetary(string="Unit Price", currency_field='currency_id') @api.onchange('received_qty', 'approved_qty') def _onchange_rejected_qty(self): if self.received_qty and self.approved_qty: self.rejected_qty = self.received_qty - self.approved_qty @api.depends('batch_allotmnet') def _compute_batch_allotmnet(self): for record in self: if record.batch_allotmnet: new_record = self.env['sos_batch_allotment_register'].create({ 'sfg_name': record.sfg_name.id, 'alloted_date':record.iqi_date, 'batch_size':record.received_qty, 'iqi_ref_no':record.id }) @api.depends('received_qty', 'rejected_qty') def _compute_ppm(self): for record in self: if record.rejected_qty != 0 and record.received_qty != 0: record.ppm = (record.rejected_qty / record.received_qty) * 10**6 else: record.ppm = 0 def action_report_iqi_btn(self): try: action = self.env.ref("sos_inventory.action_report_iqi").report_action(self) return action except ValueError as e: print(f"Failed to find report action: {e}") def action_view_report(self): if not self.test_report_doc: raise UserError("No test report available for this record.") return { 'type': 'ir.actions.act_url', 'url': '/web/content/{}/{}/{}?download=false'.format( self._name, self.id, 'test_report_doc'), 'target': 'new', # This opens the PDF in a new tab } @api.depends('rejection_percentage') def _compute_rejected_percentage_display(self): for record in self: record.rejection_percentage_display = "{:.2f}%".format(record.rejection_percentage) @api.depends('received_qty', 'rejected_qty') def _compute_rejected_percentage(self): for record in self: if record.received_qty > 0: record.rejection_percentage = (record.rejected_qty / record.received_qty) * 100 else: record.rejection_percentage = 0 @api.onchange('material_category') def _onchange_material_category(self): if self.material_category: self._load_testing_parameters('material') @api.onchange('sfg_category') def _onchange_sfg_category(self): if self.sfg_category: self._load_testing_parameters('sfg') def _load_testing_parameters(self,types): if types == 'material': if self.material_category: testing_parameters = self.env['sos_material_configuration'].search([('id', '=', self.material_category.id)]) if not testing_parameters: raise UserError('No testing parameters found for this Item.') lines = [(5, 0, 0)] sorted_parameters = testing_parameters.parameter_ids.sorted(key=lambda param: param.sequence) for param in sorted_parameters: lines.append((0, 0, { 'testing_parameter': param.name, 'req_readings':param.observation, 'obtained_readings':param.obtained_observation })) self.testing_line_ids = lines else: if self.sfg_category: testing_parameters = self.env['sos_sfg_configuration'].search([('id', '=', self.sfg_category.id)]) if not testing_parameters: raise UserError('No testing parameters found for this Item.') lines = [(5, 0, 0)] sorted_parameters = testing_parameters.parameter_ids.sorted(key=lambda param: param.sequence) for param in sorted_parameters: lines.append((0, 0, { 'testing_parameter': param.name, 'req_readings':param.observation, 'obtained_readings':param.obtained_observation })) self.testing_line_ids = lines @api.onchange('material_option') def _onchange_material_option(self): if self.material_option: self.sfg_option = False @api.onchange('sfg_option') def _onchange_sfg_option(self): if self.sfg_option: self.material_option = False def _generate_id(self): sequence_util = self.env['sos_common_scripts'] return sequence_util.generate_sequence('sos_iqi','IQI', 'iqi_no') def action_report_esign_btn(self): # Email part body_html = f"""

Below IQI is waiting for your Approval

""" send_email = self.env['sos_common_scripts'] send_email.send_group_email(self.env,'sos_iqi',self.id,"deenalaura.m@sosaley.in","IQI Approval Request",body_html,'sos_inventory.sos_qc_user') # Email part ends sequence_util = self.env['sos_common_scripts'] return sequence_util.action_assign_signature( self, 'prepared_by_name', 'prepared_on' ) def action_qc_esign_btn(self): if self.approved_qty == 0 and self.rejected_qty == 0: raise UserError("Please enter Approved Quantity or Rejected Quantity before proceeding.") ncmr_record = False sequence_util = self.env['sos_common_scripts'] ncmr_id = sequence_util.generate_sequence('sos_ncmr', 'NCMR', 'ncmr_no') if self.rejected_qty > 0: if self.material_option: ncmr_record = self.env['sos_ncmr'].create({ 'ncmr_no': ncmr_id, 'material_option':True, 'sfg_option':False, 'batch_no':self.batch_no, 'rejected_qty':self.rejected_qty, 'material_name': self.material_name.id, 'material_code': self.material_name.material_code, 'incoming_doc_ref':self.id, 'department':'Quality Control', 'rca_required':self.rca_required }) else: ncmr_record = self.env['sos_ncmr'].create({ 'ncmr_no': ncmr_id, 'material_option':False, 'sfg_option':True, 'batch_no':self.batch_no, 'rejected_qty':self.rejected_qty, 'sfg_name': self.sfg_name.id, 'sfg_code': self.sfg_name.sfg_code, 'incoming_doc_ref':self.id, 'department':'Quality Control', 'rca_required':self.rca_required }) # Email part body_html = f"""

Below IQI is waiting for your Approval

""" ncmr_body_html = f"""

Below NCMR is waiting for your Inspection

""" if ncmr_record: self.ncmr_ref = ncmr_record.id sequence_util.send_group_email(self.env,'sos_ncmr',ncmr_record.id,"deenalaura.m@sosaley.in","NCMR Inspection Pending",ncmr_body_html,'sos_inventory.sos_qc_user') sequence_util.send_group_email(self.env,'sos_iqi',self.id,"deenalaura.m@sosaley.in","IQI Approval Request",body_html,'sos_inventory.sos_scg_group_user') # Email part ends return sequence_util.action_assign_signature( self, 'qc_by_name', 'qc_tested_on' ) def action_qa_esign_btn(self): self.status = "closed" if self.ncmr_ref: self.ncmr_ref.rca_required = self.rca_required self.ncmr_ref.rca_notes_text = self.rca_notes_text # Email part body_html = f"""

Below IQI is waiting for your Approval

""" send_email = self.env['sos_common_scripts'] send_email.send_group_email(self.env,'sos_iqi',self.id,"deenalaura.m@sosaley.in","IQI Approval Request",body_html,'sos_inventory.sos_scg_group_user') # Email part ends sequence_util = self.env['sos_common_scripts'] return sequence_util.action_assign_signature( self, 'qa_by_name', 'qa_tested_on' ) def generate_grn(self): sequence_util = self.env['sos_common_scripts'] grn_no = sequence_util.generate_sequence('sos_grn', 'GRN', 'grn_no') grn_record = None grn_line_values = [] if self.material_option == True: grn_record = self.env['sos_grn'].with_context(from_script=True).create({ 'grn_no': grn_no, 'iqi_no': self.id, 'ir_no':self.ir_id_unique_id.id, 'supplier_name': self.supplier_name.id, 'received_goods_type': 'Materials', 'supplier_invoice_no':self.invoice_no, 'po_no':self.ir_id_unique_id.po_no.id }) grn_line_values = self.env['sos_grn_line'].with_context(from_script=True).create({ 'grn_id': grn_record.id, 'component_id': self.material_name.id, 'received_qty': self.received_qty, 'approved_qty':self.approved_qty, 'rejected_qty':self.rejected_qty, 'invoice_qty': self.received_qty, 'unit_price':self.unit_price }) elif self.sfg_option == True: if self.from_origin == "Vendor": grn_record = self.env['sos_grn'].with_context(from_script=True).create({ 'grn_no': grn_no, 'iqi_no': self.id, 'ir_no':self.ir_id_unique_id.id, 'service_provider_name': self.service_provider_name.id, 'received_goods_type': 'SFG', 'wo_no':self.ir_id_unique_id.wo_no.id, 'supplier_invoice_no':self.ir_id_unique_id.dc_no_char }) grn_line_values = self.env['sos_grn_line_sfg'].with_context(from_script=True).create({ 'grn_id': grn_record.id, 'component_id': self.sfg_name.id, 'received_qty': self.received_qty, 'approved_qty':self.approved_qty, 'rejected_qty':self.rejected_qty }) else: approved_qty = self.approved_qty current_qty = self.sfg_name.inhand_stock_qty current_value = self.sfg_name.unit_price new_qty = current_qty + approved_qty if new_qty > 0: new_unit_price = ((current_qty * current_value) + (approved_qty * self.sfg_name.unit_price)) / new_qty else: new_unit_price = self.sfg_name.unit_price self.sfg_name.inhand_stock_qty = new_qty self.sfg_name.unit_price = new_unit_price if approved_qty > 0: self.env['sos_sfg_transaction_history'].create({ 'ref_id': self.sfg_name.id, 'iqi_no': self.id, 'component_id': self.sfg_name.id, 'quantity': approved_qty, 'unit_price': self.sfg_name.unit_price, 'action': 'in', }) def action_stores_received_esign_btn(self): sequence_util = self.env['sos_common_scripts'] sequence_util.action_assign_signature( self, 'stores_received_by', 'stores_received_on', 'sos_inventory.sos_scg_group_user' ) self.generate_grn() # Email part body_html = f"""

Below IQI is waiting for your Approval

""" send_email = self.env['sos_common_scripts'] send_email.send_group_email(self.env,'sos_iqi',self.id,"deenalaura.m@sosaley.in","IQI Approval Request",body_html,'sos_inventory.sos_qa_user') # Email part ends class sos_testing_lines(models.Model): _name = 'sos_testing_parameters_line' _description = 'Testing Parameters Lines' ref_id = fields.Many2one('sos_iqi', string="Materials", ondelete="cascade") qp_no = fields.Char(string="QP No") testing_parameter = fields.Char(string="Testing Parameter") req_readings = fields.Char(string="Required Readings / Observation") obtained_readings = fields.Char(string="Obtained Readings / Observation") inspection_remark = fields.Selection([('PASS','PASS'),('FAIL','FAIL')],string="Inspection Decision") sampled_qty = fields.Integer(string="Sampled Quantity") accepted_qty = fields.Integer(string="Accepted Quantity") rejected_qty = fields.Integer(string="Rejected Quantity") remarks = fields.Text(string="Remarks") aodr_qty = fields.Integer(string="Accepted on Deviation (or) Reworked Qty (If any)") aodr_no = fields.Many2one('sos_aodr',string="AODR NO") class SOS_iqi_calibration_line(models.Model): _name = 'sos_iqi_calibration_line' _description = 'Calibration Devices of IQI' calibration_id = fields.Many2one('sos_iqi', string="Materials", ondelete="cascade") name = fields.Many2one('sos_calibration_devices',string="Equipment Name", required=True) s_no = fields.Char(string="Equipment Serial No",related="name.s_no") identification_no= fields.Char(string="Identification No",related="name.identification_no") certification_no= fields.Char(string="Certification No",related="name.certification_no") calibrated_on = fields.Date(string="Calibrated On",related="name.calibrated_on") calibrated_due = fields.Date(string="Calibration Due",related="name.calibrated_due")