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.
473 lines
21 KiB
473 lines
21 KiB
# -*- coding: utf-8 -*-
|
|
################################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
#
|
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
|
# Author: Cybrosys Technologies (<https://www.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/>.
|
|
#
|
|
################################################################################
|
|
import itertools
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class SaleOrder(models.Model):
|
|
"""Inherits Sales"""
|
|
_inherit = "sale.order"
|
|
|
|
is_version = fields.Boolean(string="Is Version",
|
|
help="For checking version or not")
|
|
version_count = fields.Integer(string="Sale Version Count",
|
|
compute='_compute_version_count',
|
|
help="Count of version created")
|
|
current_version_id = fields.Many2one("sale.order",
|
|
string="Current Version",
|
|
help="For creating versions")
|
|
version_ids = fields.One2many("sale.order",
|
|
string="Version",
|
|
inverse_name="current_version_id",
|
|
help="Versions created")
|
|
quotation_ref = fields.Char(string='Quotation Reference',
|
|
copy=False, readonly=True, tracking=True,
|
|
help="Quotation Reference")
|
|
state = fields.Selection(
|
|
selection_add=[('waiting_for_approval', 'Waiting For Approval'),
|
|
('sale',)])
|
|
approval_user_id = fields.Many2one('res.users',
|
|
string='Discount Approved By',
|
|
help="Discount approving person.")
|
|
onhand_check = fields.Boolean(string='Enable OnHand',
|
|
help='To check whether it is based on'
|
|
' on hand quantity')
|
|
forecast_check = fields.Boolean(string='Enable Forecast',
|
|
help='To check whether it is based on'
|
|
' Forecast quantity')
|
|
automate_print_invoices = fields.Boolean(
|
|
string='Print Invoices',
|
|
help="Print invoices for corresponding sale orders")
|
|
signature = fields.Binary(string='Signature',
|
|
help="Field for adding "
|
|
"the signature of the "
|
|
"sales person")
|
|
check_signature = fields.Boolean(compute='_compute_check_signature',
|
|
help="To check signature approval is "
|
|
"needed")
|
|
settings_approval = fields.Boolean(compute='_compute_settings_approval',
|
|
help="To check signature approval is "
|
|
"enabled in settings")
|
|
active = fields.Boolean(string='Active', help='Active', default=True)
|
|
user_salesperson = fields.Boolean(string="User Salesperson",
|
|
compute="_compute_user_salesperson",
|
|
help="Check if user is salesperson")
|
|
|
|
@api.depends('user_salesperson')
|
|
def _compute_user_salesperson(self):
|
|
"""Computes the user_salesperson field based on login user"""
|
|
for rec in self:
|
|
if rec.user_id == rec.env.user:
|
|
rec.user_salesperson = True
|
|
else:
|
|
rec.user_salesperson = False
|
|
|
|
@api.depends('signature')
|
|
def _compute_check_signature(self):
|
|
"""In this function computes the value of
|
|
the boolean field check signature
|
|
which is used to hide/unhide the validate
|
|
button in the current document"""
|
|
if self.env['ir.config_parameter'].sudo().get_param(
|
|
'all_in_one_sales_kit.sale_document_approve'):
|
|
if self.signature:
|
|
self.check_signature = True
|
|
else:
|
|
self.check_signature = False
|
|
else:
|
|
self.check_signature = True
|
|
|
|
def action_create_versions(self):
|
|
"""For creating the versions of the sale order"""
|
|
sale_order_copy_id = self.copy()
|
|
sale_order_copy_id.is_version = True
|
|
length = len(self.version_ids)
|
|
sale_order_copy_id.name = "%s-%s" % (self.name, str(length + 1))
|
|
|
|
self.write({'version_ids': [(4, sale_order_copy_id.id)]})
|
|
|
|
@api.depends('version_ids')
|
|
def _compute_version_count(self):
|
|
"""For calculating the number of versions created"""
|
|
for sale in self:
|
|
sale.version_count = len(sale.version_ids)
|
|
|
|
@api.depends('partner_id')
|
|
def _compute_settings_approval(self):
|
|
"""Computes the settings_approval field based on settings field."""
|
|
for rec in self:
|
|
if rec.env['ir.config_parameter'].sudo().get_param(
|
|
'all_in_one_sales_kit.sale_document_approve'):
|
|
rec.settings_approval = True
|
|
else:
|
|
rec.settings_approval = False
|
|
|
|
def action_view_versions(self):
|
|
"""Action for viewing versions"""
|
|
action = {
|
|
"type": "ir.actions.act_window",
|
|
"view_mode": "kanban,tree,form",
|
|
"name": _("Sale Order Versions"),
|
|
"res_model": self._name,
|
|
"domain": [('id', 'in', self.version_ids.ids)],
|
|
"target": "current",
|
|
}
|
|
return action
|
|
|
|
def action_confirm(self):
|
|
"""Override the confirm button of the sale order for cancelling the
|
|
other versions and making the current version main,also method for
|
|
confirming the sale order discount and sending mail for the approving
|
|
person if approval limit crossed.Super the method create to confirm
|
|
quotation, create and validate invoice"""
|
|
res = super().action_confirm()
|
|
automate_invoice = self.env[
|
|
'ir.config_parameter'].sudo().get_param(
|
|
'automate_invoice')
|
|
automate_print_invoices = self.env[
|
|
'ir.config_parameter'].sudo().get_param(
|
|
'automate_print_invoices')
|
|
automate_validate_invoice = self.env[
|
|
'ir.config_parameter'].sudo().get_param(
|
|
'automate_validate_invoice')
|
|
if automate_print_invoices:
|
|
self.automate_print_invoices = True
|
|
if automate_invoice:
|
|
self._create_invoices()
|
|
if automate_validate_invoice:
|
|
self.invoice_ids.action_post()
|
|
if not self.version_ids:
|
|
parent_sale = self.current_version_id
|
|
versions = parent_sale.mapped('version_ids').ids
|
|
if versions:
|
|
versions.append(parent_sale.id)
|
|
for version in parent_sale.version_ids:
|
|
if version.state == 'sale':
|
|
# Updating the version name into main version name and
|
|
# other versions state into cancel
|
|
version.current_version_id.update({'is_version': True,
|
|
'state': 'cancel'})
|
|
version.update({'version_ids': versions,
|
|
"name": version.current_version_id.name,
|
|
'is_version': False})
|
|
if version.state == 'draft':
|
|
version.update({'state': 'cancel'})
|
|
else:
|
|
if self.state == 'sale':
|
|
for sale in self.version_ids:
|
|
sale.update({'state': 'cancel'})
|
|
low_qty = ["Can't confirm the sale order due to: \n"]
|
|
for rec in self.order_line:
|
|
product_restriction = self.env[
|
|
'ir.config_parameter'].sudo().get_param(
|
|
'sale_stock_restrict.product_restriction')
|
|
check_stock = self.env[
|
|
'ir.config_parameter'].sudo().get_param(
|
|
'sale_stock_restrict.check_stock')
|
|
if product_restriction:
|
|
if rec.product_id.detailed_type == 'product':
|
|
if check_stock == 'on_hand_quantity':
|
|
if rec.product_uom_qty > rec.qty_available:
|
|
self.onhand_check = True
|
|
onhand_qty_list = "You have added %s units of %s" \
|
|
" but you only have %s units" \
|
|
" available.\n" % (
|
|
rec.product_uom_qty,
|
|
rec.product_id.name,
|
|
rec.qty_available)
|
|
low_qty.append(onhand_qty_list)
|
|
|
|
if check_stock == 'forecast_quantity':
|
|
if rec.product_uom_qty > rec.forecast_quantity:
|
|
self.forecast_check = True
|
|
forecast_qty_list = "You have added %s" \
|
|
" units of %s but " \
|
|
"you only have" \
|
|
" %s units available.\n" % (
|
|
rec.product_uom_qty,
|
|
rec.product_id.name,
|
|
rec.forecast_quantity)
|
|
low_qty.append(forecast_qty_list)
|
|
listToStr = ' '.join(map(str, low_qty))
|
|
if self.onhand_check:
|
|
raise UserError(listToStr)
|
|
if self.forecast_check:
|
|
raise UserError(listToStr)
|
|
to_approve = False
|
|
discount_vals = self.order_line.mapped('discount')
|
|
approval_users = self.env.ref(
|
|
'all_in_one_sales_kit.group_approval_manager').users
|
|
user_discount = self.env.user.allow_discount
|
|
if self.env.user.discount_control == True:
|
|
for rec in discount_vals:
|
|
if rec > user_discount:
|
|
to_approve = True
|
|
break
|
|
if to_approve:
|
|
display_id = self.id
|
|
action_id = self.env.ref(
|
|
'sale.action_quotations_with_onboarding').id
|
|
base_url = self.env['ir.config_parameter'].sudo().get_param(
|
|
'web.base.url')
|
|
redirect_link = "/web#id=%s&cids=1&menu_id=178&action=%s" \
|
|
"&model" \
|
|
"=sale.order&view_type=form" % (
|
|
display_id, action_id)
|
|
url = base_url + redirect_link
|
|
for user in approval_users:
|
|
mail_body = """
|
|
<p>Hello,</p>
|
|
<p>New sale order '%s'
|
|
Created with Discount by '%s'
|
|
need your approval on it.</p>
|
|
<p>To Approve, Cancel Order,
|
|
Click on the Following
|
|
Link:
|
|
<a href='%s' style="display: inline-block;
|
|
padding: 10px; text-decoration: none;
|
|
font-size: 12px;
|
|
background-color: #875A7B; color: #fff;
|
|
border-radius: 5px;">
|
|
<strong>Click Me</strong></a>
|
|
</p>
|
|
<p>Thank You.</p>""" % (self.name,
|
|
self.env.user.name,
|
|
url)
|
|
mail_values = {
|
|
'subject': "'%s' Discount Approval Request" % (self.name),
|
|
'body_html': mail_body,
|
|
'email_to': user.partner_id.email,
|
|
'model': 'sale.order',
|
|
}
|
|
mail_id = self.env['mail.mail'].sudo().create(mail_values)
|
|
mail_id.sudo().send()
|
|
self.state = 'waiting_for_approval'
|
|
for line in self.order_line:
|
|
if line.product_id.is_pack:
|
|
for record in line.product_id.pack_products_ids:
|
|
dest_loc = self.env.ref(
|
|
'stock.stock_location_customers').id
|
|
self.env['stock.move'].create({
|
|
'name': record.product_id.name,
|
|
'product_id': record.product_id.id,
|
|
'product_uom_qty':
|
|
record.quantity * line.product_uom_qty,
|
|
'product_uom': record.product_id.uom_id.id,
|
|
'picking_id': self.picking_ids[0].id,
|
|
'location_id':
|
|
self.picking_ids.picking_type_id.default_location_src_id.id,
|
|
'location_dest_id': dest_loc,
|
|
})
|
|
return res
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
"""Method for generating sequence for quotation """
|
|
res = super(SaleOrder, self).create(vals)
|
|
seq_val = self.env.ref(
|
|
'all_in_one_sales_kit.seq_quotation').id
|
|
res.quotation_ref = self.env['ir.sequence'].browse(
|
|
seq_val).next_by_id()
|
|
return res
|
|
|
|
def action_waiting_approval(self):
|
|
"""Method for approving the sale order discount"""
|
|
self.approval_user_id = self.env.user.id
|
|
self.state = 'sale'
|
|
|
|
def action_print_invoice(self):
|
|
"""Method to print invoice"""
|
|
data = self.invoice_ids
|
|
return self.env.ref('account.account_invoices').report_action(data)
|
|
|
|
@api.model
|
|
def get_data(self):
|
|
"""To get data to the sales dashboard."""
|
|
domain = [('user_id', '=', self.env.user.id)]
|
|
quotation = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'draft')])
|
|
my_sale_order_templates = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'sale')])
|
|
quotation_sent = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'sent')])
|
|
quotation_cancel = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'cancel')])
|
|
customers = self.env['res.partner'].search([])
|
|
to_invoice = self.env['sale.order'].search(
|
|
domain + [('invoice_status', '=', 'to invoice')])
|
|
products = self.env['product.template'].search([])
|
|
return {
|
|
'quotation': len(quotation),
|
|
'my_sale_order_templates': len(my_sale_order_templates),
|
|
'quotation_sent': len(quotation_sent),
|
|
'quotation_cancel': len(quotation_cancel),
|
|
'customers': len(customers),
|
|
'products': len(products),
|
|
'to_invoice': len(to_invoice),
|
|
}
|
|
|
|
@api.model
|
|
def get_value(self, start_date, end_date):
|
|
"""It is to pass values according to start and end date to the
|
|
dashboard."""
|
|
if start_date and end_date:
|
|
domain = [('user_id', '=', self.env.user.id),
|
|
('date_order', '>=', start_date),
|
|
('date_order', '<=', end_date)]
|
|
elif start_date:
|
|
domain = [('user_id', '=', self.env.user.id),
|
|
('date_order', '>=', start_date)]
|
|
elif end_date:
|
|
domain = [('user_id', '=', self.env.user.id),
|
|
('date_order', '<=', end_date)]
|
|
|
|
quotation = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'draft')])
|
|
my_sale_order_templates = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'sale')])
|
|
quotation_sent = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'sent')])
|
|
quotation_cancel = self.env['sale.order'].search(
|
|
domain + [('state', '=', 'cancel')])
|
|
customers = self.env['res.partner'].search([])
|
|
products = self.env['product.template'].search([])
|
|
to_invoice = self.env['sale.order'].search(
|
|
domain + [('invoice_status', '=', 'to invoice')])
|
|
return {
|
|
'quotation': len(quotation),
|
|
'my_sale_order_templates': len(my_sale_order_templates),
|
|
'quotation_sent': len(quotation_sent),
|
|
'quotation_cancel': len(quotation_cancel),
|
|
'customers': len(customers),
|
|
'products': len(products),
|
|
'to_invoice': len(to_invoice),
|
|
}
|
|
|
|
@api.model
|
|
def get_lead_customer(self):
|
|
"""Returns customer data to the graph of dashboard"""
|
|
lead_template = {}
|
|
sale = {}
|
|
partner_id = self.env['res.partner'].sudo().search([])
|
|
vals = self.env['sale.order'].sudo().search([
|
|
]).mapped('partner_id').ids
|
|
for record in partner_id:
|
|
if record.id in vals:
|
|
record.ref = vals.count(record.id)
|
|
sale.update({record: vals.count(record.id)})
|
|
sort = dict(
|
|
sorted(sale.items(), key=lambda item: item[1], reverse=True))
|
|
out = dict(itertools.islice(sort.items(), 10))
|
|
for count in out:
|
|
lead_template[count.name] = out[count]
|
|
return {
|
|
'lead_templates': lead_template,
|
|
}
|
|
|
|
@api.model
|
|
def get_lead_product(self):
|
|
"""Returns product data to the graph of dashboard"""
|
|
lead_template = {}
|
|
sale = {}
|
|
product_id = self.env['product.template'].search([])
|
|
for record in product_id:
|
|
sale.update({record: record.sales_count})
|
|
sort = dict(
|
|
sorted(sale.items(), key=lambda item: item[1], reverse=True))
|
|
out = dict(itertools.islice(sort.items(), 10))
|
|
for product in out:
|
|
lead_template[product.name] = out[product]
|
|
return {
|
|
'lead_templates': lead_template,
|
|
}
|
|
|
|
@api.model
|
|
def get_lead_order(self):
|
|
"""Returns lead sale order data to the graph of dashboard"""
|
|
lead_template = {}
|
|
sale = {}
|
|
order_id = self.env['sale.order'].search([('state', '=', 'sale')])
|
|
for record in order_id:
|
|
sale.update({record: record.amount_total})
|
|
sort = dict(
|
|
sorted(sale.items(), key=lambda item: item[1], reverse=True))
|
|
out = dict(itertools.islice(sort.items(), 10))
|
|
for order in out:
|
|
lead_template[order.name] = out[order]
|
|
return {
|
|
'lead_templates': lead_template,
|
|
}
|
|
|
|
@api.model
|
|
def get_my_monthly_comparison(self):
|
|
"""Returns my monthly sale count data to the graph of dashboard"""
|
|
lead_template = {}
|
|
sales_order = self.env['sale.order'].search(
|
|
[('user_id', '=', self.env.user.id)])
|
|
list = [rec.date_order.month for rec in sales_order]
|
|
for i in range(1, 13):
|
|
count = list.count(i)
|
|
lead_template.update({
|
|
i: count
|
|
})
|
|
return {
|
|
'lead_templates': lead_template,
|
|
}
|
|
|
|
@api.model
|
|
def get_sales_team(self):
|
|
"""Returns sales team data to the graph of dashboard"""
|
|
lead_template = {}
|
|
sale = {}
|
|
sales_team = self.env['crm.team'].search([])
|
|
for record in sales_team:
|
|
total = sum(self.env['sale.order'].search(
|
|
[('state', '=', 'sale'),
|
|
('team_id', '=', record.id)]).mapped('amount_total'))
|
|
sale.update({record: total})
|
|
sort = dict(
|
|
sorted(sale.items(), key=lambda item: item[1], reverse=True))
|
|
out = dict(itertools.islice(sort.items(), 10))
|
|
for team in out:
|
|
lead_template[team.name] = out[team]
|
|
return {
|
|
'lead_templates': lead_template,
|
|
}
|
|
|
|
@api.model
|
|
def get_least_sold(self):
|
|
"""Returns least sold product data to the graph of dashboard"""
|
|
lead_template = {}
|
|
sale = {}
|
|
product_id = self.env['product.template'].search([])
|
|
for record in product_id:
|
|
if record.sales_count != 0:
|
|
sale.update({record: record.sales_count})
|
|
sort = dict(
|
|
sorted(sale.items(), key=lambda item: item[1], reverse=False))
|
|
out = dict(itertools.islice(sort.items(), 10))
|
|
for product in out:
|
|
lead_template[product.name] = out[product]
|
|
return {
|
|
'lead_templates': lead_template,
|
|
}
|
|
|