506 lines
24 KiB
Python
Executable File
506 lines
24 KiB
Python
Executable File
# -*- coding: utf-8 -*-
|
|
|
|
from odoo import models, fields, api
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class sos__grn(models.Model):
|
|
_name = 'sos_grn'
|
|
_description = 'Goods Receipt Note'
|
|
_rec_name = 'grn_no'
|
|
_order = 'id desc'
|
|
|
|
supplier_invoice_no = fields.Char(string="Supplier Invoice/DC No")
|
|
iqi_no = fields.Many2one('sos_iqi', string='IQI No')
|
|
ir_no = fields.Many2one('sos_ir', string='Inward Ref No')
|
|
grn_no = fields.Char(string="GRN No", required= True, readonly= True, default=lambda self: self._compute_sequence())
|
|
grn_date = fields.Datetime(string="GRN Date")
|
|
supplier_name = fields.Many2one('sos_suppliers',string="Supplier Name")
|
|
service_provider_name = fields.Many2one('sos_service_providers', string="Service Provider Name")
|
|
company_id = fields.Many2one('res.company', string='Company',
|
|
default=lambda self: self.env.company.id, index=True)
|
|
no_of_parcel=fields.Integer(string="No. of Bags / Boxes/ Barrels/Rolls Received")
|
|
no_of_parcel_per_invoice=fields.Integer(string="No. of Bags/ Boxes/ Barrels/Rolls as per Invoice")
|
|
identification_label = fields.Selection([ ('Yes', 'Yes'),('No', 'No'),('NA', 'NA')],'Identification Label affixed post inspection', default='Yes')
|
|
po_no = fields.Many2one('sos_po', string='Our PO No')
|
|
wo_no = fields.Many2one('sos_wo', string='Our WO No')
|
|
approved_selector = fields.Selection([ ('Yes', 'Yes'),('No', 'No')],'Material Received from Approved Supplier', default='Yes')
|
|
test_report_no = fields.Char(string="Material Final Inspection Test Report No. & Date")
|
|
reworked_process = fields.Char(string="Reworked Process (If any)")
|
|
approved_storage_location = fields.Char(string="Approved Material Stored in",default="Stores")
|
|
rejected_storage_location = fields.Char(string="Rejected Material Stored in",default="Stores")
|
|
inspection_remarks = fields.Selection([ ('Approved', 'Approved'),('Re work', 'Re work'),('Rejected', 'Rejected')],'Inspection Remarks: Approved / Re work / Rejected', default='Approved')
|
|
stores_approval_name = fields.Many2one('res.users', string='Approved By')
|
|
stores_approval_image = fields.Image(related="stores_approval_name.signature_image",string='Stores-RM/PM In Charge',readonly=True)
|
|
stores_approval_on = fields.Date(string="Stores Received On")
|
|
line_ids = fields.One2many('sos_grn_line', 'grn_id', string="Components")
|
|
line_ids_sfg = fields.One2many('sos_grn_line_sfg', 'grn_id', string="SFG")
|
|
line_ids_fg = fields.One2many('sos_grn_line_fg', 'grn_id', string="FG")
|
|
received_goods_type = fields.Selection([
|
|
('Materials', 'Materials'),
|
|
('SFG', 'SFG'),
|
|
('FG', 'FG')
|
|
], string='Received Goods Type',default="Materials",copy=True)
|
|
|
|
responsiveness = fields.Selection([
|
|
('10', '10'),
|
|
('5', '5')
|
|
], string='Responsiveness',copy=True)
|
|
|
|
reports = fields.Selection([
|
|
('30', '30'),
|
|
('25', '25'),
|
|
('10', '10')
|
|
], string='Report',copy=True)
|
|
|
|
delivery = fields.Selection([
|
|
('30', '30'),
|
|
('25', '25'),
|
|
('10', '10')
|
|
], string='Delivery',copy=True)
|
|
|
|
_sql_constraints = [
|
|
('grn_no_unique', 'UNIQUE(grn_no)', 'The GRN No must be unique!')
|
|
]
|
|
@api.model
|
|
def create(self, vals):
|
|
record = super(sos__grn, self).create(vals)
|
|
if not self.env.context.get('from_script', False):
|
|
record.action_report_esign_btn()
|
|
return record
|
|
|
|
def action_report_grn_btn(self):
|
|
try:
|
|
action = self.env.ref("sos_inventory.action_report_grn").report_action(self)
|
|
return action
|
|
except ValueError as e:
|
|
print(f"Failed to find report action: {e}")
|
|
|
|
def action_report_esign_btn(self):
|
|
|
|
sequence_util = self.env['sos_common_scripts']
|
|
sequence_util.action_assign_signature(
|
|
self,
|
|
'stores_approval_name',
|
|
'stores_approval_on',
|
|
'sos_inventory.sos_scg_group_user'
|
|
)
|
|
self.generate_supplier_service()
|
|
if self.received_goods_type == "Materials":
|
|
for item in self.line_ids:
|
|
component = self.env['sos_material'].browse(item.component_id.id)
|
|
if component:
|
|
current_qty = getattr(component, 'inhand_stock_qty', 0)
|
|
current_value = getattr(component, 'unit_price', 0)
|
|
current_intransit_qty = getattr(component, 'in_transit_stock_qty', 0)
|
|
if item.approved_qty < current_intransit_qty:
|
|
new_intransit_qty = current_intransit_qty - item.approved_qty
|
|
else:
|
|
new_intransit_qty = 0
|
|
new_qty = current_qty + item.approved_qty
|
|
new_unit_price = ((current_qty * current_value) + (item.approved_qty * item.unit_price))/new_qty
|
|
component.write({'inhand_stock_qty': new_qty, 'in_transit_stock_qty': new_intransit_qty,
|
|
'unit_price': new_unit_price})
|
|
if item.approved_qty > 0:
|
|
component.line_ids_in.create({'ref_id': component.id,'component_id': item.component_id.id,
|
|
'quantity' : item.approved_qty,'unit_price':item.unit_price,'ir_no':self.ir_no.id,
|
|
'action' : 'in'})
|
|
if item.rejected_qty > 0:
|
|
if self.po_no:
|
|
self.po_no.po_status = 'open'
|
|
|
|
elif self.received_goods_type == "SFG":
|
|
for item in self.line_ids_sfg:
|
|
if self.ir_no.indent_no:
|
|
procurement_record = self.env['sos_sfg_quote_generation'].search([('plan_ref_no', '=', self.ir_no.indent_no.plan_ref_no)], limit=1)
|
|
for every in procurement_record.line_ids:
|
|
|
|
if str(every.material_name.name).strip().lower() == str(item.component_id.name).strip().lower():
|
|
if (every.inprogress_qty - item.approved_qty) < 0:
|
|
inprogress = 0
|
|
else:
|
|
inprogress = every.inprogress_qty - item.approved_qty
|
|
every.write({
|
|
'inprogress_qty':inprogress
|
|
})
|
|
component = self.env['sos_sfg'].browse(item.component_id.id)
|
|
current_intransit_qty = getattr(component, 'in_transit_stock_qty', 0)
|
|
if item.approved_qty < current_intransit_qty:
|
|
new_intransit_qty = current_intransit_qty - item.approved_qty
|
|
else:
|
|
new_intransit_qty = 0
|
|
if component:
|
|
current_value = getattr(component, 'inhand_stock_qty', 0)
|
|
component.write({'inhand_stock_qty': current_value + item.approved_qty ,'in_transit_stock_qty': new_intransit_qty,})
|
|
if item.approved_qty > 0:
|
|
component.line_ids_in.create({'ref_id': component.id,'component_id': item.component_id.id,
|
|
'quantity' : item.approved_qty,'ir_no':self.ir_no.id,
|
|
'action' : 'in'})
|
|
if item.rejected_qty > 0:
|
|
if self.wo_no:
|
|
self.wo_no.wo_status = 'open'
|
|
else:
|
|
for item in self.line_ids_fg:
|
|
component = self.env['sos_fg'].browse(item.component_id.id)
|
|
if component:
|
|
current_value = getattr(component, 'inhand_stock_qty', 0)
|
|
component.write({'inhand_stock_qty': current_value + item.approved_qty})
|
|
|
|
|
|
def _compute_sequence(self):
|
|
sequence_util = self.env['sos_common_scripts']
|
|
return sequence_util.generate_sequence('sos_grn','GRN', 'grn_no')
|
|
def generate_supplier_service(self):
|
|
|
|
grn = self.env['sos_grn'].browse(self.id)
|
|
|
|
if self.received_goods_type == "Materials":
|
|
# Check for existing register line
|
|
already_created = self.env['sos_mat_outsourcing_vendor_register_lines'].search([
|
|
('ref_id.supplier_name', '=', grn.supplier_name.id),
|
|
('po_no', '=', grn.po_no.id)
|
|
], limit=1)
|
|
# Get or create the main register
|
|
outsource = self.env['sos_mat_outsourcing_vendor_register'].search(
|
|
[('supplier_name', '=', grn.supplier_name.id)], limit=1)
|
|
if not outsource:
|
|
outsource = self.env['sos_mat_outsourcing_vendor_register'].create({
|
|
'supplier_name': grn.supplier_name.id,
|
|
})
|
|
ir_records = self.env['sos_ir'].search([('po_no', '=', grn.po_no.id)])
|
|
iqi_records = self.env['sos_iqi'].search([('ir_id_unique_id', 'in', ir_records.ids)])
|
|
grn_records = self.env['sos_grn'].search([('po_no', '=', grn.po_no.id)])
|
|
grn_line_records = self.env['sos_grn_line'].search([('grn_id', 'in', grn_records.ids)])
|
|
|
|
# Prepare material_names list
|
|
mat_record = [line.component_id.id for line in grn_line_records if line.component_id and line.component_id.part_no]
|
|
material_names = [(6, 0, mat_record)]
|
|
|
|
# Sum all GRN line quantities including current
|
|
total_received_qty = sum(grn_line_records.mapped('received_qty')) #+ new_received
|
|
total_approved_qty = sum(grn_line_records.mapped('approved_qty')) #+ new_approved
|
|
total_rejected_qty = sum(grn_line_records.mapped('rejected_qty')) #+ new_rejected
|
|
|
|
delivery_values = grn_records.mapped('delivery')
|
|
delivery_numbers = [float(value) for value in delivery_values if value]
|
|
existing_count = len(delivery_numbers)
|
|
|
|
responsive_values = grn_records.mapped('responsiveness')
|
|
responsive_numbers = [float(value) for value in responsive_values if value]
|
|
|
|
reports_values = grn_records.mapped('reports')
|
|
reports_numbers = [float(value) for value in reports_values if value]
|
|
|
|
avg_delivery_marks = sum(delivery_numbers) / (existing_count)
|
|
delivery = (
|
|
30 if 27.5 < avg_delivery_marks <= 30 else
|
|
25 if 22.5 < avg_delivery_marks <= 27.5 else
|
|
10 if avg_delivery_marks < 20 else
|
|
0
|
|
)
|
|
|
|
avg_report_marks = sum(reports_numbers) / (existing_count)
|
|
report = (
|
|
30 if 27.5 < avg_report_marks <= 30 else
|
|
25 if 22.5 < avg_report_marks <= 27.5 else
|
|
10 if avg_report_marks < 20 else
|
|
0
|
|
)
|
|
|
|
avg_responsiveness_marks = sum(responsive_numbers) / (existing_count)
|
|
|
|
responsiveness = (
|
|
10 if 5 <= avg_responsiveness_marks <= 10 else
|
|
5 if avg_responsiveness_marks < 5 else
|
|
0
|
|
)
|
|
if already_created:
|
|
updated_vals = {
|
|
'received_qty': total_received_qty,
|
|
'approved_qty': total_approved_qty,
|
|
'rejected_qty': total_rejected_qty,
|
|
'delivery_marks': delivery,
|
|
'responsiveness_marks': responsiveness,
|
|
'report_marks': report,
|
|
'iqi_references': [(6, 0, iqi_records.ids)],
|
|
'grn_references': [(6, 0, grn_records.ids)],
|
|
'material_names': material_names,
|
|
}
|
|
already_created.write(updated_vals)
|
|
else:
|
|
self.env['sos_mat_outsourcing_vendor_register_lines'].create({
|
|
'ref_id': outsource.id,
|
|
'po_no': grn.po_no.id,
|
|
'received_qty': total_received_qty,
|
|
'approved_qty': total_approved_qty,
|
|
'rejected_qty': total_rejected_qty,
|
|
'iqi_references': [(6, 0, iqi_records.ids)],
|
|
'grn_references': [(6, 0, grn_records.ids)],
|
|
'material_names': material_names,
|
|
'delivery_marks': delivery,
|
|
'responsiveness_marks': responsiveness,
|
|
'report_marks': report,
|
|
})
|
|
|
|
elif self.received_goods_type == "SFG":
|
|
|
|
# Check for existing register line
|
|
already_created = self.env['sos_outsourcing_vendor_monitoring_register_lines'].search([
|
|
('ref_id.service_provider_name', '=', grn.service_provider_name.id),
|
|
('wo_no', '=', grn.wo_no.id)
|
|
], limit=1)
|
|
|
|
# Get or create the main register
|
|
outsource = self.env['sos_outsourcing_vendor_monitoring_register'].search(
|
|
[('service_provider_name', '=', grn.service_provider_name.id)], limit=1)
|
|
if not outsource:
|
|
outsource = self.env['sos_outsourcing_vendor_monitoring_register'].create({
|
|
'service_provider_name': grn.service_provider_name.id,
|
|
})
|
|
# Related documents
|
|
ir_records = self.env['sos_ir'].search([('wo_no', '=', grn.wo_no.id)])
|
|
iqi_records = self.env['sos_iqi'].search([('ir_id_unique_id', 'in', ir_records.ids)])
|
|
dc_records = self.env['sos_dc'].search([('wo_no', '=', grn.wo_no.id)])
|
|
grn_records = self.env['sos_grn'].search([('wo_no', '=', grn.wo_no.id)])
|
|
|
|
grn_line_records = self.env['sos_grn_line_sfg'].search([('grn_id', 'in', grn_records.ids)])
|
|
|
|
# Prepare material_names list
|
|
sfg_ids = [line.component_id.id for line in grn_line_records if line.component_id and line.component_id.name]
|
|
|
|
# Include current line component manually if not yet saved in DB
|
|
current_component_id = self.line_ids.component_id
|
|
if current_component_id and current_component_id not in sfg_ids:
|
|
sfg_ids.append(current_component_id)
|
|
|
|
sfg_names = [(6, 0, sfg_ids)]
|
|
|
|
total_received_qty = sum(grn_line_records.mapped('received_qty'))
|
|
total_approved_qty = sum(grn_line_records.mapped('approved_qty'))
|
|
total_rejected_qty = sum(grn_line_records.mapped('rejected_qty'))
|
|
|
|
delivery_values = grn_line_records.mapped('delivery')
|
|
delivery_numbers = [float(value) for value in delivery_values if value]
|
|
existing_count = len(delivery_numbers)
|
|
|
|
responsive_values = grn_line_records.mapped('responsiveness')
|
|
responsive_numbers = [float(value) for value in responsive_values if value]
|
|
|
|
reports_values = grn_line_records.mapped('reports')
|
|
reports_numbers = [float(value) for value in reports_values if value]
|
|
|
|
avg_delivery_marks = sum(delivery_numbers) / (existing_count)
|
|
avg_responsiveness_marks = sum(responsive_numbers) / (existing_count)
|
|
avg_report_marks = sum(reports_numbers) / (existing_count)
|
|
|
|
if already_created:
|
|
#print(f" sfg_names : if {sfg_names}")
|
|
updated_vals = {
|
|
'received_qty': total_received_qty,
|
|
'approved_qty': total_approved_qty,
|
|
'rejected_qty': total_rejected_qty,
|
|
'delivery_marks': avg_delivery_marks,
|
|
'responsiveness_marks': avg_responsiveness_marks,
|
|
'report_marks': avg_report_marks,
|
|
'iqi_references': [(6, 0, iqi_records.ids)],
|
|
'dc_references': [(6, 0, dc_records.ids)],
|
|
'grn_references': [(6, 0, grn_records.ids)],
|
|
'sfg_names': sfg_names,
|
|
}
|
|
already_created.write(updated_vals)
|
|
else:
|
|
#print(f" sfg_names : else {sfg_names}")
|
|
self.env['sos_outsourcing_vendor_monitoring_register_lines'].create({
|
|
'ref_id': outsource.id,
|
|
'iqi_references': [(6, 0, iqi_records.ids)],
|
|
'dc_references': [(6, 0, dc_records.ids)],
|
|
'grn_references': [(6, 0, grn_records.ids)],
|
|
'sfg_names': sfg_names,
|
|
'wo_no': grn.wo_no.id,
|
|
'received_qty': total_received_qty,
|
|
'approved_qty': total_approved_qty,
|
|
'rejected_qty': total_rejected_qty,
|
|
'delivery_marks': avg_delivery_marks,
|
|
'responsiveness_marks': avg_responsiveness_marks,
|
|
'report_marks': avg_report_marks,
|
|
})
|
|
class sos_grn_line(models.Model):
|
|
_name = 'sos_grn_line'
|
|
_description = 'GRN Material Lines'
|
|
|
|
grn_id = fields.Many2one('sos_grn', string="Materials", ondelete="cascade")
|
|
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
|
ordered_qty = fields.Integer(string="Ordered Quantity")
|
|
received_qty = fields.Integer(string="Actual Received Quantity")
|
|
invoice_qty = fields.Integer(string="Quantity as Per Invoice")
|
|
approved_qty = fields.Integer(string="Approved Quantity")
|
|
currency_id = fields.Many2one('res.currency', string='Currency')
|
|
unit_price = fields.Monetary(string="Unit Price", currency_field='currency_id')
|
|
rejected_qty = fields.Integer(string="Rejected Quantity")
|
|
reworked_qty = fields.Integer(string="Reworked Quantity (If any)")
|
|
|
|
quality_marks = fields.Integer(string="Quality",compute="_compute_quality")
|
|
|
|
@api.depends('received_qty', 'rejected_qty')
|
|
def _compute_quality(self):
|
|
for record in self:
|
|
record.quality_marks = self._calculate_quality_marks(record.received_qty, record.rejected_qty)
|
|
|
|
|
|
def _calculate_quality_marks(self, received, rejected):
|
|
if received != 0:
|
|
q = 100 - ((rejected / received) * 100)
|
|
if 91 <= q <= 100:
|
|
return 30
|
|
elif 80 <= q <= 90:
|
|
return 25
|
|
else:
|
|
return 10
|
|
return 0
|
|
|
|
@api.model
|
|
def write(self, vals):
|
|
for record in self:
|
|
if 'approved_qty' in vals:
|
|
component_id = vals.get('component_id', record.component_id.id)
|
|
approved_qty = vals.get('approved_qty', record.approved_qty)
|
|
unit_price = vals.get('unit_price', record.unit_price)
|
|
component = self.env['sos_material'].browse(component_id)
|
|
|
|
if component:
|
|
current_qty = component.inhand_stock_qty or 0
|
|
current_value = component.unit_price or 0
|
|
current_intransit_qty = getattr(component, 'in_transit_stock_qty', 0)
|
|
if approved_qty < current_intransit_qty:
|
|
new_intransit_qty = current_intransit_qty - approved_qty
|
|
else:
|
|
new_intransit_qty = 0
|
|
new_qty = current_qty + approved_qty
|
|
if new_qty > 0:
|
|
new_unit_price = ((current_qty * current_value) + (approved_qty * unit_price)) / new_qty
|
|
else:
|
|
new_unit_price = unit_price
|
|
|
|
component.write({
|
|
'inhand_stock_qty': new_qty,
|
|
'unit_price': new_unit_price,
|
|
'in_transit_stock_qty': new_intransit_qty
|
|
})
|
|
if approved_qty > 0:
|
|
self.env['sos_material_transaction_history'].create({
|
|
'ref_id': component.id,
|
|
'component_id': component_id,
|
|
'quantity': approved_qty,
|
|
'unit_price': unit_price,
|
|
'action': 'in',
|
|
})
|
|
|
|
# Call the super method to apply the actual write
|
|
return super(sos_grn_line, self).write(vals)
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
if not self.env.context.get('from_script', False):
|
|
component_id = vals.get('component_id')
|
|
approved_qty = vals.get('approved_qty', 0)
|
|
unit_price = vals.get('unit_price', 0)
|
|
|
|
if component_id:
|
|
component = self.env['sos_material'].browse(component_id)
|
|
if component:
|
|
current_qty = getattr(component, 'inhand_stock_qty', 0)
|
|
current_value = getattr(component, 'unit_price', 0)
|
|
current_intransit_qty = getattr(component, 'in_transit_stock_qty', 0)
|
|
if approved_qty < current_intransit_qty:
|
|
new_intransit_qty = current_intransit_qty - approved_qty
|
|
else:
|
|
new_intransit_qty = 0
|
|
new_qty = current_qty + approved_qty
|
|
if new_qty > 0:
|
|
new_unit_price = ((current_qty * current_value) + (approved_qty * unit_price)) / new_qty
|
|
else:
|
|
new_unit_price = unit_price
|
|
|
|
component.write({
|
|
'inhand_stock_qty': new_qty,
|
|
'unit_price': new_unit_price,
|
|
'in_transit_stock_qty': new_intransit_qty
|
|
})
|
|
if approved_qty > 0:
|
|
self.env['sos_material_transaction_history'].create({
|
|
'ref_id': component.id,
|
|
'component_id': component_id,
|
|
'quantity': approved_qty,
|
|
'unit_price': unit_price,
|
|
'action': 'in',
|
|
})
|
|
|
|
record = super(sos_grn_line, self).create(vals)
|
|
|
|
return record
|
|
|
|
|
|
class sos_grn_line_sfg(models.Model):
|
|
_name = 'sos_grn_line_sfg'
|
|
_description = 'GRN SFG Lines'
|
|
|
|
grn_id = fields.Many2one('sos_grn', string="Materials", ondelete="cascade")
|
|
component_id = fields.Many2one('sos_sfg', string="Material Name", required=True)
|
|
ordered_qty = fields.Integer(string="Ordered Quantity")
|
|
received_qty = fields.Integer(string="Actual Received Quantity")
|
|
invoice_qty = fields.Integer(string="Quantity as Per Invoice")
|
|
approved_qty = fields.Integer(string="Approved Quantity")
|
|
rejected_qty = fields.Integer(string="Rejected Quantity")
|
|
reworked_qty = fields.Integer(string="Reworked Quantity (If any)")
|
|
|
|
quality_marks = fields.Integer(string="Quality",compute="_compute_sfgquality")
|
|
|
|
responsiveness = fields.Selection([
|
|
('10', '10'),
|
|
('5', '5')
|
|
], string='Responsiveness',copy=True,default="10")
|
|
|
|
reports = fields.Selection([
|
|
('30', '30'),
|
|
('25', '25'),
|
|
('10', '10')
|
|
], string='Report',copy=True,default="30")
|
|
|
|
delivery = fields.Selection([
|
|
('30', '30'),
|
|
('25', '25'),
|
|
('10', '10')
|
|
], string='Delivery',copy=True,default="30")
|
|
|
|
@api.depends('received_qty', 'rejected_qty')
|
|
def _compute_sfgquality(self):
|
|
for record in self:
|
|
record.quality_marks = self._calculate_quality_marks(record.received_qty, record.rejected_qty)
|
|
|
|
|
|
|
|
def _calculate_quality_marks(self, received, rejected):
|
|
if received != 0:
|
|
q = 100 - ((rejected / received) * 100)
|
|
if 91 <= q <= 100:
|
|
return 30
|
|
elif 80 <= q <= 90:
|
|
return 25
|
|
else:
|
|
return 10
|
|
return 0
|
|
|
|
class sos_grn_line_fg(models.Model):
|
|
_name = 'sos_grn_line_fg'
|
|
_description = 'GRN FG Lines'
|
|
|
|
grn_id = fields.Many2one('sos_grn', string="Materials", ondelete="cascade")
|
|
component_id = fields.Many2one('sos_fg', string="Material Name", required=True)
|
|
ordered_qty = fields.Integer(string="Ordered Quantity")
|
|
received_qty = fields.Integer(string="Actual Received Quantity")
|
|
invoice_qty = fields.Integer(string="Quantity as Per Invoice")
|
|
approved_qty = fields.Integer(string="Approved Quantity")
|
|
rejected_qty = fields.Integer(string="Rejected Quantity")
|
|
reworked_qty = fields.Integer(string="Reworked Quantity (If any)")
|
|
|