309 lines
13 KiB
Python
Executable File
309 lines
13 KiB
Python
Executable File
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 |