from odoo import models, fields, api from datetime import date,datetime from odoo.exceptions import ValidationError,UserError import re class SOS_TC(models.Model): _name = 'sos_transfer_challan' _description = 'Transfer Challan' _rec_name="tc_no" _order = 'tc_no desc' plan_ref_no = fields.Char( readonly=False,string='Indent No' ) tc_no = fields.Char(string="TC No",default=lambda self: self._generate_id(),readonly= True, required= True) fg_name = fields.Many2one('sos_fg', string='FG Name') product_name = fields.Char(related="fg_name.name", string='Product Name',readonly=True) sfg_name = fields.Many2one('sos_sfg', string='SFG Name') planned_qty = fields.Integer(string="Planned Qty") line_ids = fields.One2many('sos_transfer_challan_line', 'tc_id', string="Finished Goods",copy=True, ondelete='cascade') tc_status = fields.Selection([ ('open', 'Open'),('close', 'Closed')], default='open' , string="Status") indent_start_date = fields.Date(string="Indent Started On") indent_target_date = fields.Date(string="Indent Target On") sfg_option = fields.Boolean('Semi-Finished Goods') fg_option = fields.Boolean('Finished Goods', default=True) indent_no = fields.Many2one('sos_fg_plan', string='Ref No') specification_line_ids = fields.One2many('sos_transfer_challan_specification_lines', 'specification_id',copy=True) @api.onchange('fg_name') def _onchange_fg_name(self): if self.fg_name: specifications = self.env['sos_testing_parameters'].search([('fg_name', '=', self.fg_name.id)], limit=1) if not specifications: raise UserError('No specifications found for this Item.') lines = [(5, 0, 0)] for param in specifications.specification_ids: lines.append((0, 0, { 'specification': param.name, 'specification_value': '' })) self.specification_line_ids = lines @api.onchange('sfg_option') def _onchange_sfg_option(self): if self.sfg_option: self.fg_option = False @api.onchange('fg_option') def _onchange_fg_option(self): if self.fg_option: self.sfg_option = False # @api.depends('fg_name') # def _compute_specifications(self): # for record in self: # if record.fg_name: # try: # specifications = self.env['sos_testing_parameters'].search([('fg_name', '=', record.fg_name.id)]) # if not specifications: # continue # lines = [(5, 0, 0)] # for param in specifications.specification_ids: # lines.append((0, 0, { # 'specification': param.id # })) # record.line_ids = lines # except Exception as e: # _logger.error("Error processing specifications for FG %s: %s", record.fg_name.name, str(e)) # continue # def _inverse_specifications(self): # for record in self: # for line in record.line_ids: # line.write({ # 'specification_value': line.specification_value # }) def _generate_id(self): sequence_util = self.env['sos_common_scripts'] return sequence_util.generate_sequence('sos_transfer_challan','TC', 'tc_no') def action_report_tc_btn(self): try: action = self.env.ref("sos_inventory.action_report_tc").report_action(self) return action except ValueError as e: print(f"Failed to find report action: {e}") class TC_Model_Line(models.Model): _name = 'sos_transfer_challan_line' _description = 'tc Lines' tc_id = fields.Many2one('sos_transfer_challan') sfg_option = fields.Boolean(related="tc_id.sfg_option",string='Semi-Finished Goods') fg_option = fields.Boolean(related="tc_id.fg_option",string='Finished Goods', default=True) specification = fields.Many2one('sos_specification', string='Specification Name') specification_value = fields.Char(string="Value") batch_no = fields.Char(string="Batch No") serial_no=fields.Text(string="Serial No",required=True) remarks=fields.Text(string="Remarks") qc_report = fields.Many2one('sos_fir_brr') production_to_qc_transfer_qty=fields.Integer(string="Transfer Quantity", compute="_compute_transfer_qty", store=True, readonly=False) production_to_qc_transfer_by = fields.Many2one('res.users',string='Production to QC By',readonly=True) production_to_qc_transfer_on = fields.Date(string="Production to QC Transfered On") qc_to_stores_transfer_qty=fields.Integer(string="Approved Quantity",related="qc_report.approved_qty") qc_to_stores_rejected_qty=fields.Integer(string="Rejected Quantity",related="qc_report.rejected_qty") qc_to_stores_transfer_by = fields.Many2one('res.users',string='QC to Stores By',related="qc_report.qc_by_name") qc_to_stores_transfer_on = fields.Date(string="QC to Stores Transfered On",related="qc_report.qc_tested_on") stores_received_by = fields.Many2one('res.users',string='Stores Received by',related="qc_report.stores_received_by") stores_received_on = fields.Date(string="Stores Received On",related="qc_report.stores_received_on") sfg_qc_report = fields.Many2one('sos_iqi') sfg_qc_to_stores_transfer_qty=fields.Integer(string="Approved Quantity",related="sfg_qc_report.approved_qty") sfg_qc_to_stores_rejected_qty=fields.Integer(string="Rejected Quantity",related="sfg_qc_report.rejected_qty") sfg_stores_received_by = fields.Many2one('res.users',string='Stores Received by',related="sfg_qc_report.stores_received_by") sfg_stores_received_on = fields.Date(string="Stores Received On",related="sfg_qc_report.stores_received_on") sfg_qc_to_stores_transfer_by = fields.Many2one('res.users',string='QC to Stores By',related="sfg_qc_report.qc_by_name") sfg_qc_to_stores_transfer_on = fields.Date(string="QC to Stores Transfered On",related="sfg_qc_report.qc_tested_on") show_serial = fields.Boolean(default=True) def toggle_serial(self): for record in self: record.show_serial = not record.show_serial @api.model def create(self, vals): """Expand serial number ranges before saving""" if 'serial_no' in vals and vals['serial_no']: vals['show_serial'] = False vals['serial_no'] = self._expand_serial_numbers(vals['serial_no']) return super(TC_Model_Line, self).create(vals) def write(self, vals): """Expand serial number ranges on update""" if 'serial_no' in vals and vals['serial_no']: vals['serial_no'] = self._expand_serial_numbers(vals['serial_no']) return super(TC_Model_Line, self).write(vals) def _expand_serial_numbers(self, serial_text): """Convert serial number ranges into individual serial numbers only if 'to' is found""" lines = serial_text.split("\n") expanded_serials = [] for line in lines: line = line.strip() if "to" in line: # Process only if 'to' is present match = re.match(r"^(.+?)-(\d+)\s+to\s+(.+?)-(\d+)$", line) # Match range format if match: prefix1, start_num, prefix2, end_num = match.groups() if prefix1 == prefix2: # Ensure both prefixes are the same start_num, end_num = int(start_num), int(end_num) for num in range(start_num, end_num + 1): expanded_serials.append(f"{prefix1}-{num:03d}") # Format with leading zeros else: expanded_serials.append(line) # Keep original if prefixes don't match else: expanded_serials.append(line) # Keep as is if not a valid range else: expanded_serials.append(line) # Keep original if 'to' is not present return "\n".join(expanded_serials) # Return expanded serials as multiline text @api.depends("serial_no") def _compute_transfer_qty(self): """Compute Transfer Quantity based on number of serial numbers entered, but allow manual override""" for record in self: if record.serial_no: serial_list = list(filter(None, map(str.strip, record.serial_no.split("\n")))) # Remove empty lines & spaces record.production_to_qc_transfer_qty = len(serial_list) # Count valid serials else: record.production_to_qc_transfer_qty = 0 # If no serials, set to 0 def send_to_qc_btn(self): if self.production_to_qc_transfer_by: return sequence_util = self.env['sos_common_scripts'] plan_ref_no = self.env['sos_fg_plan'].search([('plan_ref_no', '=', self.tc_id.plan_ref_no)], limit=1) brr_model = self.env['sos_fir_brr'] brr_record = brr_model.create({ 'fg_name': self.tc_id.fg_name.id, 'fir_date':self.production_to_qc_transfer_on, 'batch_size':self.production_to_qc_transfer_qty, 'sampling_size':self.production_to_qc_transfer_qty, 'plan_ref_no':plan_ref_no.id if plan_ref_no else '', 'batch_No':self.batch_no, 'fir_no': sequence_util.generate_sequence('sos_fir_brr','BRR', 'fir_no'), }) if brr_record: serial_numbers = self.serial_no.split('\n') # Split serial numbers into a list for serial in filter(None, map(str.strip, serial_numbers)): # Removes empty lines & spaces self.env['sos_brr_serial_no_lines'].create({ 'ref_id': brr_record.id, 'serial_no': serial, # Each row gets a single serial number 'inspection_decision':'PASS' }) # Email part body_html = f"""
Below BRR is waiting for Quality Inspection
""" sequence_util.send_group_email(self.env,"sos_fir_brr", brr_record.id,"deenalaura.m@sosaley.in","BRR Inspection Request",body_html,'sos_inventory.sos_qc_user') # Email part ends self.production_to_qc_transfer_by = self.env.user.id sequence_util = self.env['sos_common_scripts'] if self.tc_id.fg_option and self.tc_id.plan_ref_no: week_number = sequence_util.calculate_week_number( self.tc_id.indent_start_date, self.tc_id.indent_target_date, self.production_to_qc_transfer_on ) field_name = f'actual_week_{week_number}' sos_record = self.env['sos_production_plan'].search([ ('fg_name', '=', self.tc_id.fg_name.id), ('plan_ref_no', '=', self.tc_id.plan_ref_no) ], limit=1) if sos_record: current_value = getattr(sos_record, field_name, 0) sos_record.write({field_name: current_value + self.production_to_qc_transfer_qty}) self.env['sos_transfer_challan_summary_lines'].create({ 'ref_id': sos_record.id, 'name': self.env.user.id, 'date':self.production_to_qc_transfer_on, 'quantity':self.production_to_qc_transfer_qty }) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Moved to QC For Inspection", 'type': 'success', 'sticky': False } } class TC_Model_Spec_Line(models.Model): _name = 'sos_transfer_challan_specification_lines' _description = 'tc Lines' specification_id = fields.Many2one('sos_transfer_challan') specification = fields.Char(string="Specification") specification_value = fields.Char(string="Value")