from odoo import models, fields, api from odoo.exceptions import UserError from datetime import date,datetime from odoo.exceptions import ValidationError from odoo.exceptions import UserError import re class FptcModel(models.Model): _name = 'sos_fptc' _description = 'Finished Product Transfer Challan' _rec_name="fptc_no" _order = 'id desc' plan_ref_no = fields.Many2one('sos_fg_plan', string='Indent No' ) fptc_no = fields.Char(string="FPTC No",default=lambda self: self._generate_id(),readonly= True, required= True) fg_name = fields.Many2many('sos_fg', string='FG Name') sfg_name = fields.Many2one('sos_sfg', string='SFG Name') planned_qty = fields.Integer(string="Planned Qty") line_ids = fields.One2many('sos_fptc_line', 'fptc_id', string="Finished Goods",copy=True, ondelete='cascade') indent_start_date = fields.Date(string="Indent Started On") indent_target_date = fields.Date(string="Indent Target On") stores_to_production_lines= fields.One2many('sos_fptc_store_to_production', 'fptc_id',string="") customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name") deliverables_ref_no = fields.Many2one('sos_deliverables_boq',string="Deliverables/BOQ Reference") production_witness_name = fields.Many2one('res.users') production_witness_image = fields.Image(related="production_witness_name.signature_image",readonly=True) production_witness_approved_on = fields.Datetime(string="Approved On") qc_witness_name = fields.Many2one('res.users') qc_witness_image = fields.Image(related="qc_witness_name.signature_image",readonly=True) qc_witness_approved_on = fields.Datetime(string="Approved On") stores_witness_name = fields.Many2one('res.users') stores_witness_image = fields.Image(related="stores_witness_name.signature_image",readonly=True) stores_witness_approved_on = fields.Datetime(string="Approved On") def action_production_esign_btn(self): sequence_util = self.env['sos_common_scripts'] sequence_util.action_assign_signature( self, 'production_witness_name', 'production_witness_approved_on', 'sos_inventory.sos_production_user' ) def action_stores_esign_btn(self): sequence_util = self.env['sos_common_scripts'] sequence_util.action_assign_signature( self, 'stores_witness_name', 'stores_witness_approved_on', 'sos_inventory.sos_scg_group_user' ) def action_qc_esign_btn(self): sequence_util = self.env['sos_common_scripts'] sequence_util.action_assign_signature( self, 'qc_witness_name', 'qc_witness_approved_on', 'sos_inventory.sos_qc_user' ) def _generate_id(self): sequence_util = self.env['sos_common_scripts'] return sequence_util.generate_sequence('sos_fptc','FPTC', 'fptc_no') def action_report_fptc_btn(self): try: action = self.env.ref("sos_inventory.action_report_fptc").report_action(self) return action except ValueError as e: print(f"Failed to find report action: {e}") class Stores_to_Production(models.Model): _name = 'sos_fptc_store_to_production' _description = 'Stores to Production Lines' fptc_id = fields.Many2one('sos_fptc') fg_name = fields.Many2one('sos_fg', string='FG Name') batch_no = fields.Char(string="Batch No") serial_no=fields.Text(string="Serial No") from_dept = fields.Selection([('production', 'Production'),('stores', 'Stores')], string="From Department",default="stores") stores_to_production_transfer_qty = fields.Integer( string="Transfer Quantity", compute="_compute_transfer_qty", store=True, readonly=False ) stores_to_production_transfer_on=fields.Date(string="Transfered On") available_fg_names = fields.Many2many('sos_fg', compute="_compute_available_fg_names", store=False) send_to_production_btn_display = fields.Boolean(default=True) 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['serial_no'] = self._expand_serial_numbers(vals['serial_no']) return super(Stores_to_Production, 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(Stores_to_Production, 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.stores_to_production_transfer_qty = len(serial_list) # Count valid serials else: record.stores_to_production_transfer_qty = 0 # If no serials, set to 0 @api.depends("fptc_id") def _compute_available_fg_names(self): for record in self: if record.fptc_id: fg_ids = record.fptc_id.fg_name.ids record.available_fg_names = [(6, 0, fg_ids)] # Auto-select FG name only if exactly one exists if len(fg_ids) == 1: record.fg_name = fg_ids[0] # Don't reset if multiple exist; let user select else: record.available_fg_names = [(6, 0, [])] record.fg_name = False # Clear selection only if no options exist @api.onchange('fptc_id') def _onchange_fptc_id(self): if self.fptc_id and len(self.fptc_id.fg_name) == 1: self.fg_name = self.fptc_id.fg_name.id else: self.fg_name = False def send_to_production_btn(self): new_record = self.fptc_id.line_ids new_record.create({ 'fptc_id': self.fptc_id.id, 'batch_no' : self.batch_no, 'serial_no' : self.serial_no, 'fg_name' : self.fg_name.id, 'production_to_qc_transfer_qty':self.stores_to_production_transfer_qty }) self.send_to_production_btn_display = False body_html = f"""

Below FPTC is waiting for your response

""" send_email = self.env['sos_common_scripts'] send_email.send_group_email(self.env,"sos_fptc",self.id,"deenalaura.m@sosaley.in","Transfer Challan Received",body_html,'sos_inventory.sos_production_user') return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Moved to Production", 'type': 'success', 'sticky': False } } class FptcModel_Line(models.Model): _name = 'sos_fptc_line' _description = 'FPTC Lines' fptc_id = fields.Many2one('sos_fptc') fg_name = fields.Many2one('sos_fg',string="FG Name") 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") qc_report = fields.Many2one('sos_fir_brr') fir_qc_report = fields.Many2one('sos_fir') production_to_qc_transfer_qty=fields.Integer(string="Transfer Quantity") production_to_qc_transfer_by = fields.Many2one('res.users',string='Production to QC By') production_to_qc_transfer_on = fields.Date(string="Production to QC Transfered On") qc_to_stores_transfer_qty=fields.Integer(string="Approved Quantity") qc_to_stores_rejected_qty=fields.Integer(string="Rejected Quantity") qc_to_stores_transfer_by = fields.Many2one('res.users',string='QC to Stores By') qc_to_stores_transfer_on = fields.Date(string="QC to Stores Transfered On") stores_received_by = fields.Many2one('res.users',string='Stores Received by') stores_received_on = fields.Date(string="Stores Received On") from_dept = fields.Selection([('production', 'Production'),('stores', 'Stores')], string="From Department",default="production") 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") available_fg_names = fields.Many2many('sos_fg', compute="_compute_available_fg_names", store=False) 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): # """Create multiple rows in `sos_serial_nos` for each serial number in the list""" # res = super(FptcModel_Line, self).create(vals) # Create the main record first # res._update_serial_numbers() # Call helper method to insert serials # return res # def write(self, vals): # """Update related serial numbers in `sos_serial_nos` when this record is modified""" # res = super(FptcModel_Line, self).write(vals) # Update the main record first # if 'serial_no' in vals or 'batch_no' in vals or 'fg_name' in vals: # self._update_serial_numbers() # Call helper method to update serials # return res # def unlink(self): # """Delete associated serial numbers when this record is deleted""" # for record in self: # self.env['sos_serial_nos'].search([ # ('batch_no', '=', record.batch_no), # ('fg_name', '=', record.fg_name.id) # ]).unlink() # return super(FptcModel_Line, self).unlink() # def _update_serial_numbers(self): # """Helper function to update serial numbers in `sos_serial_nos`""" # for record in self: # # Remove old serial numbers before inserting new ones # self.env['sos_serial_nos'].search([ # ('batch_no', '=', record.batch_no), # ('fg_name', '=', record.fg_name.id) # ]).unlink() # if record.serial_no: # serial_numbers = record.serial_no.split('\n') # Split serial numbers into a list # # Create separate rows in sos_serial_nos for each serial number # for serial in filter(None, map(str.strip, serial_numbers)): # Removes empty lines & spaces # self.env['sos_serial_nos'].create({ # 'customer_name': record.fptc_id.customer_name, # 'fg_name': record.fg_name.id, # 'serial_no': serial, # Each row gets a single serial number # 'batch_no': record.batch_no # }) @api.depends("fptc_id") def _compute_available_fg_names(self): for record in self: if record.fptc_id: fg_ids = record.fptc_id.fg_name.ids record.available_fg_names = [(6, 0, fg_ids)] # Auto-select FG name only if exactly one exists if len(fg_ids) == 1: record.fg_name = fg_ids[0] else: record.available_fg_names = [(6, 0, [])] record.fg_name = False # Clear selection only if no options exist def send_to_qc_btn(self): self.production_to_qc_transfer_by = self.env.user.id sequence_util = self.env['sos_common_scripts'] if self.fptc_id.plan_ref_no: week_number = sequence_util.calculate_week_number( self.fptc_id.indent_start_date, self.fptc_id.indent_target_date, date.today() ) field_name = f'actual_week_{week_number}' sos_record = self.env['sos_production_plan'].search([ ('fg_name', '=', self.fg_name.id), ('plan_ref_no', '=', self.fptc_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}) #end production status update return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': "Moved to QC For Inspection", 'type': 'success', 'sticky': False } } class Fptc_Serial_lines(models.Model): _name = 'sos_serial_nos' _description = 'Batch & Serial Numbers' fg_name = fields.Many2one('sos_fg',string="FG Name") customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name") serial_no = fields.Char(string="Serial No") batch_no = fields.Char(string="Batch No") delivery_date = fields.Date(string="Delivery Date")