Redesigned budgets
This commit is contained in:
parent
b9bc7d6df3
commit
ca90963d5b
@ -15,7 +15,8 @@ frappe.ui.form.on('Budget', {
|
|||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
report_type: "Profit and Loss"
|
report_type: "Profit and Loss",
|
||||||
|
is_group: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -112,7 +112,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"default": "Stop",
|
"default": "Stop",
|
||||||
@ -139,7 +139,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"default": "Warn",
|
"default": "Warn",
|
||||||
@ -277,7 +277,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-05-16 13:12:10.439375",
|
"modified": "2016-05-16 15:00:40.233685",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Budget",
|
"name": "Budget",
|
||||||
|
@ -17,54 +17,80 @@ class Budget(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_duplicate()
|
self.validate_duplicate()
|
||||||
|
self.validate_accounts()
|
||||||
|
|
||||||
def validate_duplicate(self):
|
def validate_duplicate(self):
|
||||||
existing_budget = frappe.db.get_value("Budget", {"cost_center": self.cost_center,
|
existing_budget = frappe.db.get_value("Budget", {"cost_center": self.cost_center,
|
||||||
"fiscal_year": self.fiscal_year, "name": ["!=", self.name], "docstatus": ["!=", 2]})
|
"fiscal_year": self.fiscal_year, "company": self.company,
|
||||||
|
"name": ["!=", self.name], "docstatus": ["!=", 2]})
|
||||||
if existing_budget:
|
if existing_budget:
|
||||||
frappe.throw(_("Another Budget record {0} already exists against {1} for fiscal year {2}")
|
frappe.throw(_("Another Budget record {0} already exists against {1} for fiscal year {2}")
|
||||||
.format(existing_budget, self.cost_center, self.fiscal_year))
|
.format(existing_budget, self.cost_center, self.fiscal_year))
|
||||||
|
|
||||||
|
def validate_accounts(self):
|
||||||
|
account_list = []
|
||||||
|
for d in self.get('accounts'):
|
||||||
|
if d.account:
|
||||||
|
account_details = frappe.db.get_value("Account", d.account,
|
||||||
|
["is_group", "company", "report_type"], as_dict=1)
|
||||||
|
|
||||||
|
if account_details.is_group:
|
||||||
|
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
||||||
|
elif account_details.company != self.company:
|
||||||
|
frappe.throw(_("Account {0} does not belongs to company {1}")
|
||||||
|
.format(d.account, self.company))
|
||||||
|
elif account_details.report_type != "Profit and Loss":
|
||||||
|
frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Income or Expense account")
|
||||||
|
.format(d.account))
|
||||||
|
|
||||||
|
if d.account in account_list:
|
||||||
|
frappe.throw(_("Account {0} has been entered multiple times").format(d.account))
|
||||||
|
else:
|
||||||
|
account_list.append(d.account)
|
||||||
|
|
||||||
def validate_expense_against_budget(args):
|
def validate_expense_against_budget(args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
if frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"}):
|
if frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"}):
|
||||||
budget = frappe.db.sql("""
|
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
|
||||||
select ba.budget_amount, b.monthly_distribution,
|
|
||||||
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded
|
budget_records = frappe.db.sql("""
|
||||||
from `tabBudget` b, `tabBudget Account` ba
|
select ba.budget_amount, b.monthly_distribution, b.cost_center,
|
||||||
where b.name=ba.parent and b.cost_center=%s and b.fiscal_year=%s and ba.account=%s
|
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded
|
||||||
""", (args.cost_center, args.fiscal_year, args.account), as_dict=True)
|
from `tabBudget` b, `tabBudget Account` ba
|
||||||
|
where
|
||||||
|
b.name=ba.parent and b.fiscal_year=%s and ba.account=%s
|
||||||
|
and exists(select name from `tabCost Center` where lft<=%s and rgt>=%s and name=b.cost_center)
|
||||||
|
""", (args.fiscal_year, args.account, cc_lft, cc_rgt), as_dict=True)
|
||||||
|
|
||||||
|
for budget in budget_records:
|
||||||
|
if budget.budget_amount:
|
||||||
|
yearly_action = budget.action_if_annual_budget_exceeded
|
||||||
|
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
|
||||||
|
|
||||||
if budget and budget[0].budget_amount:
|
|
||||||
yearly_action = budget[0].action_if_annual_budget_exceeded
|
|
||||||
monthly_action = budget[0].action_if_accumulated_monthly_budget_exceeded
|
|
||||||
|
|
||||||
action_for = action = ""
|
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
if monthly_action in ["Stop", "Warn"]:
|
||||||
budget_amount = get_accumulated_monthly_budget(budget[0].monthly_distribution,
|
budget_amount = get_accumulated_monthly_budget(budget.monthly_distribution,
|
||||||
args.posting_date, args.fiscal_year, budget[0].budget_amount)
|
args.posting_date, args.fiscal_year, budget.budget_amount)
|
||||||
|
|
||||||
args["month_end_date"] = get_last_day(args.posting_date)
|
args["month_end_date"] = get_last_day(args.posting_date)
|
||||||
|
|
||||||
action_for, action = _("Accumulated Monthly"), monthly_action
|
compare_expense_with_budget(args, budget.cost_center,
|
||||||
|
budget_amount, _("Accumulated Monthly"), monthly_action)
|
||||||
|
|
||||||
elif yearly_action in ["Stop", "Warn"]:
|
elif yearly_action in ["Stop", "Warn"]:
|
||||||
budget_amount = flt(budget[0].budget_amount)
|
compare_expense_with_budget(args, budget.cost_center,
|
||||||
action_for, action = _("Annual"), yearly_action
|
flt(budget.budget_amount), _("Annual"), yearly_action)
|
||||||
|
|
||||||
if action_for:
|
def compare_expense_with_budget(args, cost_center, budget_amount, action_for, action):
|
||||||
actual_expense = get_actual_expense(args)
|
actual_expense = get_actual_expense(args, cost_center)
|
||||||
if actual_expense > budget_amount:
|
if actual_expense > budget_amount:
|
||||||
diff = actual_expense - budget_amount
|
diff = actual_expense - budget_amount
|
||||||
|
|
||||||
msg = _("{0} Budget for Account {1} against Cost Center {2} is {3}. It will exceed by {4}").format(_(action_for), args.account, args.cost_center, budget_amount, diff)
|
msg = _("{0} Budget for Account {1} against Cost Center {2} is {3}. It will exceed by {4}").format(_(action_for), args.account, cost_center, budget_amount, diff)
|
||||||
|
|
||||||
if action=="Stop":
|
if action=="Stop":
|
||||||
frappe.throw(msg, BudgetError)
|
frappe.throw(msg, BudgetError)
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(msg)
|
frappe.msgprint(msg)
|
||||||
|
|
||||||
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
||||||
distribution = {}
|
distribution = {}
|
||||||
@ -87,13 +113,20 @@ def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_ye
|
|||||||
|
|
||||||
return annual_budget * accumulated_percentage / 100
|
return annual_budget * accumulated_percentage / 100
|
||||||
|
|
||||||
def get_actual_expense(args):
|
def get_actual_expense(args, cost_center):
|
||||||
args["condition"] = " and posting_date <= '%s'" % \
|
lft_rgt = frappe.db.get_value("Cost Center", cost_center, ["lft", "rgt"], as_dict=1)
|
||||||
args.month_end_date if args.get("month_end_date") else ""
|
args.update(lft_rgt)
|
||||||
|
|
||||||
|
condition = " and gle.posting_date <= %(month_end_date)s" if args.get("month_end_date") else ""
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
return flt(frappe.db.sql("""
|
||||||
select sum(debit) - sum(credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry`
|
from `tabGL Entry` gle
|
||||||
where account='%(account)s' and cost_center='%(cost_center)s'
|
where gle.account=%(account)s
|
||||||
and fiscal_year='%(fiscal_year)s' and company='%(company)s' and docstatus=1 %(condition)s
|
and exists(select name from `tabCost Center`
|
||||||
""" % (args))[0][0])
|
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)
|
||||||
|
and gle.fiscal_year=%(fiscal_year)s
|
||||||
|
and gle.company=%(company)s
|
||||||
|
and gle.docstatus=1
|
||||||
|
{condition}
|
||||||
|
""".format(condition=condition), (args))[0][0])
|
@ -5,8 +5,111 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
|
||||||
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('Budget')
|
class TestBudget(unittest.TestCase):
|
||||||
|
def test_monthly_budget_crossed_ignore(self):
|
||||||
|
budget = make_budget()
|
||||||
|
frappe.db.set_value("Budget", budget, "action_if_accumulated_monthly_budget_exceeded", "Ignore")
|
||||||
|
|
||||||
class TestBudget(unittest.TestCase):
|
set_total_expense_zero("2013-02-28")
|
||||||
pass
|
|
||||||
|
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", submit=True)
|
||||||
|
|
||||||
|
self.assertTrue(frappe.db.get_value("GL Entry",
|
||||||
|
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
|
||||||
|
|
||||||
|
def test_monthly_budget_crossed_stop(self):
|
||||||
|
budget = make_budget()
|
||||||
|
frappe.db.set_value("Budget", budget, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
|
|
||||||
|
set_total_expense_zero("2013-02-28")
|
||||||
|
|
||||||
|
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC")
|
||||||
|
|
||||||
|
self.assertRaises(BudgetError, jv.submit)
|
||||||
|
|
||||||
|
def test_yearly_budget_crossed_stop(self):
|
||||||
|
self.test_monthly_budget_crossed_ignore()
|
||||||
|
|
||||||
|
set_total_expense_zero("2013-02-28")
|
||||||
|
|
||||||
|
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC")
|
||||||
|
|
||||||
|
self.assertRaises(BudgetError, jv.submit)
|
||||||
|
|
||||||
|
def test_monthly_budget_on_cancellation(self):
|
||||||
|
budget = make_budget()
|
||||||
|
frappe.db.set_value("Budget", budget, "action_if_accumulated_monthly_budget_exceeded", "Ignore")
|
||||||
|
|
||||||
|
set_total_expense_zero("2013-02-28")
|
||||||
|
|
||||||
|
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
|
||||||
|
|
||||||
|
self.assertTrue(frappe.db.get_value("GL Entry",
|
||||||
|
{"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
|
||||||
|
|
||||||
|
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
|
||||||
|
|
||||||
|
self.assertTrue(frappe.db.get_value("GL Entry",
|
||||||
|
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
|
||||||
|
|
||||||
|
frappe.db.set_value("Budget", budget, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
|
|
||||||
|
self.assertRaises(BudgetError, jv1.cancel)
|
||||||
|
|
||||||
|
def test_monthly_budget_against_group_cost_center(self):
|
||||||
|
budget = make_budget("_Test Company - _TC")
|
||||||
|
frappe.db.set_value("Budget", budget, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
|
|
||||||
|
set_total_expense_zero("2013-02-28")
|
||||||
|
set_total_expense_zero("2013-02-28", "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
|
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
|
self.assertRaises(BudgetError, jv.submit)
|
||||||
|
|
||||||
|
def set_total_expense_zero(posting_date, cost_center=None):
|
||||||
|
existing_expense = get_actual_expense({
|
||||||
|
"account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"cost_center": cost_center or "_Test Cost Center - _TC",
|
||||||
|
"monthly_end_date": posting_date,
|
||||||
|
"company": "_Test Company",
|
||||||
|
"fiscal_year": "_Test Fiscal Year 2013"
|
||||||
|
}, cost_center or "_Test Cost Center - _TC")
|
||||||
|
|
||||||
|
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True)
|
||||||
|
|
||||||
|
def make_budget(cost_center=None):
|
||||||
|
existing_budget = frappe.db.get_value("Budget",
|
||||||
|
{"cost_center": cost_center or "_Test Cost Center - _TC",
|
||||||
|
"fiscal_year": "_Test Fiscal Year 2013", "company": "_Test Company"})
|
||||||
|
|
||||||
|
if not existing_budget:
|
||||||
|
budget = frappe.new_doc("Budget")
|
||||||
|
budget.cost_center = cost_center or "_Test Cost Center - _TC"
|
||||||
|
budget.fiscal_year = "_Test Fiscal Year 2013"
|
||||||
|
budget.monthly_distribution = "_Test Distribution"
|
||||||
|
budget.company = "_Test Company"
|
||||||
|
budget.action_if_annual_budget_exceeded = "Stop"
|
||||||
|
budget.action_if_accumulated_monthly_budget_exceeded = "Stop"
|
||||||
|
|
||||||
|
budget.append("accounts", {
|
||||||
|
"account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"budget_amount": 100000
|
||||||
|
})
|
||||||
|
|
||||||
|
budget.insert()
|
||||||
|
budget.submit()
|
||||||
|
|
||||||
|
return budget.name
|
||||||
|
else:
|
||||||
|
return existing_budget
|
@ -1 +0,0 @@
|
|||||||
Budget amounts for year and distribution for parent Cost Center.
|
|
@ -1 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
@ -1,106 +0,0 @@
|
|||||||
{
|
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "hash",
|
|
||||||
"creation": "2013-03-07 11:55:04",
|
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Account",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "account",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Account",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 1,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "budget_allocated",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Budget Allocated",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "budget_allocated",
|
|
||||||
"oldfieldtype": "Currency",
|
|
||||||
"options": "Company:company:default_currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "fiscal_year",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Fiscal Year",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "fiscal_year",
|
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "Fiscal Year",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 1,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
|
||||||
"in_create": 0,
|
|
||||||
"in_dialog": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2015-11-16 06:29:43.050558",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Budget Detail",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC"
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
# 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.model.document import Document
|
|
||||||
|
|
||||||
class BudgetDetail(Document):
|
|
||||||
pass
|
|
@ -5,36 +5,19 @@ frappe.provide("erpnext.accounts");
|
|||||||
|
|
||||||
cur_frm.list_route = "Accounts Browser/Cost Center";
|
cur_frm.list_route = "Accounts Browser/Cost Center";
|
||||||
|
|
||||||
erpnext.accounts.CostCenterController = frappe.ui.form.Controller.extend({
|
|
||||||
onload: function() {
|
|
||||||
this.setup_queries();
|
|
||||||
},
|
|
||||||
|
|
||||||
setup_queries: function() {
|
frappe.ui.form.on('Cost Center', {
|
||||||
var me = this;
|
onload: function(frm) {
|
||||||
if(this.frm.fields_dict["budgets"].grid.get_field("account")) {
|
frm.set_query("parent_cost_center", function() {
|
||||||
this.frm.set_query("account", "budgets", function() {
|
|
||||||
return {
|
|
||||||
filters:[
|
|
||||||
['Account', 'company', '=', me.frm.doc.company],
|
|
||||||
['Account', 'is_group', '=', '0']
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.frm.set_query("parent_cost_center", function() {
|
|
||||||
return {
|
return {
|
||||||
filters:[
|
filters: {
|
||||||
['Cost Center', 'is_group', '=', '1'],
|
company: frm.doc.company,
|
||||||
['Cost Center', 'company', '=', me.frm.doc.company],
|
is_group: 1
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, new erpnext.accounts.CostCenterController({frm: cur_frm}));
|
|
||||||
|
|
||||||
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
||||||
var intro_txt = '';
|
var intro_txt = '';
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:cost_center_name",
|
"autoname": "field:cost_center_name",
|
||||||
|
"beta": 0,
|
||||||
"creation": "2013-01-23 19:57:17",
|
"creation": "2013-01-23 19:57:17",
|
||||||
"custom": 0,
|
"custom": 0,
|
||||||
"description": "Track separate Income and Expense for product verticals or divisions.",
|
"description": "Track separate Income and Expense for product verticals or divisions.",
|
||||||
@ -164,87 +165,6 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"description": "Define Budget for this Cost Center. To set budget action, see \"Company List\"",
|
|
||||||
"fieldname": "sb1",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Budget",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"description": "Select Monthly Distribution, if you want to track based on seasonality.",
|
|
||||||
"fieldname": "distribution_id",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Distribution Id",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "distribution_id",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Monthly Distribution",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"description": "Add rows to set annual budgets on Accounts.",
|
|
||||||
"fieldname": "budgets",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Budgets",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "budget_details",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Budget Detail",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -336,7 +256,7 @@
|
|||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": 0,
|
"menu_index": 0,
|
||||||
"modified": "2016-03-14 15:59:51.508268",
|
"modified": "2016-05-16 15:23:14.770933",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Cost Center",
|
"name": "Cost Center",
|
||||||
@ -443,8 +363,11 @@
|
|||||||
"write": 0
|
"write": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
"search_fields": "parent_cost_center, is_group",
|
"search_fields": "parent_cost_center, is_group",
|
||||||
|
"sort_order": "ASC",
|
||||||
|
"track_seen": 0,
|
||||||
"version": 0
|
"version": 0
|
||||||
}
|
}
|
@ -16,36 +16,12 @@ class CostCenter(NestedSet):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
self.validate_accounts()
|
|
||||||
|
|
||||||
def validate_mandatory(self):
|
def validate_mandatory(self):
|
||||||
if self.cost_center_name != self.company and not self.parent_cost_center:
|
if self.cost_center_name != self.company and not self.parent_cost_center:
|
||||||
frappe.throw(_("Please enter parent cost center"))
|
frappe.throw(_("Please enter parent cost center"))
|
||||||
elif self.cost_center_name == self.company and self.parent_cost_center:
|
elif self.cost_center_name == self.company and self.parent_cost_center:
|
||||||
frappe.throw(_("Root cannot have a parent cost center"))
|
frappe.throw(_("Root cannot have a parent cost center"))
|
||||||
|
|
||||||
def validate_accounts(self):
|
|
||||||
if self.is_group==1 and self.get("budgets"):
|
|
||||||
frappe.throw(_("Budget cannot be set for Group Cost Center"))
|
|
||||||
|
|
||||||
check_acc_list = []
|
|
||||||
for d in self.get('budgets'):
|
|
||||||
if d.account:
|
|
||||||
account_details = frappe.db.get_value("Account", d.account,
|
|
||||||
["is_group", "company", "report_type"], as_dict=1)
|
|
||||||
if account_details.is_group:
|
|
||||||
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
|
|
||||||
elif account_details.company != self.company:
|
|
||||||
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
|
|
||||||
elif account_details.report_type != "Profit and Loss":
|
|
||||||
frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Income or Expense account")
|
|
||||||
.format(d.account))
|
|
||||||
|
|
||||||
if [d.account, d.fiscal_year] in check_acc_list:
|
|
||||||
frappe.throw(_("Account {0} has been entered more than once for fiscal year {1}")
|
|
||||||
.format(d.account, d.fiscal_year))
|
|
||||||
else:
|
|
||||||
check_acc_list.append([d.account, d.fiscal_year])
|
|
||||||
|
|
||||||
def convert_group_to_ledger(self):
|
def convert_group_to_ledger(self):
|
||||||
if self.check_if_child_exists():
|
if self.check_if_child_exists():
|
||||||
|
@ -1,17 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"account": "_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"budget_allocated": 100000,
|
|
||||||
"doctype": "Budget Detail",
|
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
|
||||||
"parentfield": "budgets"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"cost_center_name": "_Test Cost Center",
|
"cost_center_name": "_Test Cost Center",
|
||||||
"distribution_id": "_Test Distribution",
|
|
||||||
"doctype": "Cost Center",
|
"doctype": "Cost Center",
|
||||||
"is_group": 0,
|
"is_group": 0,
|
||||||
"parent_cost_center": "_Test Company - _TC"
|
"parent_cost_center": "_Test Company - _TC"
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import unittest, frappe
|
import unittest, frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError, get_fiscal_year
|
|
||||||
from erpnext.exceptions import InvalidAccountCurrency
|
from erpnext.exceptions import InvalidAccountCurrency
|
||||||
|
|
||||||
|
|
||||||
@ -96,78 +95,6 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
|
|
||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
def test_monthly_budget_crossed_ignore(self):
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore")
|
|
||||||
|
|
||||||
self.set_total_expense_zero("2013-02-28")
|
|
||||||
|
|
||||||
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", submit=True)
|
|
||||||
|
|
||||||
self.assertTrue(frappe.db.get_value("GL Entry",
|
|
||||||
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
|
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop(self):
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Stop")
|
|
||||||
|
|
||||||
self.set_total_expense_zero("2013-02-28")
|
|
||||||
|
|
||||||
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC")
|
|
||||||
|
|
||||||
self.assertRaises(BudgetError, jv.submit)
|
|
||||||
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore")
|
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop(self):
|
|
||||||
self.test_monthly_budget_crossed_ignore()
|
|
||||||
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "yearly_bgt_flag", "Stop")
|
|
||||||
|
|
||||||
self.set_total_expense_zero("2013-02-28")
|
|
||||||
|
|
||||||
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"_Test Bank - _TC", 150000, "_Test Cost Center - _TC")
|
|
||||||
|
|
||||||
self.assertRaises(BudgetError, jv.submit)
|
|
||||||
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "yearly_bgt_flag", "Ignore")
|
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation(self):
|
|
||||||
self.set_total_expense_zero("2013-02-28")
|
|
||||||
|
|
||||||
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
|
|
||||||
|
|
||||||
self.assertTrue(frappe.db.get_value("GL Entry",
|
|
||||||
{"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
|
|
||||||
|
|
||||||
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
|
|
||||||
|
|
||||||
self.assertTrue(frappe.db.get_value("GL Entry",
|
|
||||||
{"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
|
|
||||||
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Stop")
|
|
||||||
|
|
||||||
self.assertRaises(BudgetError, jv1.cancel)
|
|
||||||
|
|
||||||
frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore")
|
|
||||||
|
|
||||||
def get_actual_expense(self, monthly_end_date):
|
|
||||||
return get_actual_expense({
|
|
||||||
"account": "_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
|
||||||
"monthly_end_date": monthly_end_date,
|
|
||||||
"company": "_Test Company",
|
|
||||||
"fiscal_year": get_fiscal_year(monthly_end_date)[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
def set_total_expense_zero(self, posting_date):
|
|
||||||
existing_expense = self.get_actual_expense(posting_date)
|
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True)
|
|
||||||
|
|
||||||
def test_multi_currency(self):
|
def test_multi_currency(self):
|
||||||
jv = make_journal_entry("_Test Bank USD - _TC",
|
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||||
"_Test Bank - _TC", 100, exchange_rate=50, save=False)
|
"_Test Bank - _TC", 100, exchange_rate=50, save=False)
|
||||||
|
@ -1,156 +1,159 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
"allow_import": 0,
|
"allow_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"autoname": "field:distribution_id",
|
"autoname": "field:distribution_id",
|
||||||
"creation": "2013-01-10 16:34:05",
|
"beta": 0,
|
||||||
"custom": 0,
|
"creation": "2013-01-10 16:34:05",
|
||||||
"description": "**Monthly Distribution** helps you distribute your budget across months if you have seasonality in your business.\n\nTo distribute a budget using this distribution, set this **Monthly Distribution** in the **Cost Center**",
|
"custom": 0,
|
||||||
"docstatus": 0,
|
"description": "**Monthly Distribution** helps you distribute the Budget/Target across months if you have seasonality in your business.",
|
||||||
"doctype": "DocType",
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"description": "Name of the Monthly Distribution",
|
"description": "Name of the Monthly Distribution",
|
||||||
"fieldname": "distribution_id",
|
"fieldname": "distribution_id",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Distribution Name",
|
"label": "Distribution Name",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "distribution_id",
|
"oldfieldname": "distribution_id",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "fiscal_year",
|
"fieldname": "fiscal_year",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 1,
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Fiscal Year",
|
"label": "Fiscal Year",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "fiscal_year",
|
"oldfieldname": "fiscal_year",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "percentages",
|
"fieldname": "percentages",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Monthly Distribution Percentages",
|
"label": "Monthly Distribution Percentages",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "budget_distribution_details",
|
"oldfieldname": "budget_distribution_details",
|
||||||
"oldfieldtype": "Table",
|
"oldfieldtype": "Table",
|
||||||
"options": "Monthly Distribution Percentage",
|
"options": "Monthly Distribution Percentage",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"icon": "icon-bar-chart",
|
"icon": "icon-bar-chart",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"in_dialog": 0,
|
"in_dialog": 0,
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-03-03 02:46:44.493857",
|
"modified": "2016-05-16 16:35:20.349194",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Monthly Distribution",
|
"name": "Monthly Distribution",
|
||||||
"name_case": "Title Case",
|
"name_case": "Title Case",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
"apply_user_permissions": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
"export": 0,
|
||||||
"if_owner": 0,
|
"if_owner": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
"apply_user_permissions": 0,
|
||||||
"cancel": 0,
|
"cancel": 0,
|
||||||
"create": 0,
|
"create": 0,
|
||||||
"delete": 0,
|
"delete": 0,
|
||||||
"email": 0,
|
"email": 0,
|
||||||
"export": 0,
|
"export": 0,
|
||||||
"if_owner": 0,
|
"if_owner": 0,
|
||||||
"import": 0,
|
"import": 0,
|
||||||
"permlevel": 2,
|
"permlevel": 2,
|
||||||
"print": 0,
|
"print": 0,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
"set_user_permissions": 0,
|
||||||
"share": 0,
|
"share": 0,
|
||||||
"submit": 0,
|
"submit": 0,
|
||||||
"write": 0
|
"write": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"read_only": 0,
|
"quick_entry": 1,
|
||||||
"read_only_onload": 0,
|
"read_only": 0,
|
||||||
"sort_field": "modified",
|
"read_only_onload": 0,
|
||||||
"sort_order": "DESC"
|
"sort_field": "modified",
|
||||||
}
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -8,7 +8,8 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
label: __("Fiscal Year"),
|
label: __("Fiscal Year"),
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Fiscal Year",
|
options: "Fiscal Year",
|
||||||
default: sys_defaults.fiscal_year
|
default: sys_defaults.fiscal_year,
|
||||||
|
reqd: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: "period",
|
fieldname: "period",
|
||||||
@ -20,14 +21,16 @@ 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: "Monthly",
|
||||||
|
reqd: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: "company",
|
fieldname: "company",
|
||||||
label: __("Company"),
|
label: __("Company"),
|
||||||
fieldtype: "Link",
|
fieldtype: "Link",
|
||||||
options: "Company",
|
options: "Company",
|
||||||
default: frappe.defaults.get_user_default("Company")
|
default: frappe.defaults.get_user_default("Company"),
|
||||||
},
|
reqd: 1
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -3,48 +3,43 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, msgprint
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe.utils import formatdate
|
from frappe.utils import formatdate
|
||||||
import time
|
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
|
||||||
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
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if not filters: filters = {}
|
if not filters: filters = {}
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
|
cost_centers = get_cost_centers(filters.company)
|
||||||
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
|
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
|
||||||
cam_map = get_costcenter_account_month_map(filters)
|
cam_map = get_cost_center_account_month_map(filters)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
for cost_center, cost_center_items in cam_map.items():
|
for cost_center in cost_centers:
|
||||||
for account, monthwise_data in cost_center_items.items():
|
cost_center_items = cam_map.get(cost_center)
|
||||||
row = [cost_center, account]
|
if cost_center_items:
|
||||||
totals = [0, 0, 0]
|
for account, monthwise_data in cost_center_items.items():
|
||||||
for relevant_months in period_month_ranges:
|
row = [cost_center, account]
|
||||||
period_data = [0, 0, 0]
|
totals = [0, 0, 0]
|
||||||
for month in relevant_months:
|
for relevant_months in period_month_ranges:
|
||||||
month_data = monthwise_data.get(month, {})
|
period_data = [0, 0, 0]
|
||||||
for i, fieldname in enumerate(["target", "actual", "variance"]):
|
for month in relevant_months:
|
||||||
value = flt(month_data.get(fieldname))
|
month_data = monthwise_data.get(month, {})
|
||||||
period_data[i] += value
|
for i, fieldname in enumerate(["target", "actual", "variance"]):
|
||||||
totals[i] += value
|
value = flt(month_data.get(fieldname))
|
||||||
period_data[2] = period_data[0] - period_data[1]
|
period_data[i] += value
|
||||||
row += period_data
|
totals[i] += value
|
||||||
totals[2] = totals[0] - totals[1]
|
period_data[2] = period_data[0] - period_data[1]
|
||||||
row += totals
|
row += period_data
|
||||||
data.append(row)
|
totals[2] = totals[0] - totals[1]
|
||||||
|
row += totals
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
return columns, sorted(data, key=lambda x: (x[0], x[1]))
|
return columns, data
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
for fieldname in ["fiscal_year", "period", "company"]:
|
|
||||||
if not filters.get(fieldname):
|
|
||||||
label = (" ".join(fieldname.split("_"))).title()
|
|
||||||
msgprint(_("Please specify") + ": " + label,
|
|
||||||
raise_exception=True)
|
|
||||||
|
|
||||||
columns = [_("Cost Center") + ":Link/Cost Center:120", _("Account") + ":Link/Account:120"]
|
columns = [_("Cost Center") + ":Link/Cost Center:120", _("Account") + ":Link/Account:120"]
|
||||||
|
|
||||||
group_months = False if filters["period"] == "Monthly" else True
|
group_months = False if filters["period"] == "Monthly" else True
|
||||||
@ -52,7 +47,7 @@ def get_columns(filters):
|
|||||||
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
|
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
|
||||||
for label in [_("Target") + " (%s)", _("Actual") + " (%s)", _("Variance") + " (%s)"]:
|
for label in [_("Target") + " (%s)", _("Actual") + " (%s)", _("Variance") + " (%s)"]:
|
||||||
if group_months:
|
if group_months:
|
||||||
label = label % (formatdate(from_date, format_string="MMM") + " - " + formatdate(from_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")
|
||||||
|
|
||||||
@ -60,20 +55,21 @@ def get_columns(filters):
|
|||||||
|
|
||||||
return columns + [_("Total Target") + ":Float:120", _("Total Actual") + ":Float:120",
|
return columns + [_("Total Target") + ":Float:120", _("Total Actual") + ":Float:120",
|
||||||
_("Total Variance") + ":Float:120"]
|
_("Total Variance") + ":Float:120"]
|
||||||
|
|
||||||
|
def get_cost_centers(company):
|
||||||
|
return frappe.db.sql_list("select name from `tabCost Center` where company=%s order by lft", company)
|
||||||
|
|
||||||
#Get cost center & target details
|
#Get cost center & target details
|
||||||
def get_costcenter_target_details(filters):
|
def get_cost_center_target_details(filters):
|
||||||
return frappe.db.sql("""select cc.name, cc.distribution_id,
|
return frappe.db.sql("""
|
||||||
cc.parent_cost_center, bd.account, bd.budget_allocated
|
select b.cost_center, b.monthly_distribution, ba.account, ba.budget_amount
|
||||||
from `tabCost Center` cc, `tabBudget Detail` bd
|
from `tabBudget` b, `tabBudget Account` ba
|
||||||
where bd.parent=cc.name and bd.fiscal_year=%s and
|
where b.name=ba.parent and b.fiscal_year=%s and b.company=%s
|
||||||
cc.company=%s order by cc.name""" % ('%s', '%s'),
|
""", (filters.fiscal_year, filters.company), as_dict=True)
|
||||||
(filters.get("fiscal_year"), filters.get("company")), as_dict=1)
|
|
||||||
|
|
||||||
#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=%s""", (filters["fiscal_year"]), as_dict=1):
|
||||||
@ -82,45 +78,51 @@ def get_target_distribution_details(filters):
|
|||||||
return target_details
|
return target_details
|
||||||
|
|
||||||
#Get actual details from gl entry
|
#Get actual details from gl entry
|
||||||
def get_actual_details(filters):
|
def get_actual_details(cost_center, fiscal_year):
|
||||||
|
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", cost_center, ["lft", "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.cost_center, MONTHNAME(gl.posting_date) as month_name
|
MONTHNAME(gl.posting_date) as month_name, b.cost_center
|
||||||
from `tabGL Entry` gl, `tabBudget Detail` bd
|
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
|
||||||
where gl.fiscal_year=%s and company=%s
|
where
|
||||||
and bd.account=gl.account and bd.parent=gl.cost_center""" % ('%s', '%s'),
|
b.name = ba.parent
|
||||||
(filters.get("fiscal_year"), filters.get("company")), as_dict=1)
|
and ba.account=gl.account
|
||||||
|
and gl.fiscal_year=%s
|
||||||
|
and b.cost_center=%s
|
||||||
|
and exists(select name from `tabCost Center` where name=gl.cost_center and lft>=%s and rgt<=%s)
|
||||||
|
""", (fiscal_year, cost_center, cc_lft, cc_rgt), as_dict=1)
|
||||||
|
|
||||||
cc_actual_details = {}
|
cc_actual_details = {}
|
||||||
for d in ac_details:
|
for d in ac_details:
|
||||||
cc_actual_details.setdefault(d.cost_center, {}).setdefault(d.account, []).append(d)
|
cc_actual_details.setdefault(d.account, []).append(d)
|
||||||
|
|
||||||
return cc_actual_details
|
return cc_actual_details
|
||||||
|
|
||||||
def get_costcenter_account_month_map(filters):
|
def get_cost_center_account_month_map(filters):
|
||||||
import datetime
|
import datetime
|
||||||
costcenter_target_details = get_costcenter_target_details(filters)
|
cost_center_target_details = get_cost_center_target_details(filters)
|
||||||
tdd = get_target_distribution_details(filters)
|
tdd = get_target_distribution_details(filters)
|
||||||
actual_details = get_actual_details(filters)
|
|
||||||
|
|
||||||
cam_map = {}
|
cam_map = {}
|
||||||
|
|
||||||
for ccd in costcenter_target_details:
|
for ccd in cost_center_target_details:
|
||||||
|
actual_details = get_actual_details(ccd.cost_center, filters.fiscal_year)
|
||||||
|
|
||||||
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.name, {}).setdefault(ccd.account, {})\
|
cam_map.setdefault(ccd.cost_center, {}).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.name][ccd.account][month]
|
tav_dict = cam_map[ccd.cost_center][ccd.account][month]
|
||||||
|
month_percentage = tdd.get(ccd.monthly_distribution, {}).get(month, 0) \
|
||||||
|
if ccd.monthly_distribution else 100.0/12
|
||||||
|
|
||||||
month_percentage = tdd.get(ccd.distribution_id, {}).get(month, 0) \
|
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
||||||
if ccd.distribution_id else 100.0/12
|
|
||||||
|
for ad in actual_details.get(ccd.account, []):
|
||||||
tav_dict.target = flt(ccd.budget_allocated) * month_percentage / 100
|
|
||||||
|
|
||||||
for ad in actual_details.get(ccd.name, {}).get(ccd.account, []):
|
|
||||||
if ad.month_name == month:
|
if ad.month_name == month:
|
||||||
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
||||||
|
|
||||||
|
BIN
erpnext/docs/assets/img/accounts/budget-variance-report.png
Normal file
BIN
erpnext/docs/assets/img/accounts/budget-variance-report.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 128 KiB |
BIN
erpnext/docs/assets/img/accounts/budget.png
Normal file
BIN
erpnext/docs/assets/img/accounts/budget.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
BIN
erpnext/docs/assets/img/accounts/monthly-distribution.png
Normal file
BIN
erpnext/docs/assets/img/accounts/monthly-distribution.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
@ -69,11 +69,11 @@ Since Project's Cost Center is updated in both sales and purchase entries, you c
|
|||||||
|
|
||||||
#### 3.2 Projectwise Budgeting
|
#### 3.2 Projectwise Budgeting
|
||||||
|
|
||||||
If you have also define budgets in the Cost Center of a Project, you will get Budget Variance Report for a Cost Center of a Project.
|
You can define budgets against the Cost Center associated with a Project. At any point of time, you can refer Budget Variance Report to analysis the expense vs budget against a cost center.
|
||||||
|
|
||||||
To check Budget Variance report, go to:
|
To check Budget Variance report, go to:
|
||||||
|
|
||||||
`Accounts > Standard Reports > Budget Variance Report`
|
`Accounts > Budget and Cost Center > Budget Variance Report`
|
||||||
|
|
||||||
[Click here to learn how to do budgeting from Cost Center]({{docs_base_url}}/user/manual/en/accounts/budgeting.html).
|
[Click here to learn how to do budgeting from Cost Center]({{docs_base_url}}/user/manual/en/accounts/budgeting.html).
|
||||||
|
|
||||||
|
@ -1,45 +1,29 @@
|
|||||||
ERPNext will help you set and manage budgets on your Cost Centers. This is
|
In ERPNext, you can set and manage budgets against a Cost Center. This is useful when, for example, you are doing online sales. You have a budget for search ads, and you want ERPNext to stop or warn you from over spending, based on that budget.
|
||||||
useful when, for example, you are doing online sales. You have a budget for
|
|
||||||
search ads, and you want ERPNext to stop or warn you from over spending, based
|
|
||||||
on that budget.
|
|
||||||
|
|
||||||
Budgets are also great for planning purposes. When you are making plans for
|
Budgets are also great for planning purposes. When you are making plans for the next financial year, you would typically target a revenue based on which you would set your expenses. Setting a budget will ensure that your expenses do not get out of hand, at any point, as per your plans.
|
||||||
the next financial year, you would typically target a revenue based on which
|
|
||||||
you would set your expenses. Setting a budget will ensure that your expenses
|
|
||||||
do not get out of hand, at any point, as per your plans.
|
|
||||||
|
|
||||||
You can define it in the Cost Center. If you have seasonal sales you can also
|
To allocate budget, go to:
|
||||||
define a budget distribution that the budget will follow.
|
|
||||||
|
|
||||||
In order to allocate budget, go to:
|
> Accounts > Budget and Cost Center > Budget
|
||||||
|
|
||||||
> Accounts > Setup > Chart of Cost Centers
|
In the Budget form, you can select a Cost Center and for that cost center you can define budgets against any Expense / Income accounts. Budgets can be defined against any Cost Center whether it is a Group / Leaf node in the Chart of Cost Centers.
|
||||||
|
|
||||||
and click on Chart of Cost Center. Select a Cost Center and click on Open.
|
<img class="screenshot" alt="Budget" src="{{docs_base_url}}/assets/img/accounts/budget.png">
|
||||||
|
|
||||||
#### Step 1: Click on Edit.
|
If you have seasonal business, you can also define a Monthly Distribution record, to distribute the budget between months. If you don't set the monthly distribution, ERPNext will calculate the budget on yearly
|
||||||
|
|
||||||
<img alt="Cost Center" class="screenshot" src="{{docs_base_url}}/assets/img/accounts/cost-center-1.gif">
|
|
||||||
|
|
||||||
#### Step 3:Add New Row and select budget account.
|
|
||||||
|
|
||||||
<img alt="Cost Center Account" class="screenshot" src="{{docs_base_url}}/assets/img/accounts/cost-center-2.png">
|
|
||||||
|
|
||||||
#### Step 3: Enter Monthly Distribution (optional)
|
|
||||||
|
|
||||||
<img alt="Cost Center" class="screenshot" src="{{docs_base_url}}/assets/img/accounts/cost-center-distribution.gif">
|
|
||||||
|
|
||||||
If you leave the** **distribution ID blank, ERPNext will calculate on a yearly
|
|
||||||
basis or in equal proportion for every month.
|
basis or in equal proportion for every month.
|
||||||
|
|
||||||
### To Create New Distribution ID
|
<img class="screenshot" alt="Monthly Distribution" src="{{docs_base_url}}/assets/img/accounts/monthly-distribution.png">
|
||||||
|
|
||||||
ERPNext allows you to take a few budget actions. It signifies whether to stop, warn or Ignore if you exceed budgets.
|
While setting budget, you can also define the actions when expenses will exceed the allocated budget for a period. You can set separate action for monthly and annual budgets. There are 3 types of actions: Stop, Warn and Ignore. If Stop, system will not allow to book expenses more than allocated budget. In Case of Warn, it will just warn the user that expenses has been exceeded from the allocated budget. And Ignore will do nothing.
|
||||||
|
|
||||||
These can be defined from the Company record.
|
|
||||||
|
|
||||||
<img alt="Cost Center" class="screenshot" src="{{docs_base_url}}/assets/img/accounts/cost-center-company.gif">
|
At any point of time, user can check Budget Variance Report to analysis the expense vs budget against a cost center.
|
||||||
|
|
||||||
Even if you choose to “ignore” budget overruns, you will get a wealth of information from the “Budget vs Actual” variance report. This report shows month wise actual expenses as compared to the budgeted expenses.
|
To check Budget Variance report, go to:
|
||||||
|
|
||||||
|
Accounts > Budget and Cost Center > Budget Variance Report
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Budget Variance Report" src="{{docs_base_url}}/assets/img/accounts/budget-variance-report.png">
|
||||||
|
|
||||||
{next}
|
{next}
|
||||||
|
@ -34,45 +34,4 @@ To setup your Chart of Cost Centers go to:
|
|||||||
|
|
||||||
![Chart of Cost Center]({{docs_base_url}}/assets/old_images/erpnext/chart-of-cost-centers.png)
|
![Chart of Cost Center]({{docs_base_url}}/assets/old_images/erpnext/chart-of-cost-centers.png)
|
||||||
|
|
||||||
Cost centers help you in one more activity, budgeting.
|
|
||||||
|
|
||||||
### Budgeting
|
|
||||||
|
|
||||||
ERPNext will help you set and manage budgets on your Cost Centers. This is
|
|
||||||
useful when, for example, you are doing online sales. You have a budget for
|
|
||||||
search ads, and you want ERPNext to stop or warn you from over spending, based
|
|
||||||
on that budget.
|
|
||||||
|
|
||||||
Budgets are also great for planning purposes. When you are making plans for
|
|
||||||
the next financial year, you would typically target a revenue based on which
|
|
||||||
you would set your expenses. Setting a budget will ensure that your expenses
|
|
||||||
do not get out of hand, at any point, as per your plans.
|
|
||||||
|
|
||||||
You can define it in the Cost Center. If you have seasonal sales you can also
|
|
||||||
define a budget distribution that the budget will follow.
|
|
||||||
|
|
||||||
> Accounts > Setup > Budget Distribution > New Budget Distribution
|
|
||||||
|
|
||||||
![Budget Distribution]({{docs_base_url}}/assets/old_images/erpnext/budgeting.png)
|
|
||||||
|
|
||||||
#### Budget Actions
|
|
||||||
|
|
||||||
ERPNext allows you to either:
|
|
||||||
|
|
||||||
* Stop.
|
|
||||||
* Warn or,
|
|
||||||
* Ignore
|
|
||||||
|
|
||||||
if you exceed budgets.
|
|
||||||
|
|
||||||
These can be defined from the Company record.
|
|
||||||
|
|
||||||
Even if you choose to “ignore” budget overruns, you will get a wealth of
|
|
||||||
information from the “Budget vs Actual” variance report.
|
|
||||||
|
|
||||||
> Note: When you set a budget, it has to be set as per Account under the Cost
|
|
||||||
Center. For example if you have a Cost Center “Online Sales”, you can restrict
|
|
||||||
“Advertising Budget” by creating a row with that Account and defining the
|
|
||||||
amount.
|
|
||||||
|
|
||||||
{next}
|
{next}
|
||||||
|
@ -20,7 +20,7 @@ In this table, you should select Item Group, Fiscal Year, Target Qty and Amount.
|
|||||||
|
|
||||||
####1.3 Target Distribution
|
####1.3 Target Distribution
|
||||||
|
|
||||||
If you wish to spread allocated target across months, then you shoult setup Target Distribution master, and select it in the Sales Person master. Considering our example, target for the month of December will be set as 5 qty (10% of total allocation).
|
If you wish to spread allocated target across months, then you should setup Monthly Distribution master, and select it in the Sales Person master. Considering our example, target for the month of December will be set as 5 qty (10% of total allocation).
|
||||||
|
|
||||||
![Sales Person Target Distribution]({{docs_base_url}}/assets/old_images/erpnext/sales-person-target-distribution.png)
|
![Sales Person Target Distribution]({{docs_base_url}}/assets/old_images/erpnext/sales-person-target-distribution.png)
|
||||||
|
|
||||||
@ -52,11 +52,11 @@ In the Territory master, you will find field to select Territory Manager. This f
|
|||||||
|
|
||||||
####2.2 Allocating Target
|
####2.2 Allocating Target
|
||||||
|
|
||||||
Allocation Target in the Territory master is same as in Sales Person master. You can follow same steps as given above to specify target in the Territory master as well.
|
Target Allocation in the Territory master is same as in Sales Person master. You can follow same steps as given above to specify target in the Territory master as well.
|
||||||
|
|
||||||
####2.3 Target Distribution
|
####2.3 Target Distribution
|
||||||
|
|
||||||
Using this master, you can divide target Qty or Amount across various months.
|
Using this Monthly Distribution document, you can divide target Qty or Amount across various months.
|
||||||
|
|
||||||
####2.4 Report - Territory Target Variance Item Groupwise
|
####2.4 Report - Territory Target Variance Item Groupwise
|
||||||
|
|
||||||
@ -68,15 +68,15 @@ This report will provide you variance between target and actual performance of S
|
|||||||
|
|
||||||
###3. Target Distribution
|
###3. Target Distribution
|
||||||
|
|
||||||
Target Distribution master allows you to divide allocated target across multiple months. If your product and services is seasonal, you can distribute the sales target accordingly. For example, if you are into umbrella business, then target allocated in the monsoon seasion will be higher than in other months.
|
Target Distribution document allows you to divide allocated target across multiple months. If your product and services is seasonal, you can distribute the sales target accordingly. For example, if you are into umbrella business, then target allocated in the monsoon seasion will be higher than in other months.
|
||||||
|
|
||||||
To create new Budget Distriibution master, go to:
|
To create new Monthly Distriibution, go to:
|
||||||
|
|
||||||
`Accounts > Setup > Budget Distributon`
|
`Accounts > Monthly Distributon`
|
||||||
|
|
||||||
![Target Distribution]({{docs_base_url}}/assets/old_images/erpnext/target-distribution.png)
|
![Target Distribution]({{docs_base_url}}/assets/old_images/erpnext/target-distribution.png)
|
||||||
|
|
||||||
You can link target distribution while allocation targets in Sales Person as well as in Territory master.
|
You can link Monthly Distribution while allocating targets in Sales Person as well as in Territory master.
|
||||||
|
|
||||||
###See Also
|
###See Also
|
||||||
|
|
||||||
|
@ -264,4 +264,5 @@ erpnext.patches.v7_0.update_party_status
|
|||||||
erpnext.patches.v7_0.update_item_projected
|
erpnext.patches.v7_0.update_item_projected
|
||||||
erpnext.patches.v7_0.fix_duplicate_icons
|
erpnext.patches.v7_0.fix_duplicate_icons
|
||||||
erpnext.patches.v7_0.remove_features_setup
|
erpnext.patches.v7_0.remove_features_setup
|
||||||
erpnext.patches.v7_0.update_home_page
|
erpnext.patches.v7_0.update_home_page
|
||||||
|
erpnext.patches.v7_0.create_budget_record
|
@ -86,9 +86,6 @@ rename_map = {
|
|||||||
"Bank Reconciliation": [
|
"Bank Reconciliation": [
|
||||||
["entries", "journal_entries"]
|
["entries", "journal_entries"]
|
||||||
],
|
],
|
||||||
"Cost Center": [
|
|
||||||
["budget_details", "budgets"]
|
|
||||||
],
|
|
||||||
"C-Form": [
|
"C-Form": [
|
||||||
["invoice_details", "invoices"]
|
["invoice_details", "invoices"]
|
||||||
],
|
],
|
||||||
|
46
erpnext/patches/v7_0/create_budget_record.py
Normal file
46
erpnext/patches/v7_0/create_budget_record.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
existing_budgets = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
cc.name, cc.company, cc.distribution_id,
|
||||||
|
budget.account, budget.budget_allocated, budget.fiscal_year
|
||||||
|
from
|
||||||
|
`tabCost Center` cc, `tabBudget Detail` budget
|
||||||
|
where
|
||||||
|
cc.name=budget.parent
|
||||||
|
""", as_dict=1)
|
||||||
|
|
||||||
|
actions = {}
|
||||||
|
for d in frappe.db.sql("select name, yearly_bgt_flag, monthly_bgt_flag from tabCompany", as_dict=1):
|
||||||
|
actions.setdefault(d.name, d)
|
||||||
|
|
||||||
|
budget_records = []
|
||||||
|
for d in existing_budgets:
|
||||||
|
budget = frappe.db.get_value("Budget",
|
||||||
|
{"cost_center": d.name, "fiscal_year": d.fiscal_year, "company": d.company})
|
||||||
|
|
||||||
|
if not budget:
|
||||||
|
budget = frappe.new_doc("Budget")
|
||||||
|
budget.cost_center = d.name
|
||||||
|
budget.fiscal_year = d.fiscal_year
|
||||||
|
budget.monthly_distribution = d.distribution_id
|
||||||
|
budget.company = d.company
|
||||||
|
budget.action_if_annual_budget_exceeded = actions[d.company]["yearly_bgt_flag"]
|
||||||
|
budget.action_if_accumulated_monthly_budget_exceeded = actions[d.company]["monthly_bgt_flag"]
|
||||||
|
else:
|
||||||
|
budget = frappe.get_doc("Budget", budget)
|
||||||
|
|
||||||
|
budget.append("accounts", {
|
||||||
|
"account": d.account,
|
||||||
|
"budget_amount": d.budget_allocated
|
||||||
|
})
|
||||||
|
|
||||||
|
budget.insert()
|
||||||
|
budget_records.append(budget)
|
||||||
|
|
||||||
|
for budget in budget_records:
|
||||||
|
budget.submit()
|
||||||
|
|
||||||
|
if frappe.db.get_value("DocType", "Budget Detail"):
|
||||||
|
frappe.delete_doc("DocType", "Budget Detail")
|
@ -3,6 +3,7 @@
|
|||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:company_name",
|
"autoname": "field:company_name",
|
||||||
|
"beta": 0,
|
||||||
"creation": "2013-04-10 08:35:39",
|
"creation": "2013-04-10 08:35:39",
|
||||||
"custom": 0,
|
"custom": 0,
|
||||||
"description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.",
|
"description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.",
|
||||||
@ -661,7 +662,7 @@
|
|||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Cost Center",
|
"label": "Deafult Cost Center",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
@ -675,59 +676,6 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"fieldname": "credit_days_based_on",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Credit Days Based On",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nFixed Days\nLast Day of the Next Month",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"depends_on": "eval:(!doc.__islocal && doc.credit_days_based_on=='Fixed Days')",
|
|
||||||
"fieldname": "credit_days",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"label": "Credit Days",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "credit_days",
|
|
||||||
"oldfieldtype": "Int",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
@ -784,21 +732,19 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "eval:!doc.__islocal",
|
"fieldname": "credit_days_based_on",
|
||||||
"fieldname": "yearly_bgt_flag",
|
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "If Yearly Budget Exceeded (for expense account)",
|
"label": "Credit Days Based On",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "yearly_bgt_flag",
|
"options": "\nFixed Days\nLast Day of the Next Month",
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "\nWarn\nIgnore\nStop",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
@ -812,20 +758,19 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"depends_on": "eval:!doc.__islocal",
|
"depends_on": "eval:(!doc.__islocal && doc.credit_days_based_on=='Fixed Days')",
|
||||||
"fieldname": "monthly_bgt_flag",
|
"fieldname": "credit_days",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "If Monthly Budget Exceeded (for expense account)",
|
"label": "Credit Days",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "monthly_bgt_flag",
|
"oldfieldname": "credit_days",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Int",
|
||||||
"options": "\nWarn\nIgnore\nStop",
|
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
@ -1387,7 +1332,7 @@
|
|||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": 0,
|
"menu_index": 0,
|
||||||
"modified": "2016-03-10 04:34:43.440914",
|
"modified": "2016-05-16 15:24:47.178826",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Company",
|
"name": "Company",
|
||||||
@ -1534,7 +1479,9 @@
|
|||||||
"write": 0
|
"write": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"quick_entry": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
"sort_order": "ASC"
|
"sort_order": "ASC",
|
||||||
|
"track_seen": 0
|
||||||
}
|
}
|
@ -194,17 +194,11 @@ class Company(Document):
|
|||||||
|
|
||||||
rec = frappe.db.sql("SELECT name from `tabGL Entry` where company = %s", self.name)
|
rec = frappe.db.sql("SELECT name from `tabGL Entry` where company = %s", self.name)
|
||||||
if not rec:
|
if not rec:
|
||||||
# delete Account
|
frappe.db.sql("""delete from `tabBudget Account` b
|
||||||
frappe.db.sql("delete from `tabAccount` where company = %s", self.name)
|
where exists(select name from tabBudget where name=b.parent and company = %s)""", self.name)
|
||||||
|
|
||||||
# delete cost center child table - budget detail
|
for doctype in ["Account", "Cost Center", "Budget", "Party Account"]:
|
||||||
frappe.db.sql("""delete bd.* from `tabBudget Detail` bd, `tabCost Center` cc
|
frappe.db.sql("delete from `tab{0}` where company = %s".format(doctype), self.name)
|
||||||
where bd.parent = cc.name and cc.company = %s""", self.name)
|
|
||||||
#delete cost center
|
|
||||||
frappe.db.sql("delete from `tabCost Center` WHERE company = %s", self.name)
|
|
||||||
|
|
||||||
# delete account from customer and supplier
|
|
||||||
frappe.db.sql("delete from `tabParty Account` where company=%s", self.name)
|
|
||||||
|
|
||||||
if not frappe.db.get_value("Stock Ledger Entry", {"company": self.name}):
|
if not frappe.db.get_value("Stock Ledger Entry", {"company": self.name}):
|
||||||
frappe.db.sql("""delete from `tabWarehouse` where company=%s""", self.name)
|
frappe.db.sql("""delete from `tabWarehouse` where company=%s""", self.name)
|
||||||
|
@ -14,7 +14,8 @@ def delete_company_transactions(company_name):
|
|||||||
doc = frappe.get_doc("Company", company_name)
|
doc = frappe.get_doc("Company", company_name)
|
||||||
|
|
||||||
if frappe.session.user != doc.owner:
|
if frappe.session.user != doc.owner:
|
||||||
frappe.throw(_("Transactions can only be deleted by the creator of the Company"), frappe.PermissionError)
|
frappe.throw(_("Transactions can only be deleted by the creator of the Company"),
|
||||||
|
frappe.PermissionError)
|
||||||
|
|
||||||
delete_bins(company_name)
|
delete_bins(company_name)
|
||||||
delete_time_logs(company_name)
|
delete_time_logs(company_name)
|
||||||
@ -22,7 +23,7 @@ def delete_company_transactions(company_name):
|
|||||||
|
|
||||||
for doctype in frappe.db.sql_list("""select parent from
|
for doctype in frappe.db.sql_list("""select parent from
|
||||||
tabDocField where fieldtype='Link' and options='Company'"""):
|
tabDocField where fieldtype='Link' and options='Company'"""):
|
||||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail",
|
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
|
||||||
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
||||||
"Purchase Taxes and Charges Template", "POS Profile", 'BOM'):
|
"Purchase Taxes and Charges Template", "POS Profile", 'BOM'):
|
||||||
delete_for_doctype(doctype, company_name)
|
delete_for_doctype(doctype, company_name)
|
||||||
|
Loading…
Reference in New Issue
Block a user