312 lines
14 KiB
Python
Executable File
312 lines
14 KiB
Python
Executable File
# -*- coding: utf-8 -*-
|
|
|
|
from odoo import models, fields, api
|
|
import re
|
|
import math
|
|
class sos__po(models.Model):
|
|
_name = 'sos_po'
|
|
_description = 'Purchase Order'
|
|
_rec_name = 'po_no'
|
|
_order = 'id desc'
|
|
|
|
def _default_invoicing_address(self):
|
|
html_content = self.env.company.company_details
|
|
if not isinstance(html_content, str):
|
|
html_content = str(html_content)
|
|
clean_text = re.sub(r'</p>', '\n', html_content)
|
|
clean_text = re.sub(r'<[^>]+>', '', clean_text)
|
|
lines = clean_text.split('\n')
|
|
for line in lines:
|
|
if line.strip():
|
|
line.strip()
|
|
return clean_text
|
|
|
|
|
|
plan_ref_no = fields.Char(
|
|
string='Indent No'
|
|
)
|
|
supplier_name = fields.Many2one('sos_suppliers',string="Supplier Name", required= True)
|
|
supplier_address = fields.Text(related='supplier_name.address',string="Supplier Address")
|
|
po_no = fields.Char(string="P.O No", readonly= True, required= True, default=lambda self: self._generate_id())
|
|
po_date = fields.Datetime(string="P.O Date",required=True)
|
|
quotation_no = fields.Char(string="Quotation No")
|
|
quotation_date = fields.Date(string="Quotation Date")
|
|
material_delivery_required_date = fields.Date(string="Material Delivery Required Date")
|
|
supplier_gst_no = fields.Char(related='supplier_name.gst_no', string="Supplier GST No", readonly=False)
|
|
sosaley_gst_no = fields.Char(string="Sosaley GST No",default="33AACCJ0045R1ZI")
|
|
invoicing_address = fields.Text(string="Invoicing Address",default= _default_invoicing_address)
|
|
delivery_address = fields.Text(string="Delivery Address",default= _default_invoicing_address)
|
|
sarf_no = fields.Char(related='supplier_name.supplier_code',string="SARF No")
|
|
Contact_details = fields.Char(string="Contact Person Name & Mobile No")
|
|
terms_conditions = fields.Html(
|
|
string="Terms and Conditions",
|
|
default="""
|
|
<h3>Terms and Conditions:</h3><br/>
|
|
1. This Purchase Order is valid up to …………………………….<br/>
|
|
2. Payment: ................. from the date of receipt of the Invoice.<br/>
|
|
3. Invoice and Material Final Inspection Report should be provided with all the Supplies made.<br/>
|
|
4. Sosaley P.O. No. should be mentioned in your Delivery challan / Invoice.<br/>
|
|
5. All the products shall be supplied with closed boxes/containers and labelled.<br/>
|
|
6. All the boxes/containers shall be numbered (1 of 3, 2 of 3, etc.)
|
|
"""
|
|
)
|
|
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)
|
|
line_ids = fields.One2many('sos_po_line', 'po_id', string="Components", ondelete='cascade')
|
|
gross_value = fields.Monetary(compute='_compute_gross_value', string="Gross Value", currency_field='currency_id', readonly=True)
|
|
delivery_charges = fields.Monetary(string="Delivery Charges", currency_field='currency_id')
|
|
delivery_tax = fields.Integer(default=18,string="Tax (%)")
|
|
tax = fields.Selection(
|
|
[
|
|
('6', '6%'),
|
|
('12', '12%'),
|
|
('18', '18%'),
|
|
('28', '28%')
|
|
],
|
|
string="Tax (%)",
|
|
default='18'
|
|
)
|
|
total_value = fields.Monetary(compute='_compute_total_value', string="Total Value", currency_field='currency_id', readonly=True)
|
|
approval_status=fields.Selection([('Approved', 'Approve'),('Rejected', 'Reject')],string="Approval Status")
|
|
indent_id = fields.Integer(String="Integer")
|
|
total_qty = fields.Integer(string='Total Line Items Count', compute='_compute_total_qty', store=True)
|
|
received_qty = fields.Integer(string='Received Line Items Count', store=True)
|
|
payment_method=fields.Selection([('cash', 'Cash'),('neft', 'NEFT'),('credit', 'Credit'),('credit_card', 'Credit Card')],string="Payment Method")
|
|
po_status = fields.Selection([ ('amend', 'Amended'),('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
|
|
progress = fields.Float(string="Completion Percentage", compute='_compute_progress', store=True, group_operator=False)
|
|
|
|
stores_approved_by = fields.Many2one('res.users', string='Stores Approval By')
|
|
stores_approved_image = fields.Image(related="stores_approved_by.signature_image",string='Top Management Approval Sign',readonly=True)
|
|
stores_approved_on = fields.Datetime(string="Approved On")
|
|
|
|
stores_manager_approved_by = fields.Many2one('res.users', string='Manager Approval By')
|
|
stores_manager_approved_image = fields.Image(related="stores_manager_approved_by.signature_image",string='Top Management Approval Sign',readonly=True)
|
|
stores_manager_approved_on = fields.Datetime(string="Approved On")
|
|
|
|
top_management_approved_by = fields.Many2one('res.users', string='Top Management Approver')
|
|
top_management_approval_image = fields.Image(related="top_management_approved_by.signature_image",string='Top Management Approval',readonly=True)
|
|
top_management_approved_on = fields.Datetime(string="Approved On")
|
|
rounded_total_value = fields.Float(string="Total", compute="_compute_total_value", store=True)
|
|
delivery_total_value = fields.Float(string="Total", store=True)
|
|
adjustment_value = fields.Float(string="Round-off", compute="_compute_total_value", store=True)
|
|
tax_amount = fields.Float(string="Tax Value", store=True,readonly=True)
|
|
delivery_tax_amount = fields.Float(string="Tax Value", store=True,readonly=True, compute="_compute_deliverytax")
|
|
remarks = fields.Text(string="Remarks")
|
|
# @api.model
|
|
# def create(self, vals):
|
|
# record = super(sos__po, self).create(vals)
|
|
# record.action_esign_btn()
|
|
# return record
|
|
def action_amend(self):
|
|
active_ids = self.env.context.get('active_ids', [])
|
|
records = self.browse(active_ids)
|
|
for record in records:
|
|
record.po_status = 'amend'
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Amended',
|
|
'message': 'The selected record(s) have been amended.',
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
|
|
|
|
def action_esign_btn(self):
|
|
sequence_util = self.env['sos_common_scripts']
|
|
sequence_util.action_assign_signature(
|
|
self,
|
|
'stores_approved_by',
|
|
'stores_approved_on',
|
|
'sos_inventory.sos_scg_group_user'
|
|
)
|
|
# Email part
|
|
body_html = f"""
|
|
<p>Below <b>PO</b> is waiting for your Approval</p>
|
|
"""
|
|
|
|
send_email = self.env['sos_common_scripts']
|
|
send_email.send_group_email(self.env,"sos_po",self.id,"deenalaura.m@sosaley.in","PO Approval Request",body_html,'sos_inventory.sos_scg_group_manager')
|
|
# Email part ends
|
|
|
|
def action_head_esign_btn(self):
|
|
# Email part
|
|
body_html = f"""
|
|
<p>Below <b>PO</b> is waiting for your Approval</p>
|
|
"""
|
|
subject = f"Purchase Order Approval Request - {self.po_no}"
|
|
send_email = self.env['sos_common_scripts']
|
|
send_email.send_direct_email(self.env,"sos_po",self.id,"ramachandran.r@sosaley.in",subject,body_html)
|
|
# Email part ends
|
|
sequence_util = self.env['sos_common_scripts']
|
|
return sequence_util.action_assign_signature(
|
|
self,
|
|
'stores_manager_approved_by',
|
|
'stores_manager_approved_on'
|
|
|
|
)
|
|
def action_top_esign_btn(self):
|
|
sequence_util = self.env['sos_common_scripts']
|
|
sequence_util.action_assign_signature(
|
|
self,
|
|
'top_management_approved_by',
|
|
'top_management_approved_on',
|
|
'sos_inventory.sos_management_user'
|
|
)
|
|
body_html = f"""
|
|
<p>Below <b>PO</b> is Approved</p>
|
|
"""
|
|
|
|
send_email = self.env['sos_common_scripts']
|
|
send_email.send_direct_email(self.env,"sos_po",self.id,"accounts@sosaley.in","PO Approved",body_html)
|
|
if self.stores_approved_by and self.stores_approved_by.login:
|
|
send_email.send_direct_email(self.env,"sos_po",self.id,self.stores_approved_by.login,"PO Approved",body_html)
|
|
|
|
@api.depends('total_qty', 'received_qty', 'line_ids.prev_received_qty', 'line_ids.quantity') # ✅ Added 'line_ids.quantity'
|
|
def _compute_progress(self):
|
|
for record in self:
|
|
total_quantity = sum(record.line_ids.mapped('quantity'))
|
|
prev_received_quantity = sum(record.line_ids.mapped('prev_received_qty'))
|
|
|
|
if total_quantity > 0:
|
|
percentage = int((prev_received_quantity / total_quantity) * 100)
|
|
else:
|
|
percentage = 0
|
|
|
|
|
|
record.progress = percentage
|
|
|
|
if percentage >= 100:
|
|
record.progress = 100
|
|
record.po_status = "close"
|
|
else:
|
|
record.po_status = "open"
|
|
# @api.depends('total_qty', 'received_qty')
|
|
# def _compute_progress(self):
|
|
# for po in self:
|
|
# if po.total_qty > 0:
|
|
# percentage = (po.received_qty / po.total_qty) * 100
|
|
# po.progress = percentage
|
|
# if percentage >= 100:
|
|
# po.progress = 100
|
|
# po.po_status = "close"
|
|
# else:
|
|
# po.progress = 0
|
|
@api.depends('line_ids')
|
|
def _compute_total_qty(self):
|
|
for po in self:
|
|
po.total_qty = len(po.line_ids)
|
|
|
|
def _generate_id(self):
|
|
sequence_util = self.env['sos_common_scripts']
|
|
return sequence_util.generate_sequence('sos_po','PO', 'po_no')
|
|
|
|
@api.depends('line_ids.total_price')
|
|
def _compute_gross_value(self):
|
|
for record in self:
|
|
record.gross_value = round(sum(line.total_price for line in record.line_ids), 2)
|
|
@api.depends('delivery_charges', 'delivery_tax')
|
|
def _compute_deliverytax(self):
|
|
for record in self:
|
|
if record.delivery_charges:
|
|
delivery_tax_amount = round((record.delivery_tax * record.delivery_charges) / 100, 2)
|
|
exact_total = round(record.delivery_charges + delivery_tax_amount, 2)
|
|
if math.isnan(exact_total) or exact_total is None:
|
|
record.total_value = 0.00
|
|
record.delivery_total_value = 0.00
|
|
else:
|
|
rounded_total = round(exact_total)
|
|
|
|
record.delivery_total_value = exact_total
|
|
record.delivery_tax_amount = delivery_tax_amount
|
|
else:
|
|
record.delivery_total_value = 0.00
|
|
record.delivery_tax_amount = 0.00
|
|
|
|
@api.depends('gross_value', 'tax','delivery_total_value')
|
|
def _compute_total_value(self):
|
|
for record in self:
|
|
if record.gross_value:
|
|
tax_amount = round((int(record.tax) * record.gross_value) / 100, 2)
|
|
exact_total = round(record.delivery_total_value + record.gross_value + tax_amount, 2)
|
|
if math.isnan(exact_total) or exact_total is None:
|
|
record.total_value = 0.00
|
|
record.rounded_total_value = 0.00
|
|
record.adjustment_value = 0.00
|
|
else:
|
|
rounded_total = round(exact_total)
|
|
adjustment = round(rounded_total - exact_total, 2)
|
|
|
|
record.total_value = exact_total
|
|
record.rounded_total_value = rounded_total
|
|
record.adjustment_value = adjustment
|
|
record.tax_amount = tax_amount
|
|
else:
|
|
record.total_value = 0.00
|
|
record.rounded_total_value = 0.00
|
|
record.adjustment_value = 0.00
|
|
record.tax_amount = 0.00
|
|
|
|
|
|
|
|
|
|
def action_report_po_btn(self):
|
|
try:
|
|
action = self.env.ref("sos_inventory.action_report_po").report_action(self)
|
|
return action
|
|
except ValueError as e:
|
|
print(f"Failed to find report action: {e}")
|
|
|
|
def action_approve_po_btn(self):
|
|
for record in self:
|
|
record.approval_status = "Approved"
|
|
return {
|
|
'type':'ir.actions.client',
|
|
'tag':'display_notification',
|
|
'params':{
|
|
'message':'Approved'
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class Po_Line(models.Model):
|
|
_name = 'sos_po_line'
|
|
_description = 'PO Material Lines'
|
|
|
|
po_id = fields.Many2one('sos_po', string="Materials", ondelete="cascade")
|
|
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
|
|
material_code = fields.Char(related='component_id.material_code',string="Material Code")
|
|
qp = fields.Char(related='component_id.qp_no',string="QP #")
|
|
hsn_code = fields.Char(related='component_id.hsn_code',string="HSN Code #")
|
|
unit_price = fields.Monetary(string="Unit Price")
|
|
quantity = fields.Integer(string="Qty", required=True, default=1)
|
|
prev_received_qty = fields.Integer()
|
|
currency_id = fields.Many2one('res.currency', string='Currency')
|
|
total_price = fields.Monetary(compute='_compute_total_cost', string="Total Price", currency_field='currency_id')
|
|
status = fields.Integer(string="Status",default=0)
|
|
@api.onchange('prev_received_qty')
|
|
def _onchange_prev_received_qty(self):
|
|
if self.prev_received_qty >= self.quantity:
|
|
self.status = 2
|
|
|
|
@api.onchange('component_id')
|
|
def _onchange_component_id(self):
|
|
if self.component_id:
|
|
self.unit_price = self.component_id.unit_price
|
|
else:
|
|
self.unit_price = 0.0 # Default value when no component is selected
|
|
@api.depends('unit_price', 'quantity')
|
|
def _compute_total_cost(self):
|
|
for record in self:
|
|
record.total_price = record.unit_price * record.quantity
|
|
|
|
|