from odoo import api, fields, models, _ import logging _logger = logging.getLogger(__name__) from odoo.exceptions import ValidationError from datetime import datetime,date,timedelta from odoo.exceptions import UserError import io import xlsxwriter from odoo.tools.misc import xlsxwriter import base64 class SOS_FG_Plan(models.Model): _name = 'sos_fg_plan' _description = 'FG Plan' _rec_name = 'plan_ref_no' plan_ref_no = fields.Char( readonly=True, required=True, string='Plan ID', default=lambda self: self._generate_id() ) line_ids = fields.One2many('sos_fg_plan_line', 'plan_id', string='Lines', ondelete='cascade') sfg_line_ids = fields.One2many('sos_sfg_plan_line', 'plan_id', string='SFG Lines', ondelete='cascade') material_line_ids = fields.One2many('sos_material_plan_line', 'plan_id', string='Lines', ondelete='cascade') approved_by = fields.Many2one('res.users', string='Approved By') approval_image = fields.Image(related="approved_by.signature_image",string='Top Management Approval Sign',readonly=True) approved_on = fields.Datetime(string="Approved On") prepared_by = fields.Many2one('res.users', string='Planned By') prepared_image = fields.Image(related="prepared_by.signature_image",string='Prepared By Sign',readonly=True) prepared_on = fields.Datetime(string="Planned On") blowup = fields.Boolean(string="Blowup Done",default=False) target_date = fields.Date(string="Target Date",required=True) indent_start_date = fields.Date(string="Indent Start Date", default=lambda self: fields.Date.context_today(self)) indent_status = fields.Selection([('open', 'Open'), ('cancel', 'Cancel'), ('hold', 'Hold'), ('close', 'Closed')], string='Indent Status', default="open") hold_cancel_reason = fields.Text(string='Hold/Cancel Reason') hold_cancel_by = fields.Many2one('res.users', string='Hold & Cancelled By') 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) estimated_assembling_charges = fields.Monetary(compute='_compute_assembling_charges', string="SFG Assembling Charges", currency_field='currency_id', readonly=True) estimated_material_cost = fields.Monetary(compute='_compute_material_charges', string="Material Procurement Cost", currency_field='currency_id', readonly=True) other_charges = fields.Monetary(string="Transport & Other Charges",currency_field='currency_id') overall_total = fields.Monetary(compute='_compute_overall_total',string ="Overall Estimated Cost",currency_field='currency_id') tax_value = fields.Monetary(string="Tax(18%)") @api.depends('estimated_assembling_charges', 'estimated_material_cost', 'other_charges') def _compute_overall_total(self): for record in self: overall_total = ( (record.estimated_assembling_charges or 0) + (record.estimated_material_cost or 0) + (record.other_charges or 0) ) with_tax = overall_total * (1.18) record.tax_value = with_tax - overall_total record.overall_total = with_tax @api.depends('sfg_line_ids.total_assembling_cost') def _compute_assembling_charges(self): for record in self: record.estimated_assembling_charges = round(sum(line.total_assembling_cost for line in record.sfg_line_ids), 2) @api.depends('material_line_ids.total_approx_price') def _compute_material_charges(self): for record in self: record.estimated_material_cost = round(sum(line.total_approx_price for line in record.material_line_ids), 2) def open_reason_wizard(self): return { 'type': 'ir.actions.act_window', 'res_model': 'hold_cancel_reason_wizard', 'name':'Indent Status Change', 'view_mode': 'form', 'target': 'new', 'context': { 'default_model_id': self.id, } } @api.onchange('target_date', 'indent_start_date') def _onchange_dates(self): for record in self: if record.target_date: max_target_date = date.today() + timedelta(days=90) if record.target_date > max_target_date: raise UserError("Target Date cannot exceed 90 days from the Indent Start Date(Today)") @api.model def default_get(self, fields_list): res = super(SOS_FG_Plan, self).default_get(fields_list) if 'line_ids' in fields_list and 'line_ids' not in res: default_lines = self._get_default_lines('sos_fg','fg_name') res['line_ids'] = [(0, 0, line) for line in default_lines] if 'sfg_line_ids' in fields_list and 'sfg_line_ids' not in res: default_lines = self._get_default_lines('sos_sfg','sfg_name') res['sfg_line_ids'] = [(0, 0, line) for line in default_lines] if 'material_line_ids' in fields_list and 'material_line_ids' not in res: default_lines = self._get_default_lines('sos_material','material_name') res['material_line_ids'] = [(0, 0, line) for line in default_lines] return res def _generate_id(self): model_name = 'sos_fg_plan' form_name = 'INDENT' field_name = 'plan_ref_no' today = fields.Date.today() year_start = today.year if today.month > 3 else today.year - 1 year_end = year_start + 1 fy = f"{year_start % 100}-{year_end % 100}" month = today.strftime('%m') base_sequence_prefix = f"SOS/{form_name}/{fy}/{month}/" records = self.env[model_name].sudo().search( [(field_name, 'like', f"{base_sequence_prefix}%")], order=f"{field_name} desc", limit=1 ) if records: last_sequence = records[0][field_name] last_suffix = last_sequence.split('/')[-1] if last_suffix.isdigit(): new_suffix = f"{last_suffix}a" else: base_num = last_suffix[:-1] last_alpha = last_suffix[-1] next_alpha = chr(ord(last_alpha) + 1) if last_alpha < 'z' else 'a' new_suffix = f"{base_num}{next_alpha}" else: new_suffix = '016' return f"{base_sequence_prefix}{new_suffix}" def _get_default_lines(self,model,column): products = self.env[model].search([]) default_lines = [] for product in products: if (product.order_qty + product.minimum_stock_qty) > product.inhand_stock_qty: to_be_produce = (product.order_qty + product.minimum_stock_qty) - (product.inhand_stock_qty + product.in_transit_stock_qty) if to_be_produce > product.minimum_order_qty: final_qty = to_be_produce else: final_qty = product.minimum_order_qty default_line = { column: product.id, 'inhand_qty': product.inhand_stock_qty, 'in_transit_stock_qty': product.in_transit_stock_qty, 'minimum_stock_qty': product.minimum_stock_qty, 'required_qty': product.order_qty, 'minimum_order_qty':product.minimum_order_qty, 'approved_cnt':final_qty, 'actual_required_qty':to_be_produce } if column == "material_name": default_line['approx_price'] = product.unit_price elif column == "sfg_name": default_line['assembling_cost'] = product.assembling_charges default_lines.append(default_line) return default_lines def send_indent_plan_email(self, email_ids): template = self.env.ref('sos_inventory.send_indent_plan_email_template') if template: template.email_to = ','.join(email_ids) template.send_mail(self.id, force_send=True) def get_unique_emails(self): group_refs = [ 'sos_inventory.sos_scg_group_user', 'sos_inventory.sos_scg_group_manager', 'sos_inventory.sos_finance_user', 'sos_inventory.sos_management_user', 'sos_inventory.sos_qc_user', 'sos_inventory.sos_qa_user', 'sos_inventory.sos_production_user' ] group_ids = [self.env.ref(group_ref).id for group_ref in group_refs] users = self.env['res.users'].search([('groups_id', 'in', group_ids)]) emails = list(set(users.mapped('email'))) return emails def action_export_final(self): output = io.BytesIO() workbook = xlsxwriter.Workbook(output, {'in_memory': True}) # Define custom headers for each sheet line_headers = { 'fg_name': 'FG Name', 'approved_cnt': 'Planned Qty' } sfg_headers = { 'sfg_name': 'SFG Name', 'actual_required_qty': 'Actual Req Qty ', 'minimum_order_qty': 'Minimum Order Qty', 'assembling_cost':'Assembling Cost per unit', 'total_assembling_cost':'Total Assembling Cost', 'approved_cnt':'Planned Qty' } material_headers = { 'material_name': 'Material Name', 'approx_price': 'Unit Price', 'actual_required_qty': 'Field Z Header', 'minimum_order_qty':'Minimum Order Qty', 'total_approx_price':'Approx Price', 'approved_cnt':'Planned Qty' } custom_headers = { 'estimated_assembling_charges': 'SFG Assembling Charges', 'estimated_material_cost': 'Material Procurement Cost', 'other_charges': 'Transport & Other Charges', 'tax_value':'Tax (18%)', 'overall_total':'Overall Estimated Cost' } self._write_sheet(workbook, 'FG Plan', self.line_ids, line_headers) self._write_sheet(workbook, 'SFG Plan', self.sfg_line_ids, sfg_headers) self._write_sheet(workbook, 'Material Plan', self.material_line_ids, material_headers) # Write the custom sheet with fields from the main model (self) and custom headers self._write_custom_sheet(workbook, 'Budget', custom_headers) workbook.close() output.seek(0) # Create attachment and initiate download attachment = self.env['ir.attachment'].create({ 'name': f'{self.plan_ref_no}_Export.xlsx', 'type': 'binary', 'datas': base64.b64encode(output.read()), 'res_model': self._name, 'res_id': self.id, 'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) return { 'type': 'ir.actions.act_url', 'url': f'/web/content/{attachment.id}?download=true', 'target': 'self', } def _write_sheet(self, workbook, sheet_name, line_records, headers): """Write data to each sheet based on specified fields and custom headers.""" worksheet = workbook.add_worksheet(sheet_name) # Write custom headers for col, header in enumerate(headers.values()): worksheet.write(0, col, header) # Write data rows for row, record in enumerate(line_records, start=1): for col, field in enumerate(headers.keys()): value = getattr(record, field, '') # Convert value if necessary (Many2one, Date, etc.) if isinstance(value, models.Model): worksheet.write(row, col, value.display_name if value else '') elif isinstance(value, (fields.Date, fields.Datetime)): worksheet.write(row, col, value.strftime('%Y-%m-%d') if value else '') else: worksheet.write(row, col, value) def _write_custom_sheet(self, workbook, sheet_name, headers): """Write a custom sheet with fields from the main model, row by row with custom headers.""" worksheet = workbook.add_worksheet(sheet_name) # Write each field as a separate row with its custom header and value for row, (field, header) in enumerate(headers.items()): # Write the header in the first column worksheet.write(row, 0, header) # Retrieve the value for the field and write it in the second column value = getattr(self, field, '') # Convert value if necessary (Many2one, Date, etc.) if isinstance(value, models.Model): worksheet.write(row, 1, value.display_name if value else '') elif isinstance(value, (fields.Date, fields.Datetime)): worksheet.write(row, 1, value.strftime('%Y-%m-%d') if value else '') else: worksheet.write(row, 1, value) def action_top_approver_esign_btn(self): sequence_util = self.env['sos_common_scripts'] result = sequence_util.action_assign_signature( self, 'approved_by', 'approved_on', 'sos_inventory.sos_management_user' ) email_addresses = self.get_unique_emails() self.send_indent_plan_email(email_addresses) self.env['sos_audit_log'].create_log('Approval', f'Indent {self.plan_ref_no} approved by {self.env.user.name}') self.update_store_qty(self.line_ids,self.sfg_line_ids,self.material_line_ids) self.create_production_plan(self.line_ids) self.create_procurement_plan(self.material_line_ids) self.create_fptc(self.line_ids) self.create_sfg_quotation(self.sfg_line_ids) def action_report_esign_btn(self): if self.blowup == False: self.blowup = True missing_boms = [] for item in self.line_ids: if item.approved_cnt != 0: fg_name = item.fg_name.id product_bom = self.env['sos_fg_bom'].search([ ('fg_name', '=', fg_name), ('is_primary', '=', True) ], limit=1) if not product_bom: missing_boms.append(item.fg_name.name) if missing_boms: raise UserError(f"BOM for following product(s) are Missing, Delete those for this Indent or add BOM(s) : {', '.join(missing_boms)}") else: self.create_sfg_plan(self.line_ids) self.create_material_plan(self.sfg_line_ids) else: raise UserError("Already Blowed up") def action_approve_esign_btn(self): body_html = f"""

Below Indent Budget is waiting for your Approval

""" send_email = self.env['sos_common_scripts'] send_email.send_direct_email(self.env,"sos_fg_plan",self.id,"ramachandran.r@sosaley.in","Indent Budget Approval",body_html) sequence_util = self.env['sos_common_scripts'] result = sequence_util.action_assign_signature( self, 'prepared_by', 'prepared_on' ) def update_store_qty(self,line_ids,sfg_line_ids,material_line_ids): model_names = ['sos_fg', 'sos_sfg', 'sos_material'] for model_name in model_names: if model_name == 'sos_fg': column_name = 'fg_name' line_ids_final = line_ids elif model_name == 'sos_sfg': column_name = 'sfg_name' line_ids_final = sfg_line_ids else: column_name = 'material_name' line_ids_final = material_line_ids for item in line_ids_final: f_id = getattr(item, column_name).id sos_record = self.env[model_name].search([('id', '=', f_id)], limit=1) if sos_record: new_in_transit_qty = sos_record.in_transit_stock_qty + item.approved_cnt sos_record.write({ 'order_qty': 0, 'in_transit_stock_qty': new_in_transit_qty, }) if model_name != "sos_fg": sos_record.write({ 'blocked_qty': sos_record.blocked_qty + item.approved_cnt }) def create_sfg_quotation(self, line_ids): quotation_model = self.env['sos_sfg_quote_generation'] quotation_line_model = self.env['sos_sfg_quote_generation_line'] quotation_record = quotation_model.search([('plan_ref_no', '=', self.plan_ref_no)], limit=1) if not quotation_record: quotation_record = quotation_model.create({'plan_ref_no': self.plan_ref_no}) for item in line_ids: if item.approved_cnt != 0: if item.blowup_action == "yes": blowup_action_result = "with_materials" else: blowup_action_result = "without_materials" quotation_line_model.create({ 'material_name': item.sfg_name.id, 'required_qty': item.approved_cnt, 'inprogress_qty': item.approved_cnt, 'plan_id': quotation_record.id, 'purchase_type':blowup_action_result, 'supplier_name':item.sfg_name.service_providers }) def create_procurement_plan(self, line_ids): quotation_model = self.env['sos_quote_generation'] quotation_line_model = self.env['sos_quote_generation_line'] quotation_record = quotation_model.search([('plan_ref_no', '=', self.plan_ref_no)], limit=1) if not quotation_record: quotation_record = quotation_model.create({'plan_ref_no': self.plan_ref_no}) for item in line_ids: if item.approved_cnt != 0: quotation_line_model.create({ 'material_name': item.material_name.id, 'required_qty': item.approved_cnt, 'plan_id': quotation_record.id, 'supplier_name':item.material_name.suppliers }) def create_material_plan(self, line_ids): material_stores_model = self.env['sos_material'] material_line_model = self.env['sos_material_plan_line'] # Dictionary to store component quantities part_no_quantities = {} # Process each line to populate the quantities for item in line_ids: if item.approved_cnt != 0 and item.blowup_action != 'no': # Fetch the SFG BOM for the current item sfg_bom = self.env['sos_sfg_bom'].search([ ('name', '=', item.sfg_name.id) ], limit=1) if sfg_bom: # Fetch SFG BOM lines sfg_lines = self.env['sos_sfg_bom_line'].search([('bom_id', '=', sfg_bom.id)]) for sfg_line in sfg_lines: component = sfg_line.primary_component_id if component: req_qty = sfg_line.quantity * item.approved_cnt # Aggregate quantities for the component if component.part_no in part_no_quantities: part_no_quantities[component.part_no]['req_qty'] += req_qty else: part_no_quantities[component.part_no] = { 'inhand_stock_qty': max(0, component.inhand_stock_qty - component.blocked_qty), 'in_transit_stock_qty': component.in_transit_stock_qty, 'minimum_stock_qty': component.minimum_stock_qty, 'minimum_order_qty': component.minimum_order_qty, 'req_qty': req_qty, 'part_no_id': component.id } # Process the aggregated quantities to update or create material plan lines for part_no, data in part_no_quantities.items(): inhand_stock_qty = data['inhand_stock_qty'] minimum_stock_qty = data['minimum_stock_qty'] req_qty = data['req_qty'] minimum_order_qty = data['minimum_order_qty'] in_transit_stock_qty = data['in_transit_stock_qty'] # Check if the material line already exists pick_line = material_line_model.search([ ('plan_id', '=', self.id), ('material_name', '=', data['part_no_id']) ], limit=1) if pick_line: # Update existing material line pick_line.write({ 'actual_required_qty': pick_line.actual_required_qty + req_qty, 'required_qty': pick_line.required_qty + req_qty, 'approved_cnt': pick_line.approved_cnt + (req_qty if pick_line.approved_cnt else 0) }) else: # Calculate the approved count if creating a new line if minimum_stock_qty > (inhand_stock_qty + in_transit_stock_qty) - req_qty: approved_cnt = max(req_qty + minimum_stock_qty - (inhand_stock_qty + in_transit_stock_qty), minimum_order_qty) else: approved_cnt = 0 # Create a new material plan line material_line_model.create({ 'material_name': data['part_no_id'], 'inhand_qty': inhand_stock_qty, 'minimum_stock_qty': minimum_stock_qty, 'plan_id': self.id, 'required_qty': req_qty, 'actual_required_qty': req_qty, # Set correctly for new records 'approved_cnt': approved_cnt, 'minimum_order_qty': minimum_order_qty }) def create_sfg_plan(self, line_ids): sfg_stores_model = self.env['sos_sfg'] sfg_line_model = self.env['sos_sfg_plan_line'] # Loop through the lines to process each item for item in line_ids: if item.approved_cnt != 0: fg_name = item.fg_name.id # Use the ID for Many2one relationship fg_qty_to_produce = int(item.approved_cnt) # Fetch the primary BOM for the FG product_bom = self.env['sos_fg_bom'].search([ ('fg_name', '=', fg_name), ('is_primary', '=', True) ], limit=1) if product_bom: lines = self.env['sos_fg_bom_line'].search([('bom_id', '=', product_bom.id)]) # Fetch existing SFG plan lines for the current plan old_lines = sfg_line_model.search([('plan_id', '=', self.id)]) already_created_sfg = {line.sfg_name.id: line for line in old_lines} 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 * fg_qty_to_produce sfg_inhand_qty = max(0, sfg_line.sfg_bom_id.name.inhand_stock_qty - sfg_line.sfg_bom_id.name.blocked_qty) sfg_minimum_stock_qty = sfg_line.sfg_bom_id.name.minimum_stock_qty sfg_intransit_stock_qty = sfg_line.sfg_bom_id.name.in_transit_stock_qty # Calculate approved quantity if sfg_minimum_stock_qty <= (sfg_inhand_qty + sfg_intransit_stock_qty) - req_sfg_qty: approved_cnt = 0 else: #approved_cnt = req_sfg_qty + sfg_minimum_stock_qty - (sfg_inhand_qty + sfg_intransit_stock_qty) approved_cnt = req_sfg_qty - (sfg_inhand_qty + sfg_intransit_stock_qty) # If the SFG already exists in the plan, update its values if sfg_name in already_created_sfg: existing_line = already_created_sfg[sfg_name] # Determine the new approved count if existing_line.actual_required_qty + req_sfg_qty > sfg_line.sfg_bom_id.name.minimum_order_qty: order_to_be = existing_line.actual_required_qty + req_sfg_qty else: order_to_be = sfg_line.sfg_bom_id.name.minimum_order_qty # Update the existing line existing_line.write({ 'required_qty': existing_line.required_qty + req_sfg_qty, 'actual_required_qty': existing_line.actual_required_qty + req_sfg_qty, 'approved_cnt': order_to_be, 'blowup_action': 'yes' }) else: # Create a new SFG plan line sfg_line_model.create({ 'fg_name': fg_name, 'sfg_name': sfg_name, 'inhand_qty': sfg_inhand_qty, 'minimum_stock_qty': sfg_minimum_stock_qty, 'plan_id': self.id, 'actual_required_qty': approved_cnt, 'required_qty': req_sfg_qty, 'approved_cnt': approved_cnt, 'blowup_action': 'yes', 'assembling_cost': sfg_line.sfg_bom_id.name.assembling_charges }) material_lines = self.env['sos_sfg_bom_line'].search([('fg_bom_id', '=', product_bom.id)]) material_stores_model = self.env['sos_material'] material_line_model = self.env['sos_material_plan_line'] # Fetch existing material plan lines for the current plan old_lines_material = material_line_model.search([('plan_id', '=', self.id)]) already_created_material = {line_material.material_name.id: line_material for line_material in old_lines_material} for material_line in material_lines: inhand_stock_qty = material_line.primary_component_id.inhand_stock_qty minimum_stock_qty = material_line.primary_component_id.minimum_stock_qty req_qty = material_line.quantity * fg_qty_to_produce minimum_order_qty = material_line.primary_component_id.minimum_order_qty intransit_qty_material = material_line.primary_component_id.in_transit_stock_qty # Calculate the approved count for the material if minimum_stock_qty > (inhand_stock_qty + intransit_qty_material) - req_qty: #approved_cnt_material = max(req_qty + minimum_stock_qty - (inhand_stock_qty + intransit_qty_material), minimum_order_qty) approved_cnt_material = max(req_qty - (inhand_stock_qty + intransit_qty_material), 0, minimum_order_qty) else: approved_cnt_material = 0 # Check if the material already exists in the current plan if material_line.primary_component_id.id in already_created_material: existing_line = already_created_material[material_line.primary_component_id.id] # Accumulate approved_cnt new_approved_cnt = existing_line.approved_cnt + approved_cnt_material existing_line.write({ 'actual_required_qty': existing_line.actual_required_qty + req_qty, 'required_qty': existing_line.required_qty + req_qty, 'approved_cnt': new_approved_cnt }) else: # Create a new material line material_line_model.create({ 'material_name': material_line.primary_component_id.id, 'inhand_qty': inhand_stock_qty, 'minimum_stock_qty': minimum_stock_qty, 'plan_id': self.id, 'minimum_order_qty': minimum_order_qty, 'actual_required_qty': req_qty, 'required_qty': req_qty, 'approved_cnt': approved_cnt_material }) def create_production_plan(self,line_ids): indent_model = self.env['sos_production_plan'] indent_record = indent_model.search([('plan_ref_no', '=', self.plan_ref_no)], limit=1) if not indent_record: for item in line_ids: fg_name = item.fg_name.id qp_no = item.fg_name.qp_no fg_qty_to_produce = int(item.approved_cnt) indent_model.create({ 'plan_ref_no': self.plan_ref_no, 'fg_name': fg_name, 'qp_no': qp_no, 'required_qty': fg_qty_to_produce, 'target_date': self.target_date, 'indent_start_date' : self.indent_start_date }) def create_fptc(self,line_ids): tc_model = self.env['sos_transfer_challan'] tc_record = tc_model.search([('plan_ref_no', '=', self.plan_ref_no)], limit=1) if not tc_record: for item in line_ids: fg_name = item.fg_name.id tc_record_new = tc_model.create({ 'plan_ref_no': self.plan_ref_no, 'planned_qty': item.approved_cnt, 'fg_name': fg_name, 'indent_start_date':self.indent_start_date, 'indent_target_date':self.target_date }) if tc_record_new: try: specifications = self.env['sos_testing_parameters'].search([('fg_name', '=', fg_name)], limit=1) if not specifications: continue lines = [(5, 0, 0)] for param in specifications.specification_ids: lines.append((0, 0, { 'specification': param.name, 'specification_value': '' })) tc_record_new.specification_line_ids = lines except Exception as e: _logger.error("Error processing specifications for FG %s: %s", fg_name, str(e)) continue class SOS_FG_Plan_Line(models.Model): _name = 'sos_fg_plan_line' _description = 'FG Plan Lines' _order = 'approved_cnt desc' fg_name = fields.Many2one('sos_fg',string="FG Name") inhand_qty = fields.Integer(string="FG Stocks") minimum_stock_qty = fields.Integer(string="Minimum Stock Qty") in_transit_stock_qty = fields.Integer(string="In-transit Stock Qty") plan_id = fields.Many2one('sos_fg_plan', string='FG Plan', ondelete='cascade') required_qty = fields.Integer(string='Required Qty') actual_required_qty = fields.Integer(string='Actual Req Qty') minimum_order_qty = fields.Integer(string='Minimum Order Qty') approved_cnt = fields.Integer(string='Planned Qty', store=True) planned_week_1 = fields.Integer(string='Week 1') planned_week_2 = fields.Integer(string='Week 2') planned_week_3 = fields.Integer(string='Week 3') planned_week_4 = fields.Integer(string='Week 4') planned_week_5 = fields.Integer(string='Week 5') planned_week_6 = fields.Integer(string='Week 6') planned_week_7 = fields.Integer(string='Week 7') planned_week_8 = fields.Integer(string='Week 8') completed_qty = fields.Integer(string='Completed Qty', compute='_compute_completed', store=True) @api.depends('planned_week_1', 'planned_week_2', 'planned_week_3', 'planned_week_4', 'planned_week_5', 'planned_week_6', 'planned_week_7', 'planned_week_8') def _compute_completed(self): for record in self: total_completed = ( record.planned_week_1 + record.planned_week_2 + record.planned_week_3 + record.planned_week_4 + record.planned_week_5 + record.planned_week_6 + record.planned_week_7 + record.planned_week_8 ) record.completed_qty = total_completed class SOS_SFG_Plan_Line(models.Model): _name = 'sos_sfg_plan_line' _description = 'SFG Plan Lines' _order = 'approved_cnt desc' 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) sfg_name = fields.Many2one('sos_sfg',string="SFG Name") assembling_cost = fields.Monetary(currency_field='currency_id',string="Assembling Cost per unit") blowup_action = fields.Selection( [('yes', 'Yes'), ('no', 'No')], string='Blowup',default="yes" ) inhand_qty = fields.Integer(string="SFG Stocks") minimum_stock_qty = fields.Integer(string="Minimum Stock Qty") in_transit_stock_qty = fields.Integer(string="In-transit Stock Qty") minimum_order_qty = fields.Integer(string="Minimum Order Qty") plan_id = fields.Many2one('sos_fg_plan', string='SFG Plan', ondelete='cascade') required_qty = fields.Integer(string='Required Qty') approved_cnt = fields.Integer(string='Planned Qty',default='1') actual_required_qty = fields.Integer(string='Actual Req Qty') total_assembling_cost = fields.Float(compute='_compute_total_assembling_cost', string='Total Assembling Cost') @api.onchange('sfg_name') def _onchange_sfg_name(self): for record in self: if record.sfg_name: record.assembling_cost = record.sfg_name.assembling_charges @api.depends('assembling_cost', 'approved_cnt') def _compute_total_assembling_cost(self): for record in self: if record.assembling_cost is not None and record.approved_cnt is not None: record.total_assembling_cost = record.assembling_cost * record.approved_cnt else: record.total_assembling_cost = 0 class SOS_Material_Plan_Line(models.Model): _name = 'sos_material_plan_line' _description = 'Material Plan Lines' _order = 'approved_cnt desc' material_name = fields.Many2one('sos_material',string="Material 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) inhand_qty = fields.Integer(string="Material Stocks") in_transit_stock_qty = fields.Integer(string="In-transit Stock Qty") minimum_stock_qty = fields.Integer(string="Minimum Stock Qty") plan_id = fields.Many2one('sos_fg_plan', string='Material Plan', ondelete='cascade') required_qty = fields.Integer(string='Required Qty') actual_required_qty = fields.Integer(string='Actual Req Qty') minimum_order_qty = fields.Integer(string='Minimum Order Qty') approved_cnt = fields.Integer(string='Planned Qty') approx_price = fields.Monetary(related="material_name.unit_price",currency_field='currency_id', string='Unit Price',store=True) total_approx_price = fields.Float(compute='_compute_total_approx_price', string='Approx Price',store=True) @api.onchange('material_name') def _onchange_material_name(self): for record in self: if record.material_name: record.approx_price = record.material_name.unit_price @api.depends('approx_price', 'approved_cnt') def _compute_total_approx_price(self): for record in self: if record.approx_price is not None and record.approved_cnt is not None: record.total_approx_price = record.approx_price * record.approved_cnt else: record.total_approx_price = 0