# -*- coding: utf-8 -*- from odoo import models, fields, api class SOS_FG(models.Model): _name = 'sos_fg' _description = 'Finished Goods Master' _order = 'fg_code asc' fg_code = fields.Char(string="Part No", required= True) code_no = fields.Char(string="Code") name = fields.Char(string="Product Name", required= True) display_name = fields.Char(string="Display Name", required= True) qp_no = fields.Char(string="QP No") hsn_code = fields.Char(string="HSN Code") opening_bal_qty = fields.Float(string="Opening Balance Qty") uom = fields.Selection([ ("Nos", "Nos"),('set', 'set'), ('Packs', 'Packs')], default="Nos" , string="UOM") currency_id = fields.Many2one('res.currency', string='Currency') minimum_stock_qty = fields.Integer(string="Minimum Stock Qty") minimum_order_qty = fields.Integer(string="Minimum Order Qty") inhand_stock_qty = fields.Integer(string="In-Hand Stock Qty") inhand_stock_val = fields.Monetary(string="In-Hand Stock Value", currency_field='currency_id',compute='_compute_stock_val', store=True) in_transit_stock_qty = fields.Integer(string="In-Transit Qty") in_transit_stock_val = fields.Monetary(string="In-Transit Stock Value", currency_field='currency_id') received_qty = fields.Integer(string="Last Received Qty") received_stock_val = fields.Monetary(string="Last Received Stock Value", currency_field='currency_id') cancelled_qty = fields.Integer(string="Canceled Qty") issued_qty = fields.Integer(string="Issue to Dispatch Qty") issued_val = fields.Monetary(string="Issue to Dispatch Value", currency_field='currency_id') defect_qty = fields.Integer(string="Defect Qty") defect_val = fields.Monetary(string="Defect Value", currency_field='currency_id') blocked_qty = fields.Integer(string="Blocked Qty") order_qty = fields.Integer(string="Required/Order Qty") unit_price = fields.Monetary(string="Unit Price", currency_field='currency_id') msp = fields.Monetary(string="Max Selling Price", currency_field='currency_id') location = fields.Char(string="Location in Stores") image = fields.Image(string="Upload Image",max_height=100,max_width=100) description = fields.Text(string="Remarks") line_ids_in = fields.One2many( 'sos_fg_transaction_history', 'ref_id', string="FG History - In", domain=[('action', '=', 'in')] ) line_ids_out = fields.One2many( 'sos_fg_transaction_history', 'ref_id', string="FG History - Out", domain=[('action', '=', 'out')] ) fg_type = fields.Selection( [ ('BHMS', 'BHMS'), ('LV-BMS', 'BMS-LV'), ('HV-BMS', 'BMS-HV'), ('SBMS', 'SBMS'), ('Motor Controller', 'Motor Controller'), ('Health Care', 'Health Care') ], default='BHMS', string="Type", required=True ) sub_type = fields.Many2one( 'sos.fg.subtype', string="Sub Type", domain="[('fg_type', '=', fg_type)]" ) sub_type = fields.Selection( [ ('1.2V', 'BHMS 1.2V'), ('2V', 'BHMS 2V'), ('12V', 'BHMS 12V'), ('48V', 'BHMS 48V'), ('100A', 'BMS-LV 100A'), ('40A', 'BMS-LV 40A'), ('100A-HV', 'BMS-HV 100A'), ('250A-HV', 'BMS-HV 250A'), ('15S Slave-HV', 'BMS-HV 15S Slave'), ('SBMS55A', 'SBMS 55A'), ('250W', 'MC 250W'), ('HeartTarang', 'HeartTarang') ], string="Sub Type", required=True ) _sql_constraints = [ ('fg_code_unique', 'UNIQUE(fg_code)', 'The Product Code No must be unique!') ] @api.onchange('fg_type') def _onchange_fg_type(self): self.sub_type = False if self.fg_type == 'BHMS': return {'domain': {'sub_type': [('1.2V', 'BHMS 1.2V'),('2V', 'BHMS 2V'),('12V', 'BHMS 12V'),('48V', 'BHMS 48V')]}} elif self.fg_type == 'LV-BMS': return {'domain': {'sub_type': [('100A', 'BMS-LV 100A'),('40A', 'BMS-LV 40A')]}} elif self.fg_type == 'HV-BMS': return {'domain': {'sub_type': [('100A-HV', 'BMS-HV 100A'),('250A-HV', 'BMS-HV 250A')]}} elif self.fg_type == 'Motor Controller': return {'domain': {'sub_type': [('250W', 'MC 250W')]}} elif self.fg_type == 'SBMS': return {'domain': {'sub_type': [('SBMS55A', 'SBMS 55A')]}} elif self.fg_type == 'Health Care': return {'domain': {'sub_type': [('HeartTarang', 'HeartTarang') ]}} else: return {'domain': {'sub_type': []}} @api.onchange('sub_type') def onchange_sub_type(self): if self.sub_type: prefix_map = { "12V": "H1", "1.2V": "H2", "2V": "H3", "48V": "H4", "100A": "M1", "40A": "M2", "250W": "C1", "HeartTarang": "T1" } prefix = prefix_map.get(self.sub_type, "") self.code_no = prefix if prefix else False def action_naming_culture(self): print("Test") @api.depends('unit_price','inhand_stock_qty') def _compute_stock_val(self): for val in self: val.inhand_stock_val = val.unit_price * val.inhand_stock_qty def action_calculate_msp(self): part_no_quantities = {} sfg_stores_model = self.env['sos_sfg'] sfg_line_model = self.env['sos_sfg_plan_msp'] material_line_model = self.env['sos_material_plan_msp'] product_bom = self.env['sos_fg_bom'].search([ ('fg_name', '=', self.id), ('is_primary', '=', True) ], limit=1) if product_bom: existing_sfg_lines = sfg_line_model.search([('fg_name', '=', self.id)]) if existing_sfg_lines: existing_sfg_lines.unlink() existing_material_lines = material_line_model.search([('fg_name', '=', self.id)]) if existing_material_lines: existing_material_lines.unlink() lines = self.env['sos_fg_bom_line'].search([('bom_id', '=', product_bom.id)]) for sfg_line in lines: sfg_name = sfg_line.sfg_bom_id.name.id # Use the ID for Many2one relationship req_sfg_qty = sfg_line.quantity sfg_line_model.create({ 'fg_name':self.id, 'sfg_name': sfg_name, 'required_qty': req_sfg_qty, 'unit_price':sfg_line.sfg_bom_id.name.unit_price }) # sfg_bom = self.env['sos_sfg_bom'].search([ # ('name', '=', sfg_line.sfg_bom_id.name.id) # ]) # sfg_lines = self.env['sos_sfg_bom_line'].search([('bom_id', '=', sfg_bom.id)]) # for sfg_line in sfg_lines: # for component_id in sfg_line.primary_component_id: # req_qty = sfg_line.quantity # # If part_no is already in the dictionary, increase the req_qty # if component_id.part_no in part_no_quantities: # part_no_quantities[component_id.part_no]['req_qty'] += req_qty # else: # part_no_quantities[component_id.part_no] = { # 'req_qty': req_qty, # 'part_no_id':component_id.id, # 'approx_price':component_id.unit_price # } # for part_no, data in part_no_quantities.items(): # req_qty = data['req_qty'] # pick_line = self.env['sos_material_plan_msp'].search([ # ('fg_name', '=', self.id), # ('material_name', '=', data['part_no_id']) # ],limit=1) # if pick_line: # pick_line.write({ # 'required_qty': pick_line.required_qty + req_qty, # }) # else: # material_line_model.create({ # 'material_name': data['part_no_id'], # 'fg_name': self.id, # 'required_qty': req_qty, # 'approx_price':data['approx_price'] # }) material_lines = self.env['sos_sfg_bom_line'].search([('fg_bom_id', '=', product_bom.id)]) for material_line in material_lines: req_qty = material_line.quantity material_line_model.create({ 'fg_name':self.id, 'material_name': material_line.primary_component_id.id, 'required_qty': req_qty, 'approx_price':material_line.primary_component_id.unit_price }) return { 'type': 'ir.actions.act_window', 'name': 'Calculate MSP', 'res_model': 'sos_msp_wizard', 'view_mode': 'form', 'target': 'new', 'context': { 'fg_name': self.id, 'old_msp':self.msp }, } class SOS_FG_Line(models.Model): _name = 'sos_fg_transaction_history' _description = 'FG Lines' _order = 'date desc' ref_id = fields.Many2one('sos_fg', string="FG", ondelete="cascade") component_id = fields.Many2one('sos_fg', string="Part No") action = fields.Selection([('in', 'IN'),('out', 'OUT')], string="Action", default='in') quantity = fields.Integer(string="Quantity") currency_id = fields.Many2one('res.currency', string='Currency') unit_price = fields.Monetary(string="Unit Price", currency_field='currency_id') date = fields.Datetime(string="Date", default=fields.Datetime.now) min_no = fields.Many2one('sos_min', string="Issue Ref No") fir_no = fields.Many2one('sos_fir_brr', string="BRR No") return_fir_no = fields.Many2one('sos_return_fir', string="Return - BRR No") mrn_no = fields.Many2one('sos_mrn', string="Material Return Ref No") class SOS_Material_Plan_MSP(models.Model): _name = 'sos_material_plan_msp' _description = 'Material Plan Lines For MSP' material_name = fields.Many2one('sos_material',string="Material Name") fg_name = fields.Many2one('sos_fg',string="FG Name") company_id = fields.Many2one('res.company', store=True, copy=False, string="Company", default=lambda self: self.env.user.company_id.id) currency_id = fields.Many2one('res.currency', string="Currency", related='company_id.currency_id', default=lambda self: self.env.user.company_id.currency_id.id) required_qty = fields.Integer(string='Required Qty') approx_price = fields.Monetary(currency_field='currency_id', string='Unit Price') total_cost = fields.Monetary( currency_field='currency_id', string="Total Cost", compute="_compute_total_cost", store=True ) margin = fields.Float(string='Margin (%)', default="10", store=True) final_cost = fields.Monetary( currency_field='currency_id', string="Final Cost", compute="_compute_final_cost", inverse="_set_final_cost", store=True ) @api.depends('required_qty', 'approx_price') def _compute_total_cost(self): for record in self: record.total_cost = record.required_qty * record.approx_price @api.depends('total_cost', 'margin') def _compute_final_cost(self): for record in self: record.final_cost = record.total_cost + (record.total_cost * (record.margin / 100)) def _set_final_cost(self): for record in self: if record.total_cost > 0: record.margin = (record.final_cost / record.total_cost) * 100 @api.onchange('material_name') def _onchange_material_name(self): if self.material_name: self.approx_price = self.material_name.unit_price else: self.approx_price = 0.0 class SOS_SFG_Plan_MSP(models.Model): _name = 'sos_sfg_plan_msp' _description = 'SFG Plan Lines For MSP' sfg_name = fields.Many2one('sos_sfg',string="SFG Name") category = fields.Selection([ ('pcba', 'PCB Board'),('cables', 'Cables & Connectors'),('others', 'Others')], default='pcba' , string="Category", related="sfg_name.category" ) fg_name = fields.Many2one('sos_fg',string="FG Name") company_id = fields.Many2one('res.company', store=True, copy=False, string="Company", default=lambda self: self.env.user.company_id.id) currency_id = fields.Many2one('res.currency', string="Currency", related='company_id.currency_id', default=lambda self: self.env.user.company_id.currency_id.id) required_qty = fields.Integer(string='Required Qty') unit_price = fields.Monetary(currency_field='currency_id',string="Unit Price") total_cost = fields.Monetary( currency_field='currency_id', string="Total Cost", compute="_compute_total_cost", store=True ) margin = fields.Float(string='Margin (%)', compute="_compute_margin", store=True) final_cost = fields.Monetary( currency_field='currency_id', string="Final Cost", compute="_compute_final_cost", inverse="_set_final_cost", store=True ) @api.depends('required_qty', 'unit_price') def _compute_total_cost(self): for record in self: record.total_cost = record.required_qty * record.unit_price @api.depends('total_cost', 'margin') def _compute_final_cost(self): for record in self: record.final_cost = record.total_cost + (record.total_cost * (record.margin / 100)) def _set_final_cost(self): for record in self: if record.total_cost > 0: record.margin = (record.final_cost / record.total_cost) * 100 @api.depends('category') def _compute_margin(self): for record in self: if record.category == 'pcba': record.margin = 42.85 # Default value for PCBA else: record.margin = 15 # Default value for Other categories class SosFgSubType(models.Model): _name = 'sos_fg_subtype' _description = 'FG Sub Type Master' name = fields.Char(required=True, string="Sub Type Name") # E.g., BHMS 1.2V code = fields.Char(string="Code") # Optional fg_type = fields.Selection([ ('BHMS', 'BHMS'), ('LV-BMS', 'BMS-LV'), ('HV-BMS', 'BMS-HV'), ('SBMS', 'SBMS'), ('Motor Controller', 'Motor Controller'), ('Health Care', 'Health Care') ], required=True, string="Main FG Type")