Enhancement in Budget Variance and other reports (#15455)

* Enhanced Budget Variance report and added filters and columns in other reports

* Update budget_variance_report.js

* Changes in budget variance report

* Spacing in column names
This commit is contained in:
deepeshgarg007 2018-10-02 12:52:13 +05:30 committed by Rushabh Mehta
parent 7d115183e4
commit 719701f2a4
9 changed files with 153 additions and 49 deletions

View File

@ -4,8 +4,16 @@
frappe.query_reports["Budget Variance Report"] = { frappe.query_reports["Budget Variance Report"] = {
"filters": [ "filters": [
{ {
fieldname: "fiscal_year", fieldname: "from_fiscal_year",
label: __("Fiscal Year"), label: __("From Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
reqd: 1
},
{
fieldname: "to_fiscal_year",
label: __("To Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: frappe.sys_defaults.fiscal_year,
@ -21,7 +29,7 @@ frappe.query_reports["Budget Variance Report"] = {
{ "value": "Half-Yearly", "label": __("Half-Yearly") }, { "value": "Half-Yearly", "label": __("Half-Yearly") },
{ "value": "Yearly", "label": __("Yearly") } { "value": "Yearly", "label": __("Yearly") }
], ],
default: "Monthly", default: "Yearly",
reqd: 1 reqd: 1
}, },
{ {
@ -46,5 +54,11 @@ frappe.query_reports["Budget Variance Report"] = {
fieldtype: "Link", fieldtype: "Link",
options: "Cost Center" options: "Cost Center"
}, },
{
fieldname:"show_cumulative",
label: __("Show Cumulative Amount"),
fieldtype: "Check",
default: 0,
},
] ]
} }

View File

@ -9,7 +9,7 @@ from frappe.utils import formatdate
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
from six import iteritems from six import iteritems
from pprint import pprint
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} if not filters: filters = {}
validate_filters(filters) validate_filters(filters)
@ -19,7 +19,7 @@ def execute(filters=None):
else: else:
cost_centers = get_cost_centers(filters) cost_centers = get_cost_centers(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
cam_map = get_cost_center_account_month_map(filters) cam_map = get_cost_center_account_month_map(filters)
data = [] data = []
@ -29,17 +29,27 @@ def execute(filters=None):
for account, monthwise_data in iteritems(cost_center_items): for account, monthwise_data in iteritems(cost_center_items):
row = [cost_center, account] row = [cost_center, account]
totals = [0, 0, 0] totals = [0, 0, 0]
for year in get_fiscal_years(filters):
last_total = 0
for relevant_months in period_month_ranges: for relevant_months in period_month_ranges:
period_data = [0, 0, 0] period_data = [0, 0, 0]
for month in relevant_months: for month in relevant_months:
month_data = monthwise_data.get(month, {}) if monthwise_data.get(year[0]):
month_data = monthwise_data.get(year[0]).get(month, {})
for i, fieldname in enumerate(["target", "actual", "variance"]): for i, fieldname in enumerate(["target", "actual", "variance"]):
value = flt(month_data.get(fieldname)) value = flt(month_data.get(fieldname))
period_data[i] += value period_data[i] += value
totals[i] += value totals[i] += value
period_data[0] += last_total
if(filters.get("show_cumulative")):
last_total = period_data[0] - period_data[1]
period_data[2] = period_data[0] - period_data[1] period_data[2] = period_data[0] - period_data[1]
row += period_data row += period_data
totals[2] = totals[0] - totals[1] totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly" :
row += totals row += totals
data.append(row) data.append(row)
@ -50,21 +60,32 @@ def validate_filters(filters):
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center")) frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
def get_columns(filters): def get_columns(filters):
columns = [_(filters.get("budget_against")) + ":Link/%s:120"%(filters.get("budget_against")), _("Account") + ":Link/Account:120"] columns = [_(filters.get("budget_against")) + ":Link/%s:80"%(filters.get("budget_against")), _("Account") + ":Link/Account:80"]
group_months = False if filters["period"] == "Monthly" else True group_months = False if filters["period"] == "Monthly" else True
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]): fiscal_year = get_fiscal_years(filters)
for label in [_("Target") + " (%s)", _("Actual") + " (%s)", _("Variance") + " (%s)"]:
for year in fiscal_year:
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
if filters["period"] == "Yearly":
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Varaiance ") + " " + str(year[0])]
for label in labels:
columns.append(label+":Float:80")
else:
for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]:
if group_months: if group_months:
label = label % (formatdate(from_date, format_string="MMM") + " - " + formatdate(to_date, format_string="MMM")) label = label % (formatdate(from_date, format_string="MMM") + "-" + formatdate(to_date, format_string="MMM"))
else: else:
label = label % formatdate(from_date, format_string="MMM") label = label % formatdate(from_date, format_string="MMM")
columns.append(label+":Float:120") columns.append(label+":Float:80")
return columns + [_("Total Target") + ":Float:120", _("Total Actual") + ":Float:120", if filters["period"] != "Yearly" :
_("Total Variance") + ":Float:120"] return columns + [_("Total Budget") + ":Float:80", _("Total Actual") + ":Float:80",
_("Total Variance") + ":Float:80"]
else:
return columns
def get_cost_centers(filters): def get_cost_centers(filters):
cond = "and 1=1" cond = "and 1=1"
@ -81,19 +102,21 @@ def get_cost_center_target_details(filters):
cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center")) cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center"))
return frappe.db.sql(""" return frappe.db.sql("""
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
from `tabBudget` b, `tabBudget Account` ba from `tabBudget` b, `tabBudget Account` ba
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year=%s where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
and b.budget_against = %s and b.company=%s {cond} and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
(filters.fiscal_year, filters.budget_against, filters.company), as_dict=True) (filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
#Get target distribution details of accounts of cost center #Get target distribution details of accounts of cost center
def get_target_distribution_details(filters): def get_target_distribution_details(filters):
target_details = {} target_details = {}
for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation for d in frappe.db.sql("""select md.name, mdp.month, mdp.percentage_allocation
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where mdp.parent=md.name and md.fiscal_year=%s""", (filters["fiscal_year"]), as_dict=1): where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation)) target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
return target_details return target_details
@ -107,7 +130,7 @@ def get_actual_details(name, filters):
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"]) cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt) cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit, ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
where where
@ -115,11 +138,11 @@ def get_actual_details(name, filters):
and b.docstatus = 1 and b.docstatus = 1
and ba.account=gl.account and ba.account=gl.account
and b.{budget_against} = gl.{budget_against} and b.{budget_against} = gl.{budget_against}
and gl.fiscal_year=%s and gl.fiscal_year between %s and %s
and b.{budget_against}=%s and b.{budget_against}=%s
and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name and exists(select name from `tab{tab}` where name=gl.{budget_against} and {cond}) group by gl.name order by gl.fiscal_year
""".format(tab = filters.budget_against, budget_against = budget_against, cond = cond), """.format(tab = filters.budget_against, budget_against = budget_against, cond = cond,from_year=filters.from_fiscal_year,to_year=filters.to_fiscal_year),
(filters.fiscal_year, name), as_dict=1) (filters.from_fiscal_year, filters.to_fiscal_year, name), as_dict=1)
cc_actual_details = {} cc_actual_details = {}
for d in ac_details: for d in ac_details:
@ -139,13 +162,12 @@ def get_cost_center_account_month_map(filters):
for month_id in range(1, 13): for month_id in range(1, 13):
month = datetime.date(2013, month_id, 1).strftime('%B') month = datetime.date(2013, month_id, 1).strftime('%B')
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {})\
.setdefault(month, frappe._dict({ .setdefault(month, frappe._dict({
"target": 0.0, "actual": 0.0 "target": 0.0, "actual": 0.0
})) }))
tav_dict = cam_map[ccd.budget_against][ccd.account][month] tav_dict = cam_map[ccd.budget_against][ccd.account][ccd.fiscal_year][month]
month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \ month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \
if ccd.monthly_distribution else 100.0/12 if ccd.monthly_distribution else 100.0/12
@ -156,3 +178,11 @@ def get_cost_center_account_month_map(filters):
tav_dict.actual += flt(ad.debit) - flt(ad.credit) tav_dict.actual += flt(ad.debit) - flt(ad.credit)
return cam_map return cam_map
def get_fiscal_years(filters):
fiscal_year = frappe.db.sql("""select name from `tabFiscal Year` where
name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
{'from_fiscal_year': filters["from_fiscal_year"], 'to_fiscal_year': filters["to_fiscal_year"]})
return fiscal_year

View File

@ -28,6 +28,24 @@ frappe.query_reports["Item-wise Sales Register"] = {
"label": __("Mode of Payment"), "label": __("Mode of Payment"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Mode of Payment" "options": "Mode of Payment"
},
{
"fieldname":"warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse"
},
{
"fieldname":"brand",
"label": __("Brand"),
"fieldtype": "Link",
"options": "Brand"
},
{
"fieldname":"item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
} }
] ]
} }

View File

@ -109,6 +109,23 @@ def get_conditions(filters):
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)""" and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
if filters.get("warehouse"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("brand"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
return conditions return conditions
def get_items(filters, additional_query_columns): def get_items(filters, additional_query_columns):

View File

@ -52,6 +52,18 @@ frappe.query_reports["Sales Register"] = {
"label": __("Warehouse"), "label": __("Warehouse"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Warehouse" "options": "Warehouse"
},
{
"fieldname":"brand",
"label": __("Brand"),
"fieldtype": "Link",
"options": "Brand"
},
{
"fieldname":"item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
} }
] ]
} }

View File

@ -153,6 +153,16 @@ def get_conditions(filters):
where parent=`tabSales Invoice`.name where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)""" and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("brand"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
return conditions return conditions
def get_invoices(filters, additional_query_columns): def get_invoices(filters, additional_query_columns):

View File

@ -29,9 +29,9 @@ def execute(filters=None):
if customer_naming_type == "Naming Series": if customer_naming_type == "Naming Series":
row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal, row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal,
d.bypass_credit_limit_check_at_sales_order] d.bypass_credit_limit_check_at_sales_order, d.disabled]
else: else:
row = [d.name, credit_limit, outstanding_amt, bal, d.bypass_credit_limit_check_at_sales_order] row = [d.name, credit_limit, outstanding_amt, bal, d.bypass_credit_limit_check_at_sales_order, d.disabled]
if credit_limit: if credit_limit:
data.append(row) data.append(row)
@ -40,11 +40,13 @@ def execute(filters=None):
def get_columns(customer_naming_type): def get_columns(customer_naming_type):
columns = [ columns = [
_("Name") + ":Link/Customer:120",
_("Customer") + ":Link/Customer:120", _("Customer") + ":Link/Customer:120",
_("Credit Limit") + ":Currency:120", _("Credit Limit") + ":Currency:120",
_("Outstanding Amt") + ":Currency:100", _("Outstanding Amt") + ":Currency:100",
_("Credit Balance") + ":Currency:120", _("Credit Balance") + ":Currency:120",
_("Bypass credit check at Sales Order ") + ":Check:240" _("Bypass credit check at Sales Order ") + ":Check:240",
_("Is Disabled ") + ":Check:240"
] ]
if customer_naming_type == "Naming Series": if customer_naming_type == "Naming Series":
@ -59,5 +61,5 @@ def get_details(filters):
conditions += " where name = %(customer)s" conditions += " where name = %(customer)s"
return frappe.db.sql("""select name, customer_name, return frappe.db.sql("""select name, customer_name,
bypass_credit_limit_check_at_sales_order from `tabCustomer` %s bypass_credit_limit_check_at_sales_order,disabled from `tabCustomer` %s
""" % conditions, filters, as_dict=1) """ % conditions, filters, as_dict=1)

View File

@ -17,7 +17,7 @@ def execute(filters=None):
for d in entries: for d in entries:
if d.stock_qty > 0 or filters.get('show_return_entries', 0): if d.stock_qty > 0 or filters.get('show_return_entries', 0):
data.append([ data.append([
d.name, d.customer, d.territory, d.posting_date, d.item_code, d.name, d.customer, d.territory, item_details.get(d.item_code, {}).get("website_warehouse"), d.posting_date, d.item_code,
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"), item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt
]) ])
@ -33,7 +33,8 @@ def get_columns(filters):
msgprint(_("Please select the document type first"), raise_exception=1) msgprint(_("Please select the document type first"), raise_exception=1)
return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140", return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140",
_("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Posting Date") + ":Date:100", _("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Warehouse") + ":Link/Warehouse:100",
_("Posting Date") + ":Date:100",
_("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120", _("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120",
_("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120", _("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120",
_("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110", _("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110",
@ -115,7 +116,7 @@ def get_items(filters):
def get_item_details(): def get_item_details():
item_details = {} item_details = {}
for d in frappe.db.sql("""select name, item_group, brand from `tabItem`""", as_dict=1): for d in frappe.db.sql("""select name, item_group, brand, website_warehouse from `tabItem`""", as_dict=1):
item_details.setdefault(d.name, d) item_details.setdefault(d.name, d)
return item_details return item_details

View File

@ -1,17 +1,17 @@
{ {
"add_total_row": 0, "add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-06-04 11:03:47", "creation": "2013-06-04 11:03:47",
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 3, "idx": 3,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2017-02-24 20:19:13.150769", "modified": "2018-09-20 18:20:49.229153",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Batch-Wise Balance History", "name": "Batch-Wise Balance History",
"owner": "Administrator", "owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Stock Ledger Entry", "ref_doctype": "Stock Ledger Entry",
"report_name": "Batch-Wise Balance History", "report_name": "Batch-Wise Balance History",
"report_type": "Script Report", "report_type": "Script Report",