# -*- coding: utf-8 -*- from odoo import models, fields, api import time from odoo.exceptions import UserError from datetime import datetime class sos__min(models.Model): _name = 'sos_min' _description = 'Material Issue Note' _rec_name = 'min_no' _order = 'id desc' min_no = fields.Char(string="MIN/SIN/FIN No", readonly= True, required= True, default=lambda self: self._generate_id()) min_date = fields.Date(string="MIN/SIN/FIN Date",required=True) mon_no = fields.Many2one('sos_mon',string="MON/SON/FON No",readonly= True) mon_date = fields.Date(string="MON/SON/FON Date") ref_no = fields.Char(string="Purpose") indent_ref_no = fields.Many2one('sos_fg_plan',string="Indent Reference No") stores_approval_name = fields.Many2one('res.users', string='Approved By') stores_approval_image = fields.Image(related="stores_approval_name.signature_image",string='Received by RM/PM Stores In charge Sign',readonly=True) stores_approved_on = fields.Datetime(string="Approved On") manual_received_by = fields.Many2one('res.users', string='Received By') receiver_approval_name = fields.Many2one('res.users', string='Received By') receiver_approval_image = fields.Image(related="receiver_approval_name.signature_image",string='Received by RM/PM Stores In charge Sign',readonly=True) receiver_approved_on = fields.Datetime(string="Acknowledged On") material_option = fields.Boolean('Materials', default=True) sfg_option = fields.Boolean('Semi-Finished Goods') fg_option = fields.Boolean('Finished Goods') order_type = fields.Char(string='order_type',copy=True) line_ids_material = fields.One2many('sos_min_line_material', 'min_id', string="Materials",copy=True) line_ids_sfg = fields.One2many('sos_min_line_sfg', 'min_id', string="Semi-Finished Goods",copy=True) line_ids_fg = fields.One2many('sos_min_line_fg', 'min_id', string="Finished Goods",copy=True) mon_created_by = fields.Many2one('res.users', string='Requested By',readonly= True) reporting_to = fields.Many2one('res.users', string='Reporting To') issued_to = fields.Many2one('res.users', string='Issued To') status = fields.Selection([ ('open', 'Open'),('close', 'Closed')], default='open' , string="Status") auto_load = fields.Selection([ ('sfg', 'SFG'), ('fg', 'FG') ], string='Auto Load Items' ,default=False) auto_load_sfg_items = fields.Many2one('sos_sfg', string='SFG Items') auto_load_fg_items = fields.Many2one('sos_fg', string='FG Items') set_qty = fields.Integer(string="Set Quantity",default=1) receiver_remarks=fields.Text(string="Remarks") deliverables_boq_id = fields.Many2one('sos_deliverables_boq', string="Load From Deliverables/BOQ Id") line_ids_miscellaneous = fields.One2many('sos_min_miscellaneous_items', 'ref_id',string="") scg_manager_approval_name = fields.Many2one('res.users', string='SCG Manager') scg_manager_approval_image = fields.Image(related="scg_manager_approval_name.signature_image",readonly=True) scg_manager_approved_on = fields.Datetime(string="Acknowledged On") partialy_closed = fields.Boolean() @api.onchange('min_date') def _onchange_min_date(self): if self.min_date: minimum_date = datetime.strptime('2025-03-31', '%Y-%m-%d').date() if self.min_date < minimum_date: return { 'warning': { 'title': "Invalid Date", 'message': "The Issue Note Date cannot be before March 31, 2025. Please select a valid date.", }, 'value': { 'min_date': False, }, } @api.onchange('deliverables_boq_id') def _onchange_deliverables_boq_id(self): self.material_option = True self.sfg_option = True self.fg_option = True if self.deliverables_boq_id: self.line_ids_material = [(5, 0, 0)] # Clear existing records self.line_ids_material = [ (0, 0, { 'component_id': line.component_id, # Replace 'field1' with the actual field names 'uom': line.uom, 'material_code': line.material_code, 'quantity': line.quantity # Add other fields to copy here }) for line in self.deliverables_boq_id.line_ids_installation_kit ] self.line_ids_fg = [(5, 0, 0)] # Clear existing records self.line_ids_fg = [ (0, 0, { 'component_id': line.component_id, # Replace 'field1' with the actual field names 'quantity': line.quantity }) for line in self.deliverables_boq_id.line_ids_fg ] self.line_ids_sfg = [(5, 0, 0)] # Clear existing records self.line_ids_sfg = [ (0, 0, { 'component_id': line.component_id, # Replace 'field1' with the actual field names 'quantity': line.quantity # Add other fields to copy here }) for line in self.deliverables_boq_id.line_ids_sfg ] self.line_ids_material = [ (0, 0, { 'uom': line.uom, 'material_code': line.material_code, 'component_id': line.component_id, 'quantity': line.quantity # Add other fields to copy here }) for line in self.deliverables_boq_id.line_ids_material ] self.line_ids_miscellaneous = [ (0, 0, { 'name': line.name, 'quantity': line.quantity }) for line in self.deliverables_boq_id.line_ids_miscellaneous ] @api.onchange('mon_no') def _onchange_mon_no(self): if self.mon_no: self.mon_date = self.mon_no.mon_date self.line_ids_material = [(5, 0, 0)] material_lines = [] for material in self.mon_no.line_ids_material: line_vals = { 'min_id': self.id, 'com_type':material.com_type, 'component_id': material.component_id, 'qp_no': material.qp_no, 'new_component_id':material.new_component_id, 'uom': material.uom, 'quantity': material.quantity } material_lines.append((0, 0, line_vals)) self.line_ids_material = material_lines @api.onchange('set_qty') def _onchange_set_qty(self): if self.auto_load_sfg_items: self.material_option = True self.sfg_option = False self.fg_option = False self.line_ids_material = [(5, 0, 0)] sfg_record = self.env['sos_sfg_bom'].search([('name', '=', self.auto_load_sfg_items.name)], limit=1) if sfg_record: sfg_materials = self.env['sos_sfg_bom_line'].search([('bom_id', '=', sfg_record.id)]) material_lines = [] for material in sfg_materials: line_vals = { 'min_id': self.id, 'component_id': material.primary_component_id.id, 'qp_no': material.primary_component_id.qp_no, 'uom': material.primary_component_id.uom, 'quantity': material.quantity * self.set_qty } material_lines.append((0, 0, line_vals)) self.line_ids_material = material_lines @api.onchange('auto_load') def _onchange_auto_load(self): if self.auto_load == 'sfg': self.auto_load_fg_items = False # Clear FG field domain = [('id', 'in', self.env['sos_sfg'].search([]).ids)] return {'domain': {'auto_load_sfg_items': domain}} elif self.auto_load == 'fg': self.auto_load_sfg_items = False # Clear SFG field domain = [('id', 'in', self.env['sos_fg'].search([]).ids)] return {'domain': {'auto_load_fg_items': domain}} else: return {'domain': {'auto_load_sfg_items': [], 'auto_load_fg_items': []}} @api.onchange('auto_load_fg_items') def _onchange_auto_load_fg_items(self): if self.auto_load_fg_items: self.material_option = True self.sfg_option = True self.fg_option = False self.line_ids_material = [(5, 0, 0)] self.line_ids_sfg = [(5, 0, 0)] fg_record = self.env['sos_fg_bom'].search([('fg_name', '=', self.auto_load_fg_items.name),('is_primary', '=',True)], limit=1) if fg_record: sfg_lines = [] for line_id in fg_record.fg_bom_line_ids: line_vals = { 'min_id': self.id, 'component_id': line_id.sfg_bom_id.name.id, 'qp_no': line_id.sfg_bom_id.name.qp_no, 'quantity': 1 } sfg_lines.append((0, 0, line_vals)) self.line_ids_sfg = sfg_lines material_lines = [] for material in fg_record.sfg_bom_line_ids: line_vals = { 'min_id': self.id, 'component_id': material.primary_component_id.id, 'qp_no': material.primary_component_id.qp_no, 'uom': material.primary_component_id.uom, 'quantity': material.quantity } material_lines.append((0, 0, line_vals)) self.line_ids_material = material_lines else: raise UserError("BOM Not Found") @api.onchange('auto_load_sfg_items') def _onchange_auto_load_sfg_items(self): if self.auto_load_sfg_items: self.material_option = True self.sfg_option = False self.fg_option = False self.line_ids_material = [(5, 0, 0)] sfg_record = self.env['sos_sfg_bom'].sudo().search([('name', '=', self.auto_load_sfg_items.name)], limit=1) if sfg_record: sfg_materials = self.env['sos_sfg_bom_line'].sudo().search([('bom_id', '=', sfg_record.id)]) material_lines = [] for material in sfg_materials: line_vals = { 'min_id': self.id, 'component_id': material.primary_component_id.id, 'qp_no': material.primary_component_id.qp_no, 'uom': material.primary_component_id.uom, 'quantity': material.quantity } material_lines.append((0, 0, line_vals)) self.line_ids_material = material_lines else: raise UserError("No matching BOM found for the selected SFG item") @api.onchange('material_option', 'sfg_option', 'fg_option') def _onchange_material_option(self): options = [] if self.material_option: options.append("Materials") if self.sfg_option: options.append("SFG") if self.fg_option: options.append("FG") self.order_type = ",".join(options) def _generate_id(self): sequence_util = self.env['sos_common_scripts'] return sequence_util.generate_sequence('sos_min','MIN', 'min_no') def action_report_min_btn(self): try: action = self.env.ref("sos_inventory.action_report_min").report_action(self) return action except ValueError as e: print(f"Failed to find report action: {e}") def action_scg_manager_esign_btn(self): for record in self: all_material_ok = all(line.quantity <= line.issued_quantity for line in record.line_ids_material) if record.line_ids_material else True all_sfg_ok = all(line.quantity <= line.issued_quantity for line in record.line_ids_sfg) if record.line_ids_sfg else True all_fg_ok = all(line.quantity <= line.issued_quantity for line in record.line_ids_fg) if record.line_ids_fg else True if all_material_ok and all_sfg_ok and all_fg_ok: record.partialy_closed = False record.mon_no.status = "close" else: record.mon_no.status = "close" record.partialy_closed = True sequence_util = self.env['sos_common_scripts'] sequence_util.action_assign_signature( self, 'scg_manager_approval_name', 'scg_manager_approved_on', 'sos_inventory.sos_scg_group_manager' ) def action_stores_esign_btn(self): sequence_util = self.env['sos_common_scripts'] sequence_util.action_assign_signature( self, 'stores_approval_name', 'stores_approved_on', 'sos_inventory.sos_scg_group_user' ) # Email part body_html = f"""
Below Issue Note is waiting for your approval
""" send_email = self.env['sos_common_scripts'] send_email.send_group_email(self.env,'sos_min',self.id,"deenalaura.m@sosaley.in","Issue Note Approval Request",body_html,'sos_inventory.sos_scg_group_manager') # Email part ends if self.manual_received_by: # Email part body_html = f"""Below Issue Note is closed and waiting for your Acknowledgement
""" send_email = self.env['sos_common_scripts'] send_email.send_direct_email(self.env,"sos_min",self.id,self.manual_received_by.login,"Issue Note Ack",body_html) # Email part ends else: # Email part body_html = f"""Below Issue Note is closed and waiting for your Acknowledgement
""" send_email = self.env['sos_common_scripts'] send_email.send_direct_email(self.env,"sos_min",self.id,self.mon_created_by.login,"Issue Note Ack",body_html) # Email part ends def action_receiver_esign_btn(self): self.mon_no.status = "close" self.status="close" sequence_util = self.env['sos_common_scripts'] return sequence_util.action_assign_signature( self, 'receiver_approval_name', 'receiver_approved_on' ) @api.model def write(self, vals): record = super(sos__min, self).write(vals) s_no = 0 for line in self.line_ids_material: s_no += 1 line.s_no = s_no return record @api.model def create(self, vals): record = super(sos__min, self).create(vals) s_no = 0 for line in record.line_ids_material: s_no += 1 line.s_no = s_no return record class Min_Line_Material(models.Model): _name = 'sos_min_line_material' _description = 'Issue Note Material Lines' s_no = fields.Integer(string="S.No",readonly=True,default=1) min_id = fields.Many2one('sos_min', string="Materials", ondelete="cascade") qp_no = fields.Char(string="QP No") com_type = fields.Selection([('exits', 'In-stock'),('new', 'New')], string="Availability", default='exits') component_id = fields.Many2one('sos_material', string="Part No") material_code = fields.Char(string="Material Code") inhand_stock_qty=fields.Float(string="Inhand Qty",related="component_id.inhand_stock_qty") new_component_id = fields.Char(string="New Part No") quantity = fields.Float(string="Requested Qty") uom = fields.Selection([('meters', 'Meters'), ('Packs', 'Packs'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram')], string="Uom") issued_quantity = fields.Integer(string="Issued Qty",readonly=False) current_issued_quantity = fields.Integer(string="Current Issued Qty", store=False, default=0) grn_dc_no = fields.Char(string="GRN No / DC No") issued_date = fields.Datetime(string="Issued On") comments = fields.Text(string="Comments") @api.onchange('component_id') def _onchange_component_id(self): """Update material_code when component_id changes.""" for record in self: if record.component_id: record.material_code = record.component_id.material_code else: record.material_code = False @api.onchange('material_code') def _onchange_material_code(self): """Update component_id when material_code changes.""" for record in self: if record.material_code: # Search for a sos_material record with the matching material_code material = self.env['sos_material'].search( [('material_code', '=', record.material_code)], limit=1 ) if material: record.component_id = material else: # Optionally clear component_id if no matching material is found record.component_id = False else: record.component_id = False @api.model def create(self, vals): result = super(Min_Line_Material, self).create(vals) current_issued_quantity = vals.get('current_issued_quantity', 0) if self.env.context.get('from_script') in [None, False]: for record in result: component = record.component_id # Access the component record (Many2one) quantity = record.quantity current_issued_quantity = vals.get('current_issued_quantity', record.current_issued_quantity) if current_issued_quantity > 0: if component: record.write({ 'issued_quantity': record.issued_quantity + current_issued_quantity }) # Update the component's inhand_stock_qty component.write({ 'inhand_stock_qty': component.inhand_stock_qty - current_issued_quantity }) if record.min_id.indent_ref_no: component.write({ 'blocked_qty': max(0, component.blocked_qty - current_issued_quantity) }) component.line_ids_out.create({ 'ref_id': component.id, 'component_id': component.id, 'quantity': current_issued_quantity, 'min_no': record.min_id.id, 'action': 'out' }) if current_issued_quantity > 0: result.with_context(prevent_recursion=True).write({'current_issued_quantity': 0}) return result @api.onchange('quantity') def _compute_current_issued_quantity(self): for record in self: record.current_issued_quantity = record.quantity - record.issued_quantity def write(self, vals): for record in self: if 'component_id' in vals: component_idd = vals.get('component_id') component = self.env['sos_material'].search([('id', '=', component_idd)]) else: component = record.component_id quantity = record.quantity current_issued_quantity = vals.get('current_issued_quantity', record.current_issued_quantity) if current_issued_quantity > 0: if component: vals['issued_quantity'] = record.issued_quantity + current_issued_quantity component.write({ 'inhand_stock_qty': component.inhand_stock_qty - current_issued_quantity }) if record.min_id.indent_ref_no: component.write({ 'blocked_qty': max(0, component.blocked_qty - current_issued_quantity) }) component.line_ids_out.create({ 'ref_id': component.id, 'component_id': component.id, 'quantity': current_issued_quantity, 'min_no': record.min_id.id, 'action': 'out' }) result = super(Min_Line_Material, self).write(vals) if current_issued_quantity > 0: self.with_context(prevent_recursion=True).write({'current_issued_quantity': 0}) return result class Min_Line_SFG(models.Model): _name = 'sos_min_line_sfg' _description = 'Issue Note SFG Lines' min_id = fields.Many2one('sos_min', string="SFG", ondelete="cascade") qp_no = fields.Char(string="QP No") com_type = fields.Selection([('exits', 'In-stock'),('new', 'New')], string="Availability", default='exits') component_id = fields.Many2one('sos_sfg', string="SFG Name", ondelete='cascade') inhand_stock_qty=fields.Integer(string="Inhand Qty",related="component_id.inhand_stock_qty") new_component_id = fields.Char(string="New Part No") quantity = fields.Integer(string="Requested Quantity") issued_quantity = fields.Integer(string="Issued Quantity",readonly=True) current_issued_quantity = fields.Integer(string="Current Issued Quantity", store=False, default=0) grn_dc_no = fields.Char(string="GRN No / DC No") issued_date = fields.Datetime(string="Issued Date") comments = fields.Text(string="Comments") @api.model def create(self, vals): result = super(Min_Line_SFG, self).create(vals) current_issued_quantity = vals.get('current_issued_quantity', 0) if self.env.context.get('from_script') in [None, False]: for record in result: component = record.component_id # Access the component record (Many2one) quantity = record.quantity current_issued_quantity = vals.get('current_issued_quantity', record.current_issued_quantity) if current_issued_quantity > 0: if component: record.write({ 'issued_quantity': record.issued_quantity + current_issued_quantity }) # Update the component's inhand_stock_qty component.write({ 'inhand_stock_qty': component.inhand_stock_qty - current_issued_quantity }) if record.min_id.indent_ref_no: component.write({ 'blocked_qty': max(0, component.blocked_qty - current_issued_quantity) }) component.line_ids_out.create({ 'ref_id': component.id, # Using the component's ID 'component_id': component.id, # Component ID from the record 'quantity': current_issued_quantity, 'min_no': record.min_id.id, # Reference MON No. 'action': 'out' }) if current_issued_quantity > 0: result.with_context(prevent_recursion=True).write({'current_issued_quantity': 0}) return result @api.onchange('quantity') def _compute_current_issued_quantity(self): for record in self: record.current_issued_quantity = record.quantity - record.issued_quantity def write(self, vals): for record in self: component = record.component_id # Access the component record (Many2one) quantity = record.quantity current_issued_quantity = vals.get('current_issued_quantity', record.current_issued_quantity) if current_issued_quantity > 0: if component: vals['issued_quantity'] = record.issued_quantity + current_issued_quantity component.write({ 'inhand_stock_qty': component.inhand_stock_qty - current_issued_quantity }) if record.min_id.indent_ref_no: component.write({ 'blocked_qty': max(0, component.blocked_qty - current_issued_quantity) }) component.line_ids_out.create({ 'ref_id': component.id, # Using the component's ID 'component_id': component.id, # Component ID from the record 'quantity': current_issued_quantity, 'min_no': record.min_id.id, # MON No. 'action': 'out' }) # Call the original write method to apply the changes result = super(Min_Line_SFG, self).write(vals) # Now reset current_issued_quantity after the write operation if current_issued_quantity > 0: self.with_context(prevent_recursion=True).write({'current_issued_quantity': 0}) return result class Min_Line_FG(models.Model): _name = 'sos_min_line_fg' _description = 'MIN FG Lines' min_id = fields.Many2one('sos_min', string="FG", ondelete="cascade") qp_no = fields.Char(string="QP No") com_type = fields.Selection([('exits', 'In-stock'),('new', 'New')], string="Availability", default='exits') component_id = fields.Many2one('sos_fg', string="FG Name", ondelete='cascade') new_component_id = fields.Char(string="New Part No") quantity = fields.Integer(string="Requested Quantity") issued_quantity = fields.Integer(string="Issued Quantity",readonly=True) current_issued_quantity = fields.Integer(string="Current Issued Quantity", store=False, default=0) grn_dc_no = fields.Char(string="GRN No / DC No") issued_date = fields.Datetime(string="Issued Date") comments = fields.Text(string="Comments") @api.model def create(self, vals): result = super(Min_Line_FG, self).create(vals) current_issued_quantity = vals.get('current_issued_quantity', 0) if self.env.context.get('from_script') in [None, False]: for record in result: component = record.component_id # Access the component record (Many2one) quantity = record.quantity current_issued_quantity = vals.get('current_issued_quantity', record.current_issued_quantity) if current_issued_quantity > 0: if component: record.write({ 'issued_quantity': record.issued_quantity + current_issued_quantity }) # Update the component's inhand_stock_qty component.write({ 'inhand_stock_qty': component.inhand_stock_qty - current_issued_quantity }) component.line_ids_out.create({ 'ref_id': component.id, # Using the component's ID 'component_id': component.id, # Component ID from the record 'quantity': current_issued_quantity, 'min_no': record.min_id.id, # Reference MON No. 'action': 'out' }) if current_issued_quantity > 0: result.with_context(prevent_recursion=True).write({'current_issued_quantity': 0}) return result @api.onchange('quantity') def _compute_current_issued_quantity(self): for record in self: record.current_issued_quantity = record.quantity - record.issued_quantity def write(self, vals): for record in self: component = record.component_id # Access the component record (Many2one) quantity = record.quantity current_issued_quantity = vals.get('current_issued_quantity', record.current_issued_quantity) if current_issued_quantity > 0: if component: vals['issued_quantity'] = record.issued_quantity + current_issued_quantity component.write({ 'inhand_stock_qty': component.inhand_stock_qty - current_issued_quantity }) component.line_ids_out.create({ 'ref_id': component.id, # Using the component's ID 'component_id': component.id, # Component ID from the record 'quantity': current_issued_quantity, 'min_no': record.min_id.id, # MON No. 'action': 'out' }) result = super(Min_Line_FG, self).write(vals) if current_issued_quantity > 0: self.with_context(prevent_recursion=True).write({'current_issued_quantity': 0}) return result class sos_min_MiscellaneousCost(models.Model): _name = 'sos_min_miscellaneous_items' _description = 'Miscellaneous Items' ref_id = fields.Many2one('sos_min', string="Miscellaneous", ondelete="cascade") name = fields.Char(string='Name') quantity = fields.Integer(string="Quantity") remarks = fields.Text(string="Remarks")