mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-04 20:59:11 +00:00
feat: commonified item group wise target variance report
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.accounts.report.financial_statements import get_period_list
|
||||
from erpnext.accounts.doctype.monthly_distribution.monthly_distribution import get_periodwise_distribution_data
|
||||
|
||||
def get_data_column(filters, partner_doctype):
|
||||
data = []
|
||||
period_list = get_period_list(filters.fiscal_year, filters.fiscal_year,
|
||||
filters.period, company=filters.company)
|
||||
|
||||
rows = get_data(filters, period_list, partner_doctype)
|
||||
columns = get_columns(filters, period_list, partner_doctype)
|
||||
|
||||
if not rows:
|
||||
return columns, data
|
||||
|
||||
for key, value in rows.items():
|
||||
value.update({
|
||||
frappe.scrub(partner_doctype): key[0],
|
||||
'item_group': key[1]
|
||||
})
|
||||
|
||||
data.append(value)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_data(filters, period_list, partner_doctype):
|
||||
sales_field = frappe.scrub(partner_doctype)
|
||||
sales_users_data = get_parents_data(filters, partner_doctype)
|
||||
|
||||
if not sales_users_data: return
|
||||
sales_users, item_groups = [], []
|
||||
|
||||
for d in sales_users_data:
|
||||
if d.parent not in sales_users:
|
||||
sales_users.append(d.parent)
|
||||
|
||||
if d.item_group not in item_groups:
|
||||
item_groups.append(d.item_group)
|
||||
|
||||
date_field = ("transaction_date"
|
||||
if filters.get('doctype') == "Sales Order" else "posting_date")
|
||||
|
||||
actual_data = get_actual_data(filters, item_groups, sales_users, date_field, sales_field)
|
||||
|
||||
return prepare_data(filters, sales_users_data,
|
||||
actual_data, date_field, period_list, sales_field)
|
||||
|
||||
def get_columns(filters, period_list, partner_doctype):
|
||||
fieldtype, options = "Currency", "currency"
|
||||
|
||||
if filters.get("target_on") == 'Quantity':
|
||||
fieldtype, options = "Float", ""
|
||||
|
||||
columns = [{
|
||||
"fieldname": frappe.scrub(partner_doctype),
|
||||
"label": _(partner_doctype),
|
||||
"fieldtype": "Link",
|
||||
"options": partner_doctype,
|
||||
"width": 100
|
||||
}, {
|
||||
"fieldname": "item_group",
|
||||
"label": _("Item Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item Group",
|
||||
"width": 100
|
||||
}]
|
||||
|
||||
for period in period_list:
|
||||
target_key = 'target_{}'.format(period.key)
|
||||
variance_key = 'variance_{}'.format(period.key)
|
||||
|
||||
columns.extend([{
|
||||
"fieldname": target_key,
|
||||
"label": _("Target ({})".format(period.label)),
|
||||
"fieldtype": fieldtype,
|
||||
"options": options,
|
||||
"width": 100
|
||||
}, {
|
||||
"fieldname": period.key,
|
||||
"label": _("Achieved ({})".format(period.label)),
|
||||
"fieldtype": fieldtype,
|
||||
"options": options,
|
||||
"width": 100
|
||||
}, {
|
||||
"fieldname": variance_key,
|
||||
"label": _("Variance ({})".format(period.label)),
|
||||
"fieldtype": fieldtype,
|
||||
"options": options,
|
||||
"width": 100
|
||||
}])
|
||||
|
||||
columns.extend([{
|
||||
"fieldname": "total_target",
|
||||
"label": _("Total Target"),
|
||||
"fieldtype": fieldtype,
|
||||
"options": options,
|
||||
"width": 100
|
||||
}, {
|
||||
"fieldname": "total_achieved",
|
||||
"label": _("Total Achieved"),
|
||||
"fieldtype": fieldtype,
|
||||
"options": options,
|
||||
"width": 100
|
||||
}, {
|
||||
"fieldname": "total_variance",
|
||||
"label": _("Total Variance"),
|
||||
"fieldtype": fieldtype,
|
||||
"options": options,
|
||||
"width": 100
|
||||
}])
|
||||
|
||||
return columns
|
||||
|
||||
def prepare_data(filters, sales_users_data, actual_data, date_field, period_list, sales_field):
|
||||
rows = {}
|
||||
|
||||
target_qty_amt_field = ("target_qty"
|
||||
if filters.get("target_on") == 'Quantity' else "target_amount")
|
||||
|
||||
qty_or_amount_field = ("stock_qty"
|
||||
if filters.get("target_on") == 'Quantity' else "base_net_amount")
|
||||
|
||||
for d in sales_users_data:
|
||||
key = (d.parent, d.item_group)
|
||||
dist_data = get_periodwise_distribution_data(d.distribution_id, period_list, filters.get("period"))
|
||||
|
||||
if key not in rows:
|
||||
rows.setdefault(key,{
|
||||
'total_target': 0,
|
||||
'total_achieved': 0,
|
||||
'total_variance': 0
|
||||
})
|
||||
|
||||
details = rows[key]
|
||||
for period in period_list:
|
||||
p_key = period.key
|
||||
if p_key not in details:
|
||||
details[p_key] = 0
|
||||
|
||||
target_key = 'target_{}'.format(p_key)
|
||||
variance_key = 'variance_{}'.format(p_key)
|
||||
details[target_key] = (d.get(target_qty_amt_field) * dist_data.get(p_key)) / 100
|
||||
details[variance_key] = 0
|
||||
details["total_target"] += details[target_key]
|
||||
|
||||
for r in actual_data:
|
||||
if (r.get(sales_field) == d.parent and r.item_group == d.item_group and
|
||||
period.from_date <= r.get(date_field) and r.get(date_field) <= period.to_date):
|
||||
details[p_key] += r.get(qty_or_amount_field, 0)
|
||||
details[variance_key] = details.get(target_key) - details.get(p_key)
|
||||
|
||||
details["total_achieved"] += details.get(p_key)
|
||||
details["total_variance"] = details.get("total_target") - details.get("total_achieved")
|
||||
|
||||
return rows
|
||||
|
||||
def get_actual_data(filters, item_groups, sales_users_or_territory_data, date_field, sales_field):
|
||||
fiscal_year = get_fiscal_year(fiscal_year=filters.get("fiscal_year"), as_dict=1)
|
||||
dates = [fiscal_year.year_start_date, fiscal_year.year_end_date]
|
||||
|
||||
select_field = "`tab{0}`.{1}".format(filters.get("doctype"), sales_field)
|
||||
child_table = "`tab{0}`".format(filters.get("doctype") + ' Item')
|
||||
|
||||
if sales_field == 'sales_person':
|
||||
select_field = "`tabSales Team`.sales_person"
|
||||
child_table = "`tab{0}`, `tabSales Team`".format(filters.get("doctype") + ' Item')
|
||||
cond = """`tabSales Team`.parent = `tab{0}`.name and
|
||||
`tabSales Team`.sales_person in ({1}) """.format(filters.get("doctype"),
|
||||
','.join(['%s'] * len(sales_users_or_territory_data)))
|
||||
else:
|
||||
cond = "`tab{0}`.{1} in ({2})".format(filters.get("doctype"), sales_field,
|
||||
','.join(['%s'] * len(sales_users_or_territory_data)))
|
||||
|
||||
return frappe.db.sql(""" SELECT `tab{child_doc}`.item_group,
|
||||
`tab{child_doc}`.stock_qty, `tab{child_doc}`.base_net_amount,
|
||||
{select_field}, `tab{parent_doc}`.{date_field}
|
||||
FROM `tab{parent_doc}`, {child_table}
|
||||
WHERE
|
||||
`tab{child_doc}`.parent = `tab{parent_doc}`.name
|
||||
and `tab{parent_doc}`.docstatus = 1 and {cond}
|
||||
and `tab{child_doc}`.item_group in ({item_groups})
|
||||
and `tab{parent_doc}`.{date_field} between %s and %s"""
|
||||
.format(
|
||||
cond = cond,
|
||||
date_field = date_field,
|
||||
select_field = select_field,
|
||||
child_table = child_table,
|
||||
parent_doc = filters.get("doctype"),
|
||||
child_doc = filters.get("doctype") + ' Item',
|
||||
item_groups = ','.join(['%s'] * len(item_groups))
|
||||
), tuple(sales_users_or_territory_data + item_groups + dates), as_dict=1, debug=1)
|
||||
|
||||
def get_parents_data(filters, partner_doctype):
|
||||
filters_dict = {'parenttype': partner_doctype}
|
||||
|
||||
target_qty_amt_field = ("target_qty"
|
||||
if filters.get("target_on") == 'Quantity' else "target_amount")
|
||||
|
||||
if filters.get("fiscal_year"):
|
||||
filters_dict["fiscal_year"] = filters.get("fiscal_year")
|
||||
|
||||
return frappe.get_all('Target Detail',
|
||||
filters = filters_dict,
|
||||
fields = ["parent", "item_group", target_qty_amt_field, "fiscal_year", "distribution_id"])
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Sales Partner Target Variance Item Group-Wise"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname:"company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
fieldname: "fiscal_year",
|
||||
label: __("Fiscal Year"),
|
||||
fieldtype: "Link",
|
||||
options: "Fiscal Year",
|
||||
default: frappe.sys_defaults.fiscal_year
|
||||
},
|
||||
{
|
||||
fieldname: "doctype",
|
||||
label: __("Document Type"),
|
||||
fieldtype: "Select",
|
||||
options: "Sales Order\nDelivery Note\nSales Invoice",
|
||||
default: "Sales Order"
|
||||
},
|
||||
{
|
||||
fieldname: "period",
|
||||
label: __("Period"),
|
||||
fieldtype: "Select",
|
||||
options: [
|
||||
{ "value": "Monthly", "label": __("Monthly") },
|
||||
{ "value": "Quarterly", "label": __("Quarterly") },
|
||||
{ "value": "Half-Yearly", "label": __("Half-Yearly") },
|
||||
{ "value": "Yearly", "label": __("Yearly") }
|
||||
],
|
||||
default: "Monthly"
|
||||
},
|
||||
{
|
||||
fieldname: "target_on",
|
||||
label: __("Target On"),
|
||||
fieldtype: "Select",
|
||||
options: "Quantity\nAmount",
|
||||
default: "Quantity"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-03-15 17:42:00.631020",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2019-03-15 17:42:00.631020",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Partner Target Variance Item Group-Wise",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Sales Order",
|
||||
"report_name": "Sales Partner Target Variance Item Group-Wise",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"role": "Sales Manager"
|
||||
},
|
||||
{
|
||||
"role": "Maintenance User"
|
||||
},
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from erpnext.selling.report.sales_partner_target_variance_item_group_wise.item_group_wise_sales_target_variance import get_data_column
|
||||
|
||||
def execute(filters=None):
|
||||
data = []
|
||||
|
||||
return get_data_column(filters, "Sales Partner")
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
|
||||
frappe.query_reports["Sales Person Target Variance Item Group-Wise"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname:"company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
fieldname: "fiscal_year",
|
||||
label: __("Fiscal Year"),
|
||||
@@ -10,6 +17,13 @@ frappe.query_reports["Sales Person Target Variance Item Group-Wise"] = {
|
||||
options: "Fiscal Year",
|
||||
default: frappe.sys_defaults.fiscal_year
|
||||
},
|
||||
{
|
||||
fieldname: "doctype",
|
||||
label: __("Document Type"),
|
||||
fieldtype: "Select",
|
||||
options: "Sales Order\nDelivery Note\nSales Invoice",
|
||||
default: "Sales Order"
|
||||
},
|
||||
{
|
||||
fieldname: "period",
|
||||
label: __("Period"),
|
||||
|
||||
@@ -3,176 +3,10 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
||||
from erpnext.selling.report.sales_partner_target_variance_item_group_wise.item_group_wise_sales_target_variance import get_data_column
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
|
||||
columns = get_columns(filters)
|
||||
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
|
||||
sim_map = get_salesperson_item_month_map(filters)
|
||||
|
||||
data = []
|
||||
for salesperson, salesperson_items in sim_map.items():
|
||||
for item_group, monthwise_data in salesperson_items.items():
|
||||
row = [salesperson, item_group]
|
||||
totals = [0, 0, 0]
|
||||
for relevant_months in period_month_ranges:
|
||||
period_data = [0, 0, 0]
|
||||
for month in relevant_months:
|
||||
month_data = monthwise_data.get(month, {})
|
||||
for i, fieldname in enumerate(["target", "achieved", "variance"]):
|
||||
value = flt(month_data.get(fieldname))
|
||||
period_data[i] += value
|
||||
totals[i] += value
|
||||
period_data[2] = period_data[0] - period_data[1]
|
||||
row += period_data
|
||||
totals[2] = totals[0] - totals[1]
|
||||
row += totals
|
||||
data.append(row)
|
||||
|
||||
return columns, sorted(data, key=lambda x: (x[0], x[1]))
|
||||
return get_data_column(filters, "Sales Person")
|
||||
|
||||
def get_columns(filters):
|
||||
for fieldname in ["fiscal_year", "period", "target_on"]:
|
||||
if not filters.get(fieldname):
|
||||
label = (" ".join(fieldname.split("_"))).title()
|
||||
msgprint(_("Please specify") + ": " + label,
|
||||
raise_exception=True)
|
||||
|
||||
columns = [_("Sales Person") + ":Link/Sales Person:120", _("Item Group") + ":Link/Item Group:120"]
|
||||
|
||||
group_months = False if filters["period"] == "Monthly" else True
|
||||
|
||||
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
|
||||
for label in [_("Target") + " (%s)", _("Achieved") + " (%s)", _("Variance") + " (%s)"]:
|
||||
if group_months:
|
||||
label = label % (_(from_date.strftime("%b")) + " - " + _(to_date.strftime("%b")))
|
||||
else:
|
||||
label = label % _(from_date.strftime("%b"))
|
||||
|
||||
columns.append(label+":Float:120")
|
||||
|
||||
return columns + [_("Total Target") + ":Float:120", _("Total Achieved") + ":Float:120",
|
||||
_("Total Variance") + ":Float:120"]
|
||||
|
||||
#Get sales person & item group details
|
||||
def get_salesperson_details(filters):
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
sp.name, td.item_group, td.target_qty, td.target_amount, sp.distribution_id
|
||||
from
|
||||
`tabSales Person` sp, `tabTarget Detail` td
|
||||
where
|
||||
td.parent=sp.name and td.fiscal_year=%s order by sp.name
|
||||
""", (filters["fiscal_year"]), as_dict=1)
|
||||
|
||||
#Get target distribution details of item group
|
||||
def get_target_distribution_details(filters):
|
||||
target_details = {}
|
||||
|
||||
for d in frappe.db.sql("""
|
||||
select
|
||||
md.name, mdp.month, mdp.percentage_allocation
|
||||
from
|
||||
`tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
||||
where
|
||||
mdp.parent=md.name and md.fiscal_year=%s
|
||||
""", (filters["fiscal_year"]), as_dict=1):
|
||||
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
|
||||
|
||||
return target_details
|
||||
|
||||
#Get achieved details from sales order
|
||||
def get_achieved_details(filters, sales_person, all_sales_persons, target_item_group, item_groups):
|
||||
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:]
|
||||
|
||||
item_details = frappe.db.sql("""
|
||||
SELECT st.sales_person, MONTHNAME(so.transaction_date) as month_name,
|
||||
CASE
|
||||
WHEN so.status = "Closed" THEN sum(soi.delivered_qty * soi.conversion_factor * (st.allocated_percentage/100))
|
||||
ELSE sum(soi.stock_qty * (st.allocated_percentage/100))
|
||||
END as qty,
|
||||
CASE
|
||||
WHEN so.status = "Closed" THEN sum(soi.delivered_qty * soi.conversion_factor * soi.base_net_rate * (st.allocated_percentage/100))
|
||||
ELSE sum(soi.base_net_amount * (st.allocated_percentage/100))
|
||||
END as amount
|
||||
from
|
||||
`tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st
|
||||
where
|
||||
soi.parent=so.name and so.docstatus=1 and st.parent=so.name
|
||||
and so.transaction_date>=%s and so.transaction_date<=%s
|
||||
and exists(SELECT name from `tabSales Person` where lft >= %s and rgt <= %s and name=st.sales_person)
|
||||
and exists(SELECT name from `tabItem Group` where lft >= %s and rgt <= %s and name=soi.item_group)
|
||||
group by
|
||||
sales_person, month_name
|
||||
""",
|
||||
(start_date, end_date, all_sales_persons[sales_person].lft, all_sales_persons[sales_person].rgt,
|
||||
item_groups[target_item_group].lft, item_groups[target_item_group].rgt), as_dict=1)
|
||||
|
||||
actual_details = {}
|
||||
for d in item_details:
|
||||
actual_details.setdefault(d.month_name, frappe._dict({
|
||||
"quantity" : 0,
|
||||
"amount" : 0
|
||||
}))
|
||||
|
||||
value_dict = actual_details[d.month_name]
|
||||
value_dict.quantity += flt(d.qty)
|
||||
value_dict.amount += flt(d.amount)
|
||||
|
||||
return actual_details
|
||||
|
||||
def get_salesperson_item_month_map(filters):
|
||||
import datetime
|
||||
salesperson_details = get_salesperson_details(filters)
|
||||
tdd = get_target_distribution_details(filters)
|
||||
item_groups = get_item_groups()
|
||||
sales_persons = get_sales_persons()
|
||||
|
||||
sales_person_achievement_dict = {}
|
||||
for sd in salesperson_details:
|
||||
achieved_details = get_achieved_details(filters, sd.name, sales_persons, sd.item_group, item_groups)
|
||||
|
||||
for month_id in range(1, 13):
|
||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
||||
sales_person_achievement_dict.setdefault(sd.name, {}).setdefault(sd.item_group, {})\
|
||||
.setdefault(month, frappe._dict({
|
||||
"target": 0.0, "achieved": 0.0
|
||||
}))
|
||||
|
||||
sales_target_achieved = sales_person_achievement_dict[sd.name][sd.item_group][month]
|
||||
month_percentage = tdd.get(sd.distribution_id, {}).get(month, 0) \
|
||||
if sd.distribution_id else 100.0/12
|
||||
|
||||
if (filters["target_on"] == "Quantity"):
|
||||
sales_target_achieved.target = flt(sd.target_qty) * month_percentage / 100
|
||||
else:
|
||||
sales_target_achieved.target = flt(sd.target_amount) * month_percentage / 100
|
||||
|
||||
sales_target_achieved.achieved = achieved_details.get(month, frappe._dict())\
|
||||
.get(filters["target_on"].lower())
|
||||
|
||||
return sales_person_achievement_dict
|
||||
|
||||
def get_item_groups():
|
||||
item_groups = frappe._dict()
|
||||
for d in frappe.get_all("Item Group", fields=["name", "lft", "rgt"]):
|
||||
item_groups.setdefault(d.name, frappe._dict({
|
||||
"lft": d.lft,
|
||||
"rgt": d.rgt
|
||||
}))
|
||||
return item_groups
|
||||
|
||||
def get_sales_persons():
|
||||
sales_persons = frappe._dict()
|
||||
for d in frappe.get_all("Sales Person", fields=["name", "lft", "rgt"]):
|
||||
sales_persons.setdefault(d.name, frappe._dict({
|
||||
"lft": d.lft,
|
||||
"rgt": d.rgt
|
||||
}))
|
||||
return sales_persons
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
|
||||
frappe.query_reports["Territory Target Variance Item Group-Wise"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname:"company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
fieldname: "fiscal_year",
|
||||
label: __("Fiscal Year"),
|
||||
@@ -10,6 +17,13 @@ frappe.query_reports["Territory Target Variance Item Group-Wise"] = {
|
||||
options: "Fiscal Year",
|
||||
default: frappe.sys_defaults.fiscal_year
|
||||
},
|
||||
{
|
||||
fieldname: "doctype",
|
||||
label: __("Document Type"),
|
||||
fieldtype: "Select",
|
||||
options: "Sales Order\nDelivery Note\nSales Invoice",
|
||||
default: "Sales Order"
|
||||
},
|
||||
{
|
||||
fieldname: "period",
|
||||
label: __("Period"),
|
||||
|
||||
@@ -3,155 +3,10 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
|
||||
from erpnext.selling.report.sales_partner_target_variance_item_group_wise.item_group_wise_sales_target_variance import get_data_column
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
|
||||
columns = get_columns(filters)
|
||||
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
|
||||
territory_item_group_dict = get_territory_item_month_map(filters)
|
||||
|
||||
data = []
|
||||
for territory, territory_items in territory_item_group_dict.items():
|
||||
for item_group, monthwise_data in territory_items.items():
|
||||
row = [territory, item_group]
|
||||
totals = [0, 0, 0]
|
||||
for relevant_months in period_month_ranges:
|
||||
period_data = [0, 0, 0]
|
||||
for month in relevant_months:
|
||||
month_data = monthwise_data.get(month, {})
|
||||
for i, fieldname in enumerate(["target", "achieved", "variance"]):
|
||||
value = flt(month_data.get(fieldname))
|
||||
period_data[i] += value
|
||||
totals[i] += value
|
||||
period_data[2] = period_data[0] - period_data[1]
|
||||
row += period_data
|
||||
totals[2] = totals[0] - totals[1]
|
||||
row += totals
|
||||
data.append(row)
|
||||
|
||||
return columns, sorted(data, key=lambda x: (x[0], x[1]))
|
||||
return get_data_column(filters, "Territory")
|
||||
|
||||
def get_columns(filters):
|
||||
for fieldname in ["fiscal_year", "period", "target_on"]:
|
||||
if not filters.get(fieldname):
|
||||
label = (" ".join(fieldname.split("_"))).title()
|
||||
msgprint(_("Please specify") + ": " + label, raise_exception=True)
|
||||
|
||||
columns = [_("Territory") + ":Link/Territory:120", _("Item Group") + ":Link/Item Group:120"]
|
||||
|
||||
group_months = False if filters["period"] == "Monthly" else True
|
||||
|
||||
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
|
||||
for label in [_("Target") +" (%s)", _("Achieved") + " (%s)", _("Variance") + " (%s)"]:
|
||||
if group_months:
|
||||
label = label % (_(from_date.strftime("%b")) + " - " + _(to_date.strftime("%b")))
|
||||
else:
|
||||
label = label % _(from_date.strftime("%b"))
|
||||
columns.append(label+":Float:120")
|
||||
|
||||
return columns + [_("Total Target") + ":Float:120", _("Total Achieved") + ":Float:120",
|
||||
_("Total Variance") + ":Float:120"]
|
||||
|
||||
#Get territory & item group details
|
||||
def get_territory_details(filters):
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
t.name, td.item_group, td.target_qty, td.target_amount, t.distribution_id
|
||||
from
|
||||
`tabTerritory` t, `tabTarget Detail` td
|
||||
where
|
||||
td.parent=t.name and td.fiscal_year=%s order by t.name
|
||||
""", (filters["fiscal_year"]), as_dict=1)
|
||||
|
||||
#Get target distribution details of item group
|
||||
def get_target_distribution_details(filters):
|
||||
target_details = {}
|
||||
|
||||
for d in frappe.db.sql("""
|
||||
select
|
||||
md.name, mdp.month, mdp.percentage_allocation
|
||||
from
|
||||
`tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
||||
where
|
||||
mdp.parent=md.name and md.fiscal_year=%s
|
||||
""", (filters["fiscal_year"]), as_dict=1):
|
||||
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
|
||||
|
||||
return target_details
|
||||
|
||||
#Get achieved details from sales order
|
||||
def get_achieved_details(filters, territory, item_groups):
|
||||
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:]
|
||||
|
||||
lft, rgt = frappe.db.get_value("Territory", territory, ["lft", "rgt"])
|
||||
|
||||
item_details = frappe.db.sql("""
|
||||
select
|
||||
soi.item_code, sum(soi.stock_qty) as qty, sum(soi.base_net_amount) as amount,
|
||||
MONTHNAME(so.transaction_date) as month_name
|
||||
from
|
||||
`tabSales Order Item` soi, `tabSales Order` so
|
||||
where
|
||||
soi.parent=so.name and so.docstatus=1
|
||||
and so.transaction_date>=%s and so.transaction_date<=%s
|
||||
and exists(select name from `tabTerritory` where lft >=%s and rgt <= %s and name=so.territory)
|
||||
group by
|
||||
month_name, item_code
|
||||
""", (start_date, end_date, lft, rgt), as_dict=1)
|
||||
|
||||
item_actual_details = {}
|
||||
for d in item_details:
|
||||
item_group = item_groups[d.item_code]
|
||||
item_actual_details.setdefault(item_group, frappe._dict())\
|
||||
.setdefault(d.month_name, frappe._dict({
|
||||
"quantity": 0,
|
||||
"amount": 0
|
||||
}))
|
||||
|
||||
value_dict = item_actual_details[item_group][d.month_name]
|
||||
value_dict.quantity += flt(d.qty)
|
||||
value_dict.amount += flt(d.amount)
|
||||
|
||||
return item_actual_details
|
||||
|
||||
def get_territory_item_month_map(filters):
|
||||
import datetime
|
||||
territory_details = get_territory_details(filters)
|
||||
tdd = get_target_distribution_details(filters)
|
||||
item_groups = get_item_groups()
|
||||
|
||||
territory_item_group_dict = {}
|
||||
|
||||
for td in territory_details:
|
||||
achieved_details = get_achieved_details(filters, td.name, item_groups)
|
||||
|
||||
for month_id in range(1, 13):
|
||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
||||
|
||||
territory_item_group_dict.setdefault(td.name, {}).setdefault(td.item_group, {})\
|
||||
.setdefault(month, frappe._dict({
|
||||
"target": 0.0, "achieved": 0.0
|
||||
}))
|
||||
|
||||
target_achieved = territory_item_group_dict[td.name][td.item_group][month]
|
||||
month_percentage = tdd.get(td.distribution_id, {}).get(month, 0) \
|
||||
if td.distribution_id else 100.0/12
|
||||
|
||||
|
||||
if (filters["target_on"] == "Quantity"):
|
||||
target_achieved.target = flt(td.target_qty) * month_percentage / 100
|
||||
else:
|
||||
target_achieved.target = flt(td.target_amount) * month_percentage / 100
|
||||
|
||||
target_achieved.achieved = achieved_details.get(td.item_group, {}).get(month, {})\
|
||||
.get(filters["target_on"].lower())
|
||||
|
||||
return territory_item_group_dict
|
||||
|
||||
def get_item_groups():
|
||||
return dict(frappe.get_all("Item", fields=["name", "item_group"], as_list=1))
|
||||
|
||||
Reference in New Issue
Block a user