You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

182 lines
9.1 KiB

# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Nikhil M (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import api, fields, models, _
from odoo.exceptions import UserError
SALE_TENDER_STATES = [
('draft', 'Draft'),
('ongoing', 'Ongoing'),
('in_progress', 'Confirmed'),
('open', 'Bid Selection'),
('done', 'Closed'),
('cancel', 'Cancelled')
]
class SaleTenderType(models.Model):
"""Creating a model to store the tender type."""
_name = "sale.tender.type"
_description = "Sale Tender Type"
name = fields.Char(string='Agreement Type', required=True, translate=True,help="Name")
exclusive = fields.Selection([
('exclusive', 'Select only one Quotation (exclusive)'), ('multiple', 'Select multiple Quotation (non-exclusive)')],
string='Agreement Selection Type', required=True, default='multiple',
help="""Select only one Quotation (exclusive): when a sale order is confirmed, cancel the remaining sale order.\n
Select multiple Quotation (non-exclusive): allows multiple sale orders. On confirmation of a sale order it does not cancel the remaining orders""")
quantity_copy = fields.Selection([
('copy', 'Use quantities of agreement'), ('none', 'Set quantities manually')],
string='Quantities', required=True, default='none',help="To copy quantity")
line_copy = fields.Selection([
('copy', 'Use lines of agreement'), ('none', 'Do not create Quotations lines automatically')],
string='Lines', required=True, default='copy',help="To copy line")
class SaleTender(models.Model):
"""Creating a model to record all the sale tender agreements."""
_name = "sale.tender"
_description = "Sale Tender"
_order = 'id desc'
_inherit = ['mail.thread', 'mail.activity.mixin']
@api.depends('sale_order_ids')
def _compute_orders_number(self):
"""Function to compute the number of associated orders for the agreement."""
for tender in self:
tender.order_count = len(tender.sale_order_ids)
def action_in_progress(self):
"""Function to activate the current agreement."""
self.ensure_one()
if not self.line_ids:
raise UserError(_("You cannot confirm agreement '%s' because there is no product line.", self.name))
for tender_line in self.line_ids:
if tender_line.price_unit <= 0.0:
raise UserError(_('You cannot confirm the Tender without price.'))
if tender_line.product_qty <= 0.0:
raise UserError(_('You cannot confirm the Tender order without quantity.'))
self.write({'state': 'ongoing'})
# Set the sequence number regarding the tender type
if self.name == 'New':
self.name = self.env['ir.sequence'].next_by_code('sale.tender.order')
def action_cancel(self):
"""Function to cancel the current agreement."""
self.write({'state': 'cancel'})
def action_done(self):
"""
Generate all sale order based on selected lines, should only be called on one agreement at a time
"""
if any(sale_order.state in ['draft', 'sent'] for sale_order in self.mapped('sale_order_ids')):
raise UserError(_('You have to cancel or validate every Order before closing the sale tender.'))
self.write({'state': 'done'})
name = fields.Char('Reference', required=True, copy=False, default='New',readonly=True,help="Name")
order_count = fields.Integer(compute='_compute_orders_number',
string='Number of Orders',help="Count of orders")
customer_id = fields.Many2one('res.partner', string="Customer",
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",help="Customer Choosen")
type_id = fields.Many2one('sale.tender.type',
string="Agreement Type", required=True,help="Type")
ordering_date = fields.Date(string="Ordering Date", tracking=True,help="Order Date")
date_end = fields.Datetime(string='Agreement Deadline', tracking=True,help="End Date")
schedule_date = fields.Date(string='Delivery Date', index=True,
help="The expected and scheduled delivery date where all the products are received",
tracking=True)
user_id = fields.Many2one(
'res.users', string='Sales Representative',
default=lambda self: self.env.user, check_company=True,help="Username")
description = fields.Html(help="Description")
company_id = fields.Many2one('res.company', string='Company', required=True,
default=lambda self: self.env.company,help="Company")
sale_order_ids = fields.One2many('sale.order', 'tender_id',
string='Sale Orders',
states={'done': [('readonly', True)]},help="Sale orders")
line_ids = fields.One2many('sale.tender.line', 'tender_id',
string='Products to Sell',
states={'done': [('readonly', True)]}, copy=True,help="Tender Lines")
state = fields.Selection(SALE_TENDER_STATES,
'Status', tracking=True, required=True,
copy=False, default='draft',help="Status")
currency_id = fields.Many2one('res.currency', 'Currency', required=True,
default=lambda
self: self.env.company.currency_id.id,help="Currency")
class SaleTender_idLine(models.Model):
"""Creating the model to store the lines in the tender."""
_name = "sale.tender.line"
_inherit = 'analytic.mixin'
_description = "Sale Tender Line"
_rec_name = 'product_id'
@api.onchange('product_id')
def _onchange_product_id(self):
"""Function to update the line price."""
self.price_unit = self.product_id.list_price
self.product_qty = 1
@api.model_create_multi
def create(self, vals_list):
"""Overriding the create function to update the uom value and also add restrictions."""
for vals in vals_list:
if not vals.get('product_uom_id'):
vals['product_uom_id'] = self.env["product.product"].browse(
vals.get('product_id')).uom_id.id
lines = super().create(vals_list)
for line, vals in zip(lines, vals_list):
if line.tender_id.state not in ['draft', 'cancel','done'] and line.tender_id.type_id.is_quantity_copy == 'none':
if vals['price_unit'] <= 0.0:
raise UserError(
_('You cannot confirm the blanket order without price.'))
return lines
product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], required=True,help="Product")
product_uom_id = fields.Many2one('uom.uom', string='Product Unit of Measure', domain="[('category_id', '=', product_uom_category_id)]",help="Uom")
product_uom_category_id = fields.Many2one(related='product_id.uom_id.category_id',help="Uom Category")
product_qty = fields.Float(string='Quantity', digits='Product Unit of Measure',help="Quantity")
product_description_variants = fields.Char('Custom Description',help="Variants Description")
price_unit = fields.Float(string='Unit Price', digits='Product Price',help="Price Unit")
tender_id = fields.Many2one('sale.tender', required=True, string='Sale Agreement', ondelete='cascade',help="Tender")
company_id = fields.Many2one('res.company', related='tender_id.company_id', string='Company', store=True, readonly=True,help="Company")
schedule_date = fields.Date(string='Scheduled Date',help="Scheduled Date")
def _prepare_sale_order_line(self, name, product_qty=0.0, price_unit=0.0, taxes_ids=False):
"""Function to prepare the required values for sale order lines."""
self.ensure_one()
if self.product_description_variants:
name += '\n' + self.product_description_variants
res = {
'name': name,
'product_id': self.product_id.id,
'product_uom': self.product_uom_id.id,
'product_uom_qty': product_qty,
'price_unit': price_unit,
'tax_id': [(6, 0, taxes_ids)],
'analytic_distribution': self.analytic_distribution,
}
return res