from odoo import api, fields, models, _ import logging from datetime import date,datetime from math import isnan import io import xlsxwriter from odoo.tools.misc import xlsxwriter import base64 _logger = logging.getLogger(__name__) class SOS_Quote_Generation(models.Model): _name = 'sos_quote_generation' _description = 'Quote Generation' _rec_name = 'plan_ref_no' plan_ref_no = fields.Char( readonly=True, required=True, string='Plan ID' ) line_ids = fields.One2many('sos_quote_generation_line', 'plan_id', string='Lines', ondelete='cascade') def action_export(self): output = io.BytesIO() workbook = xlsxwriter.Workbook(output, {'in_memory': True}) headers = { 'material_name' : 'Material Name', 'required_qty': 'Required Quantity', 'status': 'Status', } self._write_sheet(workbook, 'Materials', self.line_ids, headers) workbook.close() output.seek(0) 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) for col, header in enumerate(headers.values()): worksheet.write(0, col, header) for row, record in enumerate(line_records, start=1): for col, field in enumerate(headers.keys()): value = getattr(record, field, '') 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 action_generate_po(self): po_model = self.env['sos_po'] po_line_model = self.env['sos_po_line'] supplier_dict = {} for record in self.line_ids: if record.final_supplier1_name: supplier = record.final_supplier1_name if supplier not in supplier_dict: supplier_dict[supplier] = [] supplier_dict[supplier].append({ 'name': record.material_name, 'product_qty': record.final_supplier1_qty, 'price_unit': record.final_supplier1_quoted_price }) # if record.supplier2_name: # supplier = record.supplier2_name # if supplier not in supplier_dict: # supplier_dict[supplier] = [] # supplier_dict[supplier].append({ # 'name': record.material_name, # 'product_qty': record.supplier2_qty, # 'price_unit': record.supplier2_quoted_price # }) # if record.supplier3_name: # supplier = record.supplier3_name # if supplier not in supplier_dict: # supplier_dict[supplier] = [] # supplier_dict[supplier].append({ # 'name': record.material_name, # 'product_qty': record.supplier3_qty, # 'price_unit': record.supplier3_quoted_price # }) for x, y in supplier_dict.items(): sequence_util = self.env['sos_common_scripts'] po_no = sequence_util.generate_sequence('sos_po','PO', 'po_no') po_record = po_model.create({ 'po_no': po_no, 'po_date': date.today(),'supplier_name': x.id, 'supplier_gst_no': x.gst_no, 'sarf_no':x.supplier_code, 'supplier_address': x.address }) for components in y: po_line_model.create({ 'po_id': po_record.id, 'component_id': components['name'].id,'qp':components['name'].qp_no,'hsn_code':components['name'].hsn_code, 'quantity': components['product_qty'],'unit_price': components['price_unit'], 'total_price': components['product_qty'] * components['price_unit'] }) message = 'Purchase Order(s) successfully generated.' return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'message': message, 'type': 'success', 'sticky': False } } def action_generate_quotation(self): supplier_materials = {} for item in self.line_ids: if item.purchase_type == "supplier": for supplier in item.supplier_name: if supplier not in supplier_materials: supplier_materials[supplier] = [] supplier_materials[supplier].append({ 'material_name': item.material_name.id, 'required_qty': item.required_qty }) wizards = [] for supplier, materials in supplier_materials.items(): wizard_lines = [(0, 0, { 'material_name': material['material_name'], 'required_qty': material['required_qty'] }) for material in materials] wizard = self.env['supplier_quotation_wizard'].create({ 'supplier_id': supplier.id, 'plan_id': self.id, 'line_ids': wizard_lines }) wizards.append(wizard.id) return { 'type': 'ir.actions.act_window', 'name': 'Generate Quotation', 'view_mode': 'tree,form', 'res_model': 'supplier_quotation_wizard', 'domain': [('id', 'in', wizards)], 'target': 'new', } class SOS_Quote_Generation_Line(models.Model): _name = 'sos_quote_generation_line' _description = 'Quote Generation Lines' plan_id = fields.Many2one('sos_quote_generation', string='Quote Generation', ondelete='cascade') required_qty = fields.Integer(string='Required Qty') purchase_type = fields.Selection([ ('local', 'Local Purchase'), ('supplier', 'Supplier Purchase'), ('online', 'Online Purchase') ], string='Purchase Type', required=True, default='supplier') supplier_name = fields.Many2many('sos_suppliers', string='Supplier', domain="[('id', 'in', suppliers_ids)]") suppliers_ids = fields.Many2many('sos_suppliers', compute='_compute_suppliers_ids', store=False) is_supplier_name_readonly = fields.Boolean(compute='_compute_is_supplier_name_readonly') # Quote Comparison material_name = fields.Many2one('sos_material',string="Material Name") in_transit_stock_qty = fields.Integer(string="In-Transit Qty",related="material_name.in_transit_stock_qty") supplier1_name = fields.Many2one('sos_suppliers',string="Supplier 1") supplier1_quoted_price = fields.Float(string='Price') supplier1_qty = fields.Integer(string='Qty') supplier2_name = fields.Many2one('sos_suppliers',string="Supplier 2") supplier2_quoted_price = fields.Float(string='Price') supplier2_qty = fields.Integer(string='Qty') supplier3_name = fields.Many2one('sos_suppliers',string="Supplier 3") supplier3_quoted_price = fields.Float(string='Price') supplier3_qty = fields.Integer(string='Qty') final_supplier1_name = fields.Many2one('sos_suppliers',string="Supplier 1") final_supplier1_quoted_price = fields.Float(string='Price') final_supplier1_qty = fields.Integer(string='Qty') best_supplier = fields.Many2one('sos_suppliers', string="Best Deal", compute='_compute_best_supplier', store=True) available_suppliers = fields.Many2many('sos_service_providers', compute='_compute_available_suppliers') status = fields.Selection( [ ('open', 'Open'), ('close', 'Closed') ], default='open', string="Status", compute="_compute_from_ir", inverse="_set_status", store=True ) @api.depends('required_qty', 'in_transit_stock_qty') def _compute_from_ir(self): for record in self: if record.in_transit_stock_qty <= 0: record.status = "close" else: record.status = "open" def _set_status(self): for record in self: # Custom logic based on user changes if record.status == 'close': # Perform actions for 'close' pass elif record.status == 'open': # Perform actions for 'open' pass @api.depends('supplier1_name', 'supplier2_name', 'supplier3_name') def _compute_available_suppliers(self): for record in self: suppliers = [] if record.supplier1_name: suppliers.append(record.supplier1_name.id) if record.supplier2_name: suppliers.append(record.supplier2_name.id) if record.supplier3_name: suppliers.append(record.supplier3_name.id) record.available_suppliers = [(6, 0, suppliers)] @api.onchange('final_supplier1_name') def _onchange_final_supplier1_name(self): if self.final_supplier1_name: # Check which supplier is selected and update price and quantity accordingly if self.final_supplier1_name == self.supplier1_name: self.final_supplier1_quoted_price = self.supplier1_quoted_price self.final_supplier1_qty = self.supplier1_qty # elif self.final_supplier1_name == self.supplier2_name: # self.final_supplier1_quoted_price = self.supplier2_quoted_price # self.final_supplier1_qty = self.supplier2_qty # elif self.final_supplier1_name == self.supplier3_name: # self.final_supplier1_quoted_price = self.supplier3_quoted_price # self.final_supplier1_qty = self.supplier3_qty @api.depends('supplier1_quoted_price', 'supplier1_qty', 'supplier2_quoted_price', 'supplier2_qty', 'supplier3_quoted_price', 'supplier3_qty') def _compute_best_supplier(self): for record in self: best_supplier = None best_score = float('inf') suppliers = [ (record.supplier1_name, record.supplier1_quoted_price, record.supplier1_qty), (record.supplier2_name, record.supplier2_quoted_price, record.supplier2_qty), (record.supplier3_name, record.supplier3_quoted_price, record.supplier3_qty) ] final_supplier_qty = 0 final_supplier_price = 0 for supplier, price, qty in suppliers: if supplier: score = price / qty if qty else float('inf') if score < best_score: best_score = score best_supplier = supplier final_supplier_qty = qty final_supplier_price = price record.best_supplier = best_supplier record.final_supplier1_name = best_supplier if best_supplier: record.final_supplier1_qty = final_supplier_qty record.final_supplier1_quoted_price = final_supplier_price @api.depends('purchase_type') def _compute_is_supplier_name_readonly(self): for line in self: line.is_supplier_name_readonly = (line.purchase_type == 'local') @api.onchange('material_name') def _onchange_material_name(self): if self.material_name: supplier_ids = self.material_name.suppliers.ids if supplier_ids: self.suppliers_ids = supplier_ids else: self.suppliers_ids = self.env['sos_suppliers'].search([]).ids else: self.suppliers_ids = self.env['sos_suppliers'].search([]).ids @api.depends('material_name') def _compute_suppliers_ids(self): for record in self: if record.material_name: supplier_ids = record.material_name.suppliers.ids if supplier_ids: record.suppliers_ids = supplier_ids else: record.suppliers_ids = self.env['sos_suppliers'].search([]).ids else: record.suppliers_ids = self.env['sos_suppliers'].search([]).ids