Slink/sos_sales/models/sos_sales_achievement_repor...

532 lines
27 KiB
Python
Executable File

from odoo import models, fields, api
from datetime import date
import re
import calendar
class SOS_Sales_Achievement_Report(models.Model):
_name = 'sos_sales_achievement_report'
_description = 'Sosaley Sales Achievement Report'
_rec_name='financial_year'
_sql_constraints = [
('unique_financial_year_sales_person', 'UNIQUE(financial_year, sales_person)', 'The combination of Financial Year and Sales Person must be unique.')
]
financial_year = fields.Char(string="Financial Year", default=lambda self: self._get_financial_year())
sales_person = fields.Many2one('res.users', string='Sales person',required=True)
overall_target = fields.Float(string="Overall Target")
opening_balance = fields.Float(string="Opening Balance")
planned_target_april = fields.Float(string="Planned For April")
actual_target_april = fields.Float(string="Actual For April")
billed_target_april = fields.Float(string="Billed For April")
yet_to_billed_target_april = fields.Float(string="Yet to Billed For April")
collected_target_april = fields.Float(string="Collected For April")
achievement_percentage_april = fields.Char(string="Achievement Percentage For April", compute="_compute_achievement_percentage", store=True)
planned_target_may = fields.Float(string="Planned For May")
actual_target_may = fields.Float(string="Actual For May")
billed_target_may = fields.Float(string="Billed For May")
yet_to_billed_target_may = fields.Float(string="Yet to Billed For May")
collected_target_may = fields.Float(string="Collected For May")
achievement_percentage_may = fields.Char(string="Achievement Percentage For May", compute="_compute_achievement_percentage", store=True)
planned_target_june = fields.Float(string="Planned For June")
actual_target_june = fields.Float(string="Actual For June")
billed_target_june = fields.Float(string="Billed For June")
yet_to_billed_target_june = fields.Float(string="Yet to Billed For June")
collected_target_june = fields.Float(string="Collected For June")
achievement_percentage_june = fields.Char(string="Achievement Percentage For June", compute="_compute_achievement_percentage", store=True)
planned_target_july = fields.Float(string="Planned For July")
actual_target_july = fields.Float(string="Actual For July")
billed_target_july = fields.Float(string="Billed For July")
yet_to_billed_target_july = fields.Float(string="Yet to Billed For July")
collected_target_july = fields.Float(string="Collected For July")
achievement_percentage_july = fields.Char(string="Achievement Percentage For July", compute="_compute_achievement_percentage", store=True)
planned_target_august = fields.Float(string="Planned For August")
actual_target_august = fields.Float(string="Actual For August")
billed_target_august = fields.Float(string="Billed For August")
yet_to_billed_target_august = fields.Float(string="Yet to Billed For August")
collected_target_august = fields.Float(string="Collected For August")
achievement_percentage_august = fields.Char(string="Achievement Percentage For August", compute="_compute_achievement_percentage", store=True)
planned_target_september = fields.Float(string="Planned For September")
actual_target_september = fields.Float(string="Actual For September")
billed_target_september = fields.Float(string="Billed For September")
yet_to_billed_target_september = fields.Float(string="Yet to Billed For September")
collected_target_september = fields.Float(string="Collected For September")
achievement_percentage_september = fields.Char(string="Achievement Percentage For September", compute="_compute_achievement_percentage", store=True)
planned_target_october = fields.Float(string="Planned For October")
actual_target_october = fields.Float(string="Actual For October")
billed_target_october = fields.Float(string="Billed For October")
yet_to_billed_target_october = fields.Float(string="Yet to Billed For October")
collected_target_october = fields.Float(string="Collected For October")
achievement_percentage_october = fields.Char(string="Achievement Percentage For October", compute="_compute_achievement_percentage", store=True)
planned_target_november = fields.Float(string="Planned For November")
actual_target_november = fields.Float(string="Actual For November")
billed_target_november = fields.Float(string="Billed For November")
yet_to_billed_target_november = fields.Float(string="Yet to Billed For November")
collected_target_november = fields.Float(string="Collected For November")
achievement_percentage_november = fields.Char(string="Achievement Percentage For November", compute="_compute_achievement_percentage", store=True)
planned_target_december = fields.Float(string="Planned For December")
actual_target_december = fields.Float(string="Actual For December")
billed_target_december = fields.Float(string="Billed For December")
yet_to_billed_target_december = fields.Float(string="Yet to Billed For December")
collected_target_december = fields.Float(string="Collected For December")
achievement_percentage_december = fields.Char(string="Achievement Percentage For December", compute="_compute_achievement_percentage", store=True)
planned_target_january = fields.Float(string="Planned For January")
actual_target_january = fields.Float(string="Actual For January")
billed_target_january = fields.Float(string="Billed For January")
yet_to_billed_target_january = fields.Float(string="Yet to Billed For January")
collected_target_january = fields.Float(string="Collected For January")
achievement_percentage_january = fields.Char(string="Achievement Percentage For January", compute="_compute_achievement_percentage", store=True)
planned_target_february = fields.Float(string="Planned For February")
actual_target_february = fields.Float(string="Actual For February")
billed_target_february = fields.Float(string="Billed For February")
yet_to_billed_target_february = fields.Float(string="Yet to Billed For February")
collected_target_february = fields.Float(string="Collected For February")
achievement_percentage_february = fields.Char(string="Achievement Percentage For February", compute="_compute_achievement_percentage", store=True)
planned_target_march = fields.Float(string="Planned For March")
actual_target_march = fields.Float(string="Actual For March")
billed_target_march = fields.Float(string="Billed For March")
yet_to_billed_target_march = fields.Float(string="Yet to Billed For March")
collected_target_march = fields.Float(string="Collected For March")
achievement_percentage_march = fields.Char(string="Achievement Percentage For March", compute="_compute_achievement_percentage", store=True)
planned_target_total = fields.Float(string="Planned Total", compute="_compute_planned_total_target", store=True)
actual_target_total = fields.Float(string="Actual Total", compute="_compute_actual_total_target", store=True)
achievement_percentage_total = fields.Char(string="Achievement Percentage For Total", compute="_compute_achievement_percentage_total", store=True)
billed_target_total = fields.Char(string="Billed For Total", compute="_compute_billed_total_target", store=True)
yet_to_billed_target_total = fields.Char(string="Billed For Total", compute="_compute_yet_to_billed_total_target", store=True)
collected_target_total = fields.Char(string="collected For Total", compute="_compute_collected_total_target", store=True)
can_edit_billed_target = fields.Boolean(
string="Can Edit Billed Target",
compute="_compute_can_edit_billed_target",
store=False,
)
ytd_target = fields.Float(string="YTD Total", store=True, compute="_compute_ytd_target")
ytd_sales = fields.Float(string="YTD Sales", store=True, compute="_compute_ytd_sales_target")
ytd_sales_percentage = fields.Float(string="YTD Sales Percentage", store=True, compute="_compute_ytd_sales_percentage")
ytd_yet_to_billed = fields.Float(string="YTD Billed", store=True,compute="_compute_ytd_yet_to_billed")
ytd_billed = fields.Float(string="YTD Billed", store=True,compute="_compute_ytd_billed")
ytd_collected = fields.Float(string="YTD Collected", store=True,compute="_compute_ytd_collected")
line_ids = fields.One2many('sos_sales_achievement_report_brief', 'ref_id',copy=True)
@api.depends(
'yet_to_billed_target_april', 'yet_to_billed_target_may', 'yet_to_billed_target_june',
'yet_to_billed_target_july', 'yet_to_billed_target_august', 'yet_to_billed_target_september',
'yet_to_billed_target_october', 'yet_to_billed_target_november', 'yet_to_billed_target_december',
'yet_to_billed_target_january', 'yet_to_billed_target_february', 'yet_to_billed_target_march'
)
def _compute_ytd_yet_to_billed(self):
month_map = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
'january', 'february', 'march'
]
current_month = date.today().month
# In FY April to March, January=10th index
if current_month >= 4:
months_to_include = month_map[:current_month - 4 + 1]
else:
months_to_include = month_map[:current_month + 9 + 1]
for rec in self:
total = 0.0
for m in months_to_include:
total += getattr(rec, f'yet_to_billed_target_{m}', 0.0)
rec.ytd_yet_to_billed = total
def action_view_billed_lines(self):
self.ensure_one()
# Extract the month from context
month = int(self.env.context.get('month', 0))
if not month:
month = 4 # fallback to April
# You must extract year (hardcoded or dynamic based on financial year logic)
# Assuming your main model has `financial_year = 'FY 2025-2026'`
match = re.search(r'FY\s*(\d{4})', self.financial_year or '')
if match:
year = int(match.group(1))
else:
year = date.today().year # fallback
# Get month boundaries
last_day = calendar.monthrange(year, month)[1]
from_date = f'{year}-{month:02d}-01'
to_date = f'{year}-{month:02d}-{last_day:02d}'
# Fetch Billed lines
billed_lines = self.env['sos_billing_collection'].search([
('sales_person', '=', self.sales_person.id),
('action_status', '=', self.env.context.get('action')),
('date_of_action', '>=', from_date),
('date_of_action', '<=', to_date),
])
return {
'name': f"{self.env.context.get('action')} Items",
'type': 'ir.actions.act_window',
'res_model': 'sos_billed_collection_wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_billed_line_ids': [(6, 0, billed_lines.ids)],
'default_parent_id': self.id,
}
}
def action_view_brief_lines(self):
self.ensure_one()
domain = [('ref_id', '=', self.id)]
# Extract year from 'FY 2025-2026'
match = re.search(r'FY\s*(\d{4})', self.financial_year or '')
if match:
year = int(match.group(1))
else:
raise ValueError(f"Unable to extract year from financial year: {self.financial_year}")
# Filter by month if present
month = int(self.env.context.get('month', 0))
if month:
domain.append(('action_date', '>=', f'{year}-{month:02d}-01'))
last_day = calendar.monthrange(year, month)[1]
domain.append(('action_date', '<=', f'{year}-{month:02d}-{last_day:02d}'))
brief_lines = self.env['sos_sales_achievement_report_brief'].search(domain)
return {
'name': 'Achievement Brief Lines',
'type': 'ir.actions.act_window',
'res_model': 'sos_sales_achievement_report_brief_wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_brief_line_ids': [(6, 0, brief_lines.ids)],
'default_parent_id': self.id,
}
}
@api.depends(
'collected_target_april', 'collected_target_may', 'collected_target_june',
'collected_target_july', 'collected_target_august', 'collected_target_september',
'collected_target_october', 'collected_target_november', 'collected_target_december',
'collected_target_january', 'collected_target_february', 'collected_target_march'
)
def _compute_ytd_collected(self):
month_map = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
'january', 'february', 'march'
]
current_month = date.today().month
# In FY April to March, January=10th index
if current_month >= 4:
months_to_include = month_map[:current_month - 4 + 1]
else:
months_to_include = month_map[:current_month + 9 + 1]
for rec in self:
total = 0.0
for m in months_to_include:
total += getattr(rec, f'collected_target_{m}', 0.0)
rec.ytd_collected = total
@api.depends(
'billed_target_april', 'billed_target_may', 'billed_target_june',
'billed_target_july', 'billed_target_august', 'billed_target_september',
'billed_target_october', 'billed_target_november', 'billed_target_december',
'billed_target_january', 'billed_target_february', 'billed_target_march'
)
def _compute_ytd_billed(self):
month_map = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
'january', 'february', 'march'
]
current_month = date.today().month
# In FY April to March, January=10th index
if current_month >= 4:
months_to_include = month_map[:current_month - 4 + 1]
else:
months_to_include = month_map[:current_month + 9 + 1]
for rec in self:
total = 0.0
for m in months_to_include:
total += getattr(rec, f'billed_target_{m}', 0.0)
rec.ytd_billed = total
@api.depends(
'achievement_percentage_april', 'achievement_percentage_may', 'achievement_percentage_june',
'achievement_percentage_july', 'achievement_percentage_august', 'achievement_percentage_september',
'achievement_percentage_october', 'achievement_percentage_november', 'achievement_percentage_december',
'achievement_percentage_january', 'achievement_percentage_february', 'achievement_percentage_march',
)
def _compute_ytd_sales_percentage(self):
month_map = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
'january', 'february', 'march'
]
current_month = date.today().month
if current_month >= 4:
months_to_include = month_map[:current_month - 4 + 1]
else:
months_to_include = month_map[:current_month + 9 + 1]
for rec in self:
total = 0.0
count = 0
for m in months_to_include:
raw = getattr(rec, f'achievement_percentage_{m}', '0').replace('%', '').strip()
try:
val = float(raw)
total += val
count += 1
except (ValueError, TypeError):
continue
rec.ytd_sales_percentage = round(total / count, 2) if count else 0.0
@api.depends(
'actual_target_april', 'actual_target_may', 'actual_target_june',
'actual_target_july', 'actual_target_august', 'actual_target_september',
'actual_target_october', 'actual_target_november', 'actual_target_december',
'actual_target_january', 'actual_target_february', 'actual_target_march'
)
def _compute_ytd_sales_target(self):
month_map = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
'january', 'february', 'march'
]
current_month = date.today().month
# In FY April to March, January=10th index
if current_month >= 4:
months_to_include = month_map[:current_month - 4 + 1]
else:
months_to_include = month_map[:current_month + 9 + 1]
for rec in self:
total = 0.0
for m in months_to_include:
total += getattr(rec, f'actual_target_{m}', 0.0)
rec.ytd_sales = total
@api.depends(
'planned_target_april', 'planned_target_may', 'planned_target_june',
'planned_target_july', 'planned_target_august', 'planned_target_september',
'planned_target_october', 'planned_target_november', 'planned_target_december',
'planned_target_january', 'planned_target_february', 'planned_target_march'
)
def _compute_ytd_target(self):
month_map = [
'april', 'may', 'june', 'july', 'august', 'september',
'october', 'november', 'december',
'january', 'february', 'march'
]
current_month = date.today().month
# In FY April to March, January=10th index
if current_month >= 4:
months_to_include = month_map[:current_month - 4 + 1]
else:
months_to_include = month_map[:current_month + 9 + 1]
for rec in self:
total = 0.0
for m in months_to_include:
total += getattr(rec, f'planned_target_{m}', 0.0)
rec.ytd_target = total
@api.depends_context('uid')
def _compute_can_edit_billed_target(self):
for record in self:
record.can_edit_billed_target = (
self.env.user.has_group('sos_inventory.sos_finance_user') or
self.env.user.has_group('sos_inventory.sos_management_user')
)
@api.depends(
'collected_target_april','collected_target_may','collected_target_june',
'collected_target_july','collected_target_august','collected_target_september',
'collected_target_october','collected_target_november','collected_target_december',
'collected_target_january','collected_target_february','collected_target_march'
)
def _compute_collected_total_target(self):
for record in self:
record.collected_target_total = sum([
record.collected_target_april or 0,
record.collected_target_may or 0,
record.collected_target_june or 0,
record.collected_target_july or 0,
record.collected_target_august or 0,
record.collected_target_september or 0,
record.collected_target_october or 0,
record.collected_target_november or 0,
record.collected_target_december or 0,
record.collected_target_january or 0,
record.collected_target_february or 0,
record.collected_target_march or 0
])
@api.depends(
'billed_target_april','billed_target_may','billed_target_june',
'billed_target_july','billed_target_august','billed_target_september',
'billed_target_october','billed_target_november','billed_target_december',
'billed_target_january','billed_target_february','billed_target_march'
)
def _compute_billed_total_target(self):
for record in self:
record.billed_target_total = sum([
record.billed_target_april or 0,
record.billed_target_may or 0,
record.billed_target_june or 0,
record.billed_target_july or 0,
record.billed_target_august or 0,
record.billed_target_september or 0,
record.billed_target_october or 0,
record.billed_target_november or 0,
record.billed_target_december or 0,
record.billed_target_january or 0,
record.billed_target_february or 0,
record.billed_target_march or 0
])
@api.depends('actual_target_total', 'planned_target_total')
def _compute_achievement_percentage_total(self):
for record in self:
if record.planned_target_total != 0:
record.achievement_percentage_total = f"{round((record.actual_target_total / record.planned_target_total) * 100, 2)}%"
else:
record.achievement_percentage_total = "0.00%"
@api.depends(
'yet_to_billed_target_april','yet_to_billed_target_may','yet_to_billed_target_june',
'yet_to_billed_target_july','yet_to_billed_target_august','yet_to_billed_target_september',
'yet_to_billed_target_october','yet_to_billed_target_november','yet_to_billed_target_december',
'yet_to_billed_target_january','yet_to_billed_target_february','yet_to_billed_target_march'
)
def _compute_yet_to_billed_total_target(self):
for record in self:
record.yet_to_billed_target_total = sum([
record.yet_to_billed_target_april or 0,
record.yet_to_billed_target_may or 0,
record.yet_to_billed_target_june or 0,
record.yet_to_billed_target_july or 0,
record.yet_to_billed_target_august or 0,
record.yet_to_billed_target_september or 0,
record.yet_to_billed_target_october or 0,
record.yet_to_billed_target_november or 0,
record.yet_to_billed_target_december or 0,
record.yet_to_billed_target_january or 0,
record.yet_to_billed_target_february or 0,
record.yet_to_billed_target_march or 0
])
@api.depends(
'planned_target_april','planned_target_may','planned_target_june',
'planned_target_july','planned_target_august','planned_target_september',
'planned_target_october','planned_target_november','planned_target_december',
'planned_target_january','planned_target_february','planned_target_march'
)
def _compute_planned_total_target(self):
for record in self:
record.planned_target_total = sum([
record.planned_target_april or 0,
record.planned_target_may or 0,
record.planned_target_june or 0,
record.planned_target_july or 0,
record.planned_target_august or 0,
record.planned_target_september or 0,
record.planned_target_october or 0,
record.planned_target_november or 0,
record.planned_target_december or 0,
record.planned_target_january or 0,
record.planned_target_february or 0,
record.planned_target_march or 0
])
@api.depends(
'actual_target_april','actual_target_may','actual_target_june',
'actual_target_july','actual_target_august','actual_target_september',
'actual_target_october','actual_target_november','actual_target_december',
'actual_target_january','actual_target_february','actual_target_march'
)
def _compute_actual_total_target(self):
for record in self:
record.actual_target_total = sum([
record.actual_target_april or 0,
record.actual_target_may or 0,
record.actual_target_june or 0,
record.actual_target_july or 0,
record.actual_target_august or 0,
record.actual_target_september or 0,
record.actual_target_october or 0,
record.actual_target_november or 0,
record.actual_target_december or 0,
record.actual_target_january or 0,
record.actual_target_february or 0,
record.actual_target_march or 0
])
@api.depends(
'actual_target_april','actual_target_may','actual_target_june',
'actual_target_july','actual_target_august','actual_target_september',
'actual_target_october','actual_target_november','actual_target_december',
'actual_target_january','actual_target_february','actual_target_march'
)
def _compute_achievement_percentage(self):
# Loop over all months and calculate achievement percentage dynamically
for record in self:
for month in ['april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december', 'january', 'february', 'march']:
planned_field = f"planned_target_{month}"
actual_field = f"actual_target_{month}"
achievement_field = f"achievement_percentage_{month}"
planned_value = getattr(record, planned_field)
actual_value = getattr(record, actual_field)
if planned_value != 0:
percentage = (actual_value / planned_value) * 100
setattr(record, achievement_field, f"{percentage:.2f}%")
else:
setattr(record, achievement_field, "0.00%")
def _get_financial_year(self):
current_date = date.today()
start_year = current_date.year if current_date.month >= 4 else current_date.year - 1
end_year = start_year + 1
return f"FY {start_year}-{end_year}"
def action_report_achievement_btn(self):
try:
action = self.env.ref("sos_sales.action_report_achievement").with_context(landscape=True).report_action(self)
return action
except ValueError as e:
print(f"Failed to find report action: {e}")
class SOS_Sales_Achievement_Report_Brief(models.Model):
_name = 'sos_sales_achievement_report_brief'
_description = 'Achievement Brief'
_order = 'action_date desc'
ref_id = fields.Many2one('sos_sales_achievement_report', string="Financial Year", ondelete="cascade")
customer_name = fields.Many2one('sos_customers',string="Customer Name", required=True)
action_date = fields.Date(string="QP No")
currency_id = fields.Many2one(
'res.currency',
string='Currency',
default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
)
proposal_value = fields.Monetary(currency_field='currency_id',string="Proposal Value(In Lakhs)")
class SOS_Sales_Achievement_Report_Brief_Wizard(models.TransientModel):
_name = 'sos_sales_achievement_report_brief_wizard'
_description = 'Achievement Brief Wizard'
parent_id = fields.Many2one('sos_sales_achievement_report', string="Sales Achievement Report")
brief_line_ids = fields.Many2many(
'sos_sales_achievement_report_brief',
relation='sos_brief_wizard_rel', # Custom table name
string="Brief Lines",
readonly=True
)
class SosBilledCollectionWizard(models.TransientModel):
_name = 'sos_billed_collection_wizard'
_description = 'Billed Collection Lines Wizard'
parent_id = fields.Many2one('sos_billing_collection') # adjust accordingly
billed_line_ids = fields.Many2many(
'sos_billing_collection',
string="Billed Lines",
readonly=True,
)