1067 lines
48 KiB
Python
Executable File
1067 lines
48 KiB
Python
Executable File
|
||
from odoo import models, fields, api
|
||
from datetime import date, timedelta,datetime
|
||
import re
|
||
import calendar
|
||
from odoo.exceptions import UserError
|
||
|
||
|
||
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.')
|
||
]
|
||
display_name = fields.Char(string="Name", compute='_compute_display_name', store=True)
|
||
category = fields.Selection([
|
||
('sales', 'Sales'),
|
||
('export', 'Export')
|
||
], string="Category", required=True, default='sales')
|
||
financial_year = fields.Char(string="Financial Year", default=lambda self: self._get_financial_year())
|
||
sales_person = fields.Many2one('res.users', string='Sales person')
|
||
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",compute="_compute_yet_to_billed_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",compute="_compute_yet_to_billed_targets")
|
||
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.Float(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")
|
||
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)
|
||
billing_collection_line_ids = fields.One2many(
|
||
'sos_billing_collection',
|
||
'ref_id',
|
||
string="Billing Collection Lines"
|
||
)
|
||
def action_yet_to_bill(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': 'Yet To Bill',
|
||
'res_model': 'yet_to_bill_wizard',
|
||
'view_mode': 'form',
|
||
'target': 'new',
|
||
'context': {
|
||
'brief_ref_id': self.id, # scopes wizard to this report's brief lines
|
||
}
|
||
}
|
||
@api.depends('category','financial_year', 'sales_person')
|
||
def _compute_display_name(self):
|
||
for record in self:
|
||
if record.category == "sales":
|
||
record.display_name = f"{record.financial_year or ''} / {record.sales_person.name or ''}"
|
||
else:
|
||
record.display_name = f"{record.financial_year or ''} / Export"
|
||
|
||
|
||
@api.depends('opening_balance', 'actual_target_april', 'billed_target_april')
|
||
def _compute_yet_to_billed_april(self):
|
||
for rec in self:
|
||
rec.yet_to_billed_target_april = (rec.opening_balance or 0.0) + \
|
||
(rec.actual_target_april or 0.0) - \
|
||
(rec.billed_target_april or 0.0)
|
||
|
||
@api.depends(
|
||
'yet_to_billed_target_april', # link to April
|
||
'actual_target_may', 'billed_target_may',
|
||
'actual_target_june', 'billed_target_june',
|
||
'actual_target_july', 'billed_target_july',
|
||
'actual_target_august', 'billed_target_august',
|
||
'actual_target_september', 'billed_target_september',
|
||
'actual_target_october', 'billed_target_october',
|
||
'actual_target_november', 'billed_target_november',
|
||
'actual_target_december', 'billed_target_december',
|
||
'actual_target_january', 'billed_target_january',
|
||
'actual_target_february', 'billed_target_february',
|
||
'actual_target_march', 'billed_target_march',
|
||
)
|
||
def _compute_yet_to_billed_targets(self):
|
||
month_order = [
|
||
'may', 'june', 'july', 'august', 'september',
|
||
'october', 'november', 'december',
|
||
'january', 'february', 'march'
|
||
]
|
||
|
||
for rec in self:
|
||
prev_yet_to_bill = rec.yet_to_billed_target_april or 0.0
|
||
for month in month_order:
|
||
actual = getattr(rec, f'actual_target_{month}', 0.0) or 0.0
|
||
billed = getattr(rec, f'billed_target_{month}', 0.0) or 0.0
|
||
result = prev_yet_to_bill + actual - billed
|
||
setattr(rec, f'yet_to_billed_target_{month}', result)
|
||
prev_yet_to_bill = result
|
||
|
||
|
||
|
||
|
||
def action_view_billed_lines(self):
|
||
self.ensure_one()
|
||
|
||
month = int(self.env.context.get('month', 4)) # default April
|
||
action = self.env.context.get('action', 'Collected')
|
||
if action == 'Billed':
|
||
view_id = self.env.ref('sos_sales.view_sos_billed_collection_wizard_form_billed').id
|
||
else:
|
||
view_id = self.env.ref('sos_sales.view_sos_billed_collection_wizard_form_collected').id
|
||
|
||
match = re.search(r'FY\s*(\d{4})', self.financial_year or '')
|
||
year = int(match.group(1)) if match else date.today().year
|
||
|
||
last_day = calendar.monthrange(year, month)[1]
|
||
from_date = date(year, month, 1)
|
||
to_date = date(year, month, last_day)
|
||
if self.category == "sales":
|
||
sales_person_id = self.sales_person.id or self.ref('sales_person').id
|
||
|
||
if not sales_person_id:
|
||
raise UserError("Sales person is missing in this record.")
|
||
|
||
billed_lines = self.env['sos_billing_collection'].search([
|
||
('sales_person', '=', sales_person_id),
|
||
('action_status', '=', action),
|
||
('date_of_action', '>=', from_date),
|
||
('date_of_action', '<=', to_date),
|
||
])
|
||
else:
|
||
billed_lines = self.env['sos_billing_collection'].search([
|
||
('category', '=', "export"),
|
||
('action_status', '=', action),
|
||
('date_of_action', '>=', from_date),
|
||
('date_of_action', '<=', to_date),
|
||
])
|
||
wizard = self.env['sos_billed_collection_wizard'].create({
|
||
'main_parent_id': self.id,
|
||
'billed_line_ids': [(6, 0, billed_lines.ids)],
|
||
'action_status': action,
|
||
})
|
||
|
||
return {
|
||
'name': f"{self.env.context.get('action')} Items",
|
||
'type': 'ir.actions.act_window',
|
||
'res_model': 'sos_billed_collection_wizard',
|
||
'view_mode': 'form',
|
||
'view_id': view_id,
|
||
'target': 'new',
|
||
'res_id': wizard.id,
|
||
}
|
||
|
||
# def action_view_brief_lines_acc(self):
|
||
# return {
|
||
# 'name': "Action",
|
||
# 'type': 'ir.actions.act_window',
|
||
# 'res_model': 'sos_sales_achievement_report_brief',
|
||
# 'view_mode': 'form',
|
||
# 'view_id': self.env.ref('sos_sales.view_form_sos_sales_achievement_acc').id,
|
||
# 'target': 'new',
|
||
# 'context': {
|
||
# 'ref_record_id': self.id
|
||
# }
|
||
# }
|
||
def action_view_brief_lines_acc(self):
|
||
self.ensure_one()
|
||
current_date = date.today()
|
||
start_year = current_date.year if current_date.month >= 4 else current_date.year - 1
|
||
end_year = start_year + 1
|
||
current_fy = f"FY {start_year}-{end_year}"
|
||
domain = [
|
||
('ref_record_id', '=', self.id),
|
||
('financial_year', '!=', current_fy),
|
||
]
|
||
|
||
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,
|
||
}
|
||
}
|
||
def action_view_brief_lines(self):
|
||
self.ensure_one()
|
||
domain = [('ref_id', '=', self.id)]
|
||
# Extract starting 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}")
|
||
|
||
month = int(self.env.context.get('month', 0))
|
||
|
||
if month:
|
||
# Filter specific 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}'))
|
||
else:
|
||
# Filter for entire financial year (April to March)
|
||
start_date = f'{year}-04-01'
|
||
end_date = f'{year + 1}-03-31'
|
||
domain.append(('action_date', '>=', start_date))
|
||
domain.append(('action_date', '<=', end_date))
|
||
|
||
brief_lines = self.env['sos_sales_achievement_report_brief'].search(domain)
|
||
total_proposal_value=0
|
||
for eachrecord in brief_lines:
|
||
total_proposal_value += eachrecord.proposal_value
|
||
|
||
if month:
|
||
month_name = calendar.month_name[month].lower()
|
||
field_name = f"actual_target_{month_name}"
|
||
self[field_name] = total_proposal_value
|
||
|
||
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 = round(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
|
||
]), 2)
|
||
|
||
@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 = round(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
|
||
]), 2)
|
||
@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:
|
||
# Get current month name in lowercase
|
||
month_num = date.today().month
|
||
month_name = calendar.month_name[month_num].lower() # e.g., "august"
|
||
|
||
# Build the dynamic field name
|
||
field_name = f"yet_to_billed_target_{month_name}"
|
||
record.ytd_yet_to_billed = round(getattr(record, field_name) or 0, 2)
|
||
record.yet_to_billed_target_total = round(getattr(record, field_name) or 0, 2)
|
||
|
||
@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'
|
||
_rec_name = 'po_no'
|
||
def get_financial_year_selection(self):
|
||
current_date = date.today()
|
||
start_year = 2024 # starting from FY 2024-2025
|
||
current_year = current_date.year if current_date.month >= 4 else current_date.year - 1
|
||
end_year = current_year + 1
|
||
|
||
selection = []
|
||
for y in range(start_year, current_year + 1):
|
||
fy = f"FY {y}-{y+1}"
|
||
selection.append((fy, fy))
|
||
return selection
|
||
ref_id = fields.Many2one('sos_sales_achievement_report', string="Financial Year", ondelete="cascade")
|
||
ref_record_id = fields.Integer(string="Reference")
|
||
customer_name = fields.Many2one('sos_customers',string="Customer Name", required=True)
|
||
action_date = fields.Date(string="PO Date")
|
||
currency_id = fields.Many2one(
|
||
'res.currency',
|
||
string='Currency',
|
||
default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
|
||
)
|
||
financial_year = fields.Selection(
|
||
selection=get_financial_year_selection,
|
||
string="Financial Year",
|
||
required=True
|
||
)
|
||
category = fields.Selection([
|
||
('sales', 'Sales'),
|
||
('export', 'Export')
|
||
], string="Category", required=True,related="ref_id.category")
|
||
sales_person = fields.Many2one('res.users', string='Sales person',related="ref_id.sales_person",store=True)
|
||
po_no = fields.Char(string="PO No")
|
||
invoice_no = fields.Char(string="Invoice No")
|
||
proposal_value = fields.Monetary(currency_field='currency_id',string="PO Value(In Lakhs)")
|
||
billed_date = fields.Date(string="Billed Date")
|
||
billed_amount = fields.Monetary(currency_field='currency_id',string="Billed Value(In Lakhs)")
|
||
|
||
def delete_achievement_line(self):
|
||
|
||
Env = self.env
|
||
Line = Env['sos_sales_achievement_report_brief']
|
||
Parent = Env['sos_sales_achievement_report']
|
||
|
||
# 1) Collect affected month windows per parent (no sums yet)
|
||
# { parent_id: set((cal_year, month_num)) }
|
||
affected = {}
|
||
|
||
for rec in self:
|
||
# resolve parent id (prefer Many2one 'ref_id'; fallback to 'ref_record_id')
|
||
parent_id = False
|
||
if hasattr(rec, 'ref_id') and rec.ref_id:
|
||
parent_id = rec.ref_id.id
|
||
elif hasattr(rec, 'ref_record_id') and rec.ref_record_id:
|
||
parent_id = getattr(rec.ref_record_id, 'id', rec.ref_record_id)
|
||
if not parent_id:
|
||
continue
|
||
|
||
d = fields.Date.to_date(rec.action_date) or fields.Date.context_today(self)
|
||
month_num = d.month # 1..12
|
||
|
||
fy_text = (rec.financial_year or '').strip()
|
||
m = re.search(r'FY\s*(\d{4})', fy_text)
|
||
if not m:
|
||
# skip this line if FY is malformed
|
||
continue
|
||
fy_start = int(m.group(1))
|
||
|
||
# Apr–Dec => start year; Jan–Mar => start year+1
|
||
cal_year = fy_start if month_num >= 4 else fy_start + 1
|
||
|
||
affected.setdefault(parent_id, set()).add((cal_year, month_num))
|
||
|
||
# 2) Delete the lines first
|
||
super(type(self), self).unlink()
|
||
|
||
# 3) Recompute totals from remaining lines and write to parent
|
||
for parent_id, months in affected.items():
|
||
write_vals = {}
|
||
for cal_year, month_num in months:
|
||
last_day = calendar.monthrange(cal_year, month_num)[1]
|
||
start_str = f"{cal_year}-{month_num:02d}-01"
|
||
end_str = f"{cal_year}-{month_num:02d}-{last_day:02d}"
|
||
|
||
total = sum(Line.search([
|
||
('ref_id', '=', parent_id),
|
||
('action_date', '>=', start_str),
|
||
('action_date', '<=', end_str),
|
||
]).mapped('proposal_value')) or 0.0
|
||
|
||
field_name = f"actual_target_{calendar.month_name[month_num].lower()}"
|
||
if field_name in Parent._fields:
|
||
write_vals[field_name] = total
|
||
|
||
if write_vals:
|
||
Parent.browse(parent_id).write(write_vals)
|
||
|
||
return True
|
||
|
||
def open_line_form(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': 'Edit Line',
|
||
'res_model': self._name,
|
||
'res_id': self.id,
|
||
'view_mode': 'form',
|
||
'target': 'new', # Opens as a popup
|
||
}
|
||
def _get_financial_year(self,current_date):
|
||
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 write(self, vals):
|
||
res = super().write(vals)
|
||
|
||
# avoid infinite loop when we write our computed monthly field
|
||
if self.env.context.get('_skip_month_compute'):
|
||
return res
|
||
|
||
for rec in self:
|
||
# take incoming values if present, else current record values
|
||
action_date_any = vals.get('action_date', rec.action_date)
|
||
fy_text = (vals.get('financial_year', rec.financial_year) or '').strip()
|
||
|
||
# normalize to date
|
||
d = fields.Date.to_date(action_date_any) or fields.Date.context_today(self)
|
||
month_num = d.month # 1..12
|
||
|
||
# FY like "FY 2025-2026" -> start year 2025
|
||
m = re.search(r'FY\s*(\d{4})', fy_text)
|
||
if not m:
|
||
raise ValueError(f"Unable to extract year from financial year: {fy_text}")
|
||
fy_start = int(m.group(1))
|
||
|
||
# Apr–Dec belong to start year; Jan–Mar to start year+1
|
||
cal_year = fy_start if month_num >= 4 else fy_start + 1
|
||
|
||
# monthly date window
|
||
last_day = calendar.monthrange(cal_year, month_num)[1]
|
||
start_str = f"{cal_year}-{month_num:02d}-01"
|
||
end_str = f"{cal_year}-{month_num:02d}-{last_day:02d}"
|
||
|
||
# sum for this record in that month
|
||
domain = [
|
||
('ref_id', '=', self.ref_record_id),
|
||
('action_date', '>=', start_str),
|
||
('action_date', '<=', end_str),
|
||
]
|
||
lines = rec.env['sos_sales_achievement_report_brief'].search(domain)
|
||
total_proposal_value=0
|
||
for eachrecord in lines:
|
||
total_proposal_value += eachrecord.proposal_value
|
||
record = self.env['sos_sales_achievement_report'].search(
|
||
[('id', '=', self.ref_record_id)],
|
||
limit=1
|
||
)
|
||
month_name = calendar.month_name[month_num].lower()
|
||
field_name = f"actual_target_{month_name}"
|
||
field_name = f"actual_target_{month_name}"
|
||
record.write({
|
||
field_name : total_proposal_value
|
||
})
|
||
|
||
|
||
return res
|
||
@api.model
|
||
def create(self, vals):
|
||
ref_id = vals.get('ref_id')
|
||
vals['ref_record_id'] = ref_id
|
||
|
||
action_date = vals.get('action_date')
|
||
billed_date = vals.get('billed_date')
|
||
customer_name = vals.get('customer_name')
|
||
value = vals.get('proposal_value', 0.0)
|
||
billed_value = vals.get('billed_amount', 0.0)
|
||
ref_id = vals.get('ref_id')
|
||
|
||
if isinstance(action_date, str):
|
||
action_date = datetime.strptime(action_date, '%Y-%m-%d').date()
|
||
if isinstance(billed_date, str):
|
||
billed_date = datetime.strptime(billed_date, '%Y-%m-%d').date()
|
||
|
||
if ref_id and action_date:
|
||
report = self.env['sos_sales_achievement_report'].browse(ref_id)
|
||
month_name = action_date.strftime('%B').lower()
|
||
actual_field = f"actual_target_{month_name}"
|
||
current_date = date.today()
|
||
start_year = current_date.year if current_date.month >= 4 else current_date.year - 1
|
||
end_year = start_year + 1
|
||
current_fy = f"FY {start_year}-{end_year}"
|
||
if vals.get('financial_year') != current_fy:
|
||
report.write({'opening_balance': report.opening_balance + value})
|
||
|
||
|
||
if vals.get('financial_year') == current_fy:
|
||
if hasattr(report, actual_field):
|
||
current_val = getattr(report, actual_field) or 0.0
|
||
report.write({actual_field: current_val + value})
|
||
|
||
if billed_date:
|
||
self.env['sos_billing_collection'].create({
|
||
'ref_id': report.id,
|
||
'customer_name': customer_name,
|
||
'sales_person': report.sales_person.id,
|
||
'action_status': 'Billed',
|
||
'date_of_action': billed_date,
|
||
'value': billed_value,
|
||
'po_no':vals.get('po_no')
|
||
})
|
||
new_record = super(SOS_Sales_Achievement_Report_Brief, self).create(vals)
|
||
return new_record
|
||
|
||
|
||
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
|
||
)
|
||
|
||
|
||
def action_add_new_brief_line(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': 'Add Brief Line',
|
||
'res_model': 'sos_sales_achievement_report_brief',
|
||
'view_mode': 'form',
|
||
'target': 'new',
|
||
'context': {
|
||
'default_ref_id': self.parent_id.id,
|
||
'default_currency_id': self.env.ref('base.INR').id,
|
||
}
|
||
}
|
||
class SosBilledCollectionWizard(models.TransientModel):
|
||
_name = 'sos_billed_collection_wizard'
|
||
_description = 'Billed Collection Lines Wizard'
|
||
|
||
main_parent_id = fields.Many2one('sos_sales_achievement_report', string="Sales Achievement Report")
|
||
billed_line_ids = fields.Many2many(
|
||
'sos_billing_collection',
|
||
string="Billed Lines",
|
||
readonly=True,
|
||
domain="[('ref_id', '=', main_parent_id)]"
|
||
)
|
||
action_status = fields.Selection([
|
||
('Billed', 'Billed'),
|
||
('Collected', 'Collected')
|
||
], string='Action Status', readonly=True)
|
||
@api.model
|
||
def default_get(self, fields_list):
|
||
res = super(SosBilledCollectionWizard, self).default_get(fields_list)
|
||
main_parent_id = self.env.context.get('active_id')
|
||
if main_parent_id and self.env.context.get('active_model') == 'sos_sales_achievement_report':
|
||
res['main_parent_id'] = main_parent_id
|
||
# Populate billed_line_ids with related sos_billing_collection records
|
||
billing_lines = self.env['sos_billing_collection'].search([('ref_id', '=', main_parent_id)])
|
||
res['billed_line_ids'] = [(6, 0, billing_lines.ids)]
|
||
return res
|
||
|
||
def action_add_new_collection_line(self):
|
||
self.ensure_one()
|
||
if not self.main_parent_id:
|
||
raise UserError("No Sales Achievement Report found.")
|
||
if self.action_status == "Collected":
|
||
form_title="Collection"
|
||
view_xml_id="view_sos_billing_collection_minimal_form_collection"
|
||
else:
|
||
form_title="Billed"
|
||
view_xml_id="view_sos_billing_collection_minimal_form_billed"
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': f'Add {form_title}',
|
||
'res_model': 'sos_billing_collection',
|
||
'view_mode': 'form',
|
||
'view_id': self.env.ref(f'sos_sales.{view_xml_id}').id,
|
||
|
||
'target': 'new',
|
||
'context': {
|
||
'default_ref_id': self.main_parent_id.id,
|
||
'default_action_status': self.action_status,
|
||
'default_sales_person': self.main_parent_id.sales_person.id if self.main_parent_id.sales_person else False,
|
||
}
|
||
}
|
||
class SosBillingCollection(models.Model):
|
||
_name = 'sos_billing_collection'
|
||
_description = 'Billing & Collection Details'
|
||
|
||
|
||
ref_id = fields.Many2one('sos_sales_achievement_report', ondelete="cascade", required=True)
|
||
category = fields.Selection([
|
||
('sales', 'Sales'),
|
||
('export', 'Export')
|
||
], string="Category", required=True,related="ref_id.category")
|
||
customer_name = fields.Many2one('sos_customers', string="Customer Name")
|
||
sales_person = fields.Many2one(
|
||
'res.users',
|
||
string='Sales Executive',
|
||
related="ref_id.sales_person",
|
||
store=True
|
||
)
|
||
action_status = fields.Selection([
|
||
('Billed', 'Billed'),
|
||
('Collected', 'Collected')
|
||
], string='Action')
|
||
date_of_action = fields.Date(string="Date")
|
||
currency_id = fields.Many2one(
|
||
'res.currency',
|
||
string='Currency',
|
||
default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
|
||
)
|
||
value = fields.Monetary(currency_field='currency_id', string="Value (In Lakhs)")
|
||
customer_name = fields.Many2one('sos_customers',string="Customer Name")
|
||
po_id = fields.Many2one(
|
||
'sos_sales_achievement_report_brief', # or 'sos_sales_achievement_report' if PO lives there
|
||
string="PO No",
|
||
domain="[('po_no','!=', False), ('customer_name', '=', customer_name)]",
|
||
)
|
||
po_no = fields.Char(string="PO No", related='po_id.po_no', store=True, readonly=True)
|
||
invoice_no = fields.Char(string="Invoice No")
|
||
products = fields.Selection(
|
||
[
|
||
('BHMS 1.2V', 'BHMS 1.2V'),
|
||
('BHMS 2V', 'BHMS 2V'),
|
||
('BHMS 12V', 'BHMS 12V'),
|
||
('BHMS 48V', 'BHMS 48V'),
|
||
('BMS-HV', 'BMS-HV'),
|
||
('BMS-LV 100A', 'BMS-LV 100A'),
|
||
('BMS-LV 40A', 'BMS-LV 40A'),
|
||
('SBMS 55A', 'SBMS 55A'),
|
||
('MC 250W', 'MC 250W'),
|
||
('HeartTarang', 'HeartTarang')
|
||
],
|
||
string="Products")
|
||
quantity = fields.Integer(string="Quantity")
|
||
def _get_financial_year(self, current_date):
|
||
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 delete_form_line(self):
|
||
self.unlink()
|
||
def write_line_form(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': 'Edit Line',
|
||
'res_model': self._name,
|
||
'res_id': self.id,
|
||
'view_mode': 'form',
|
||
'target': 'new', # Opens as a popup
|
||
}
|
||
def unlink(self):
|
||
for record in self:
|
||
|
||
if record.date_of_action:
|
||
fy = self._get_financial_year(record.date_of_action)
|
||
month_name = record.date_of_action.strftime('%B').lower()
|
||
else:
|
||
fy = False
|
||
month_name = False
|
||
|
||
if fy and month_name:
|
||
actual_field_name = f"{record.action_status.lower()}_target_{month_name}"
|
||
|
||
# Fetch related record where field update should happen (example logic)
|
||
report = self.env['sos_sales_achievement_report'].search([
|
||
('financial_year', '=', fy),
|
||
('sales_person', '=', record.sales_person.id)
|
||
], limit=1)
|
||
|
||
if report and hasattr(report, actual_field_name):
|
||
current_val = getattr(report, actual_field_name, 0.0)
|
||
setattr(report, actual_field_name, current_val - record.value)
|
||
|
||
return super(SosBillingCollection, self).unlink()
|
||
def write(self, vals):
|
||
for record in self:
|
||
# Capture old values
|
||
old_date = record.date_of_action
|
||
old_status = record.action_status
|
||
old_value = record.value
|
||
|
||
# Determine if any key field is changing
|
||
new_date = vals.get('date_of_action', old_date)
|
||
new_status = vals.get('action_status', old_status)
|
||
new_value = vals.get('value', old_value)
|
||
|
||
# Convert date if needed
|
||
if isinstance(new_date, str):
|
||
new_date = datetime.strptime(new_date, '%Y-%m-%d').date()
|
||
|
||
# Compute old and new financial year/month
|
||
old_fy = self._get_financial_year(old_date) if old_date else None
|
||
old_month = old_date.strftime('%B').lower() if old_date else None
|
||
new_fy = self._get_financial_year(new_date) if new_date else None
|
||
new_month = new_date.strftime('%B').lower() if new_date else None
|
||
|
||
# Build field names
|
||
old_field = f"{old_status.lower()}_target_{old_month}" if old_month else None
|
||
new_field = f"{new_status.lower()}_target_{new_month}" if new_month else None
|
||
|
||
# Fetch related report
|
||
years = [fy for fy in [old_fy, new_fy] if fy]
|
||
report = self.env['sos_sales_achievement_report'].search([
|
||
('sales_person', '=', record.sales_person.id),
|
||
('financial_year', 'in', years)
|
||
], limit=1)
|
||
|
||
|
||
if report:
|
||
# Subtract old value
|
||
if old_field and hasattr(report, old_field):
|
||
current_old = getattr(report, old_field, 0.0) or 0.0
|
||
setattr(report, old_field, current_old - old_value)
|
||
|
||
# Add new value
|
||
if new_field and hasattr(report, new_field):
|
||
current_new = getattr(report, new_field, 0.0) or 0.0
|
||
setattr(report, new_field, current_new + new_value)
|
||
|
||
# Proceed with actual write
|
||
return super(SosBillingCollection, self).write(vals)
|
||
|
||
@api.model
|
||
def create(self, vals):
|
||
if not vals.get('ref_id'):
|
||
raise UserError("Cannot create record without a valid Sales Achievement Report.")
|
||
|
||
action_date = vals.get('date_of_action')
|
||
action_status = vals.get('action_status').lower()
|
||
if isinstance(action_date, str):
|
||
action_date = datetime.strptime(action_date, '%Y-%m-%d').date()
|
||
|
||
fy = self._get_financial_year(action_date) if action_date else False
|
||
month_name = action_date.strftime('%B').lower() if action_date else False
|
||
final_value = vals.get('value', 0.0)
|
||
actual_field = f"{action_status}_target_{month_name}" if month_name else False
|
||
|
||
record = super(SosBillingCollection, self).create(vals)
|
||
|
||
# Update the related sos_sales_achievement_report by adding value
|
||
if vals.get('ref_id') and month_name:
|
||
report = self.env['sos_sales_achievement_report'].search([
|
||
('id', '=', vals.get('ref_id')),
|
||
('financial_year', '=', fy),
|
||
])
|
||
if report and hasattr(report, actual_field):
|
||
current_value = getattr(report, actual_field, 0.0) or 0.0
|
||
new_value = current_value + final_value
|
||
report.write({actual_field: new_value})
|
||
|
||
return record
|
||
|