from odoo import models, fields, api from odoo.exceptions import UserError from odoo.exceptions import ValidationError from datetime import datetime,date,timedelta class FIR_Only(models.Model): _name = 'sos_fir' _description = 'Final Inspection Report' _rec_name='fir_no' _order = 'id desc' fir_no = fields.Char(string="FIR No",default=lambda self: self._generate_id(),readonly= True, required= True) fir_date = fields.Date(string="FIR Date") fg_name = fields.Many2many('sos_fg', string='FG Name') product_type = 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="Product Name") plan_ref_no = fields.Many2one('sos_fg_plan', string='Indent Ref No') batch_size = fields.Integer(string="Batch Size / Received Quantity") batch_No = fields.Char(string="Batch No") serial_no = fields.Char(string="Serial No") mfg_date = fields.Char(string="Manufacturing Date") sampling_size = fields.Integer(string="Sampling Size") calibration_line_ids = fields.One2many('sos_calibration_devices_fir_only', 'ref_id',copy=True) testing_parameter_line_ids = fields.One2many('sos_fir_line', 'ref_line_id',copy=True) 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') qc_by_name = fields.Many2one('res.users', string='QC Tested&Provided by Sign') qc_by_image = fields.Image(related="qc_by_name.signature_image",string='Prepared by Sign',readonly=True) qc_tested_on = fields.Date(string="Approved On") reworked_count = fields.Integer(string="Reworked Count",default=0) qa_by_name = fields.Many2one('res.users', string='QA by Sign') qa_by_image = fields.Image(related="qa_by_name.signature_image",string='Prepared by Sign',readonly=True) qa_tested_on = fields.Date(string="Approved On") ppm = fields.Integer(string="PPM",compute="_compute_ppm") stores_received_by = fields.Many2one('res.users', string='Store Received by Sign') 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") ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Reference (If any Rejected)") customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name", required=True) remarks = fields.Text(string="Remarks") test_log = fields.Binary("Test Log", required=False, attachment=True) test_log_filename = fields.Char("Test Log Filename") rejected_line_ids = fields.One2many('sos_fir_rejected_lines', 'ref_id', string="Finished Goods",copy=True, ondelete='cascade') serial_no_line_ids = fields.One2many('sos_fir_serial_no_lines', 'ref_id',copy=True) sd_card_data = fields.Binary("SD Card Data", required=False, attachment=True) sd_card_data_filename = fields.Char("SD Card Data Filename") cloud_data = fields.Binary("Cloud Data", required=False, attachment=True) cloud_data_filename = fields.Char("Cloud Data Filename") firmware_data = fields.Binary("Firmware Data", required=False, attachment=True) firmware_data_filename = fields.Char("Firmware Data Filename") @api.onchange('batch_size') def _onchange_batch_size(self): if self._origin and self.batch_size is not False: lines = self.env['sos_fir_line'].search([('ref_line_id', '=', self._origin.id)]) line_values = [ (1, line.id, { 'sampled_qty': self.batch_size, 'accepted_qty': self.batch_size }) for line in lines ] self.testing_parameter_line_ids = line_values @api.constrains('test_log_filename', 'sd_card_data_filename', 'cloud_data_filename', 'firmware_data_filename') def _check_file_extension(self): allowed_extensions = ['xlsx', 'xls', 'csv', 'zip', 'pdf'] for record in self: for field_name in ['test_log_filename', 'sd_card_data_filename', 'cloud_data_filename','firmware_data_filename']: file_name = getattr(record, field_name) if file_name: ext = file_name.split('.')[-1].lower() if ext not in allowed_extensions: raise ValidationError("Only .pdf, .xlsx, .xls, .csv, and .zip files are allowed!") def action_report_fir_btn(self): try: action = self.env.ref("sos_inventory.action_report_fir").report_action(self) return action except ValueError as e: print(f"Failed to find report action: {e}") @api.depends('batch_size', 'rejected_qty') def _compute_ppm(self): for record in self: if record.rejected_qty != 0: record.ppm = (record.rejected_qty / record.batch_size) * 10**6 else: record.ppm = 0 @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('batch_size', 'rejected_qty') def _compute_rejected_percentage(self): for record in self: if record.batch_size > 0: record.rejection_percentage = (record.rejected_qty / record.batch_size) * 100 else: record.rejection_percentage = 0 def action_qc_esign_btn(self): 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: for line in self.rejected_line_ids : ncmr_record = self.env['sos_ncmr'].create({ 'ncmr_no': ncmr_id, 'fg_option':True, 'material_option':False, 'sfg_option':False, 'batch_no':self.batch_No, 'rejected_qty':line.quantity, 'fg_name': line.fg_name.id, 'fir_incoming_doc_ref':self.id, 'department':'Quality Control' }) self.ncmr_ref = ncmr_record.id ncmr_body_html = f"""

Below NCMR is waiting for your Inspection

""" sequence_util.send_group_email(self.env,'sos_ncmr',ncmr_id,"deenalaura.m@sosaley.in","NCMR Inspection Pending",ncmr_body_html,'sos_inventory.sos_qc_user') # Email part body_html = f"""

Below Final Inspection Report is waiting for your Approval

""" sequence_util.send_group_email(self.env,"sos_fir",self.id,"deenalaura.m@sosaley.in","FIR Approval Request",body_html,'sos_inventory.sos_qa_user') # Email part ends return sequence_util.action_assign_signature( self, 'qc_by_name', 'qc_tested_on' ) def action_qa_esign_btn(self): # Email part body_html = f"""

Below FIR is waiting for your Approval

""" send_email = self.env['sos_common_scripts'] send_email.send_group_email(self.env,"sos_fir",self.id,"deenalaura.m@sosaley.in","FIR 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 action_stores_esign_btn(self): for serial in self.serial_no_line_ids: self.env['sos_serial_nos'].create({ 'customer_name': self.customer_name.id, # 'fg_name': self.fg_name.id, 'serial_no': serial.serial_no, 'batch_no': self.batch_No, 'delivery_date': fields.Date.today() }) 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' ) def _generate_id(self): sequence_util = self.env['sos_common_scripts'] return sequence_util.generate_sequence('sos_fir','FIR', 'fir_no') @api.onchange('product_type') def _onchange_product_type(self): if self.product_type: self._load_testing_parameters() # @api.model # def create(self, vals): # record = super(FIR_Only, self).create(vals) # if record.product_type: # record._load_testing_parameters() # return record # def write(self, vals): # result = super(FIR_Only, self).write(vals) # if 'product_type' in vals: # self._load_testing_parameters() # return result def _load_testing_parameters(self): if self.product_type: testing_parameters = self.env['sos_fir_testing_parameters'].search([('product_type', '=', self.product_type)]) if not testing_parameters: raise UserError('No testing parameters found for this Product.') # Clear any existing lines lines = [(5, 0, 0)] sorted_parameters = testing_parameters.fir_parameter_ids.sorted(key=lambda param: param.sequence) # Append the sorted parameters for param in sorted_parameters: lines.append((0, 0, { 'testing_parameter': param.id, 'sequence': param.sequence, 'specification': param.specification, 'results': param.results, 'inspection_decision':param.inspection_decision })) # Set the sorted lines to testing_parameter_line_ids self.testing_parameter_line_ids = lines class FIR_Only_Line(models.Model): _name = 'sos_fir_line' _description = 'Final Inspection Report Line Items' ref_line_id = fields.Many2one('sos_fir', ondelete="cascade") sequence = fields.Integer(string="sequence") testing_parameter = fields.Many2one('sos_fir_testing_parameters_lines', string='Testing Parameter') sampled_qty = fields.Integer(string="Sampled Quantity") accepted_qty = fields.Integer(string="Accepted Quantity") rejected_qty = fields.Integer(string="Rejected Quantity") inspection_decision = fields.Selection([('PASS','PASS'),('FAIL','FAIL')],string="Inspection Decision") specification = fields.Char(string="Specification") results = fields.Text(string="Results") class Calibration_devices(models.Model): _name = 'sos_calibration_devices_fir_only' _description = 'Calibartion Devices List' ref_id = fields.Many2one('sos_fir', ondelete="cascade") calibration_device = fields.Many2one('sos_calibration_devices',string="Equipment Name", required=True) identification_No = fields.Char(string="Identification No",related="calibration_device.identification_no") certification_No = fields.Char(string="Certification No",related="calibration_device.certification_no") calibrated_date = fields.Date(string="Calibrated Date",related="calibration_device.calibrated_on") due_date = fields.Date(string="Due Date",related="calibration_device.calibrated_due") class firModel_Rejected_Lines(models.Model): _name = 'sos_fir_rejected_lines' _description = 'Rejected FG Lines' ref_id = fields.Many2one('sos_fir') fg_name = fields.Many2one('sos_fg',string="FG Name") quantity = fields.Integer(string="Rejected Quantity") available_fg_names = fields.Many2many('sos_fg', compute="_compute_available_fg_names", store=False) @api.depends('ref_id') def _compute_available_fg_names(self): for record in self: record.available_fg_names = record.ref_id.fg_name.ids if record.ref_id else [] class BRR_serial_no_Lines(models.Model): _name = 'sos_fir_serial_no_lines' _description = 'Serial No FIR Line Items' ref_id = fields.Many2one('sos_fir', ondelete="cascade") serial_no = fields.Char(string="Serial No") inspection_decision = fields.Selection([('PASS','PASS'),('FAIL','FAIL')],string="Inspection Decision",default="PASS")