Redesigned budgets

This commit is contained in:
Nabin Hait 2016-05-18 12:22:42 +05:30
parent b9bc7d6df3
commit ca90963d5b
29 changed files with 501 additions and 745 deletions

View File

@ -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
} }
} }
}) })

View File

@ -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",

View File

@ -17,49 +17,75 @@ 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,
budget_records = frappe.db.sql("""
select ba.budget_amount, b.monthly_distribution, b.cost_center,
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded
from `tabBudget` b, `tabBudget Account` ba from `tabBudget` b, `tabBudget Account` ba
where b.name=ba.parent and b.cost_center=%s and b.fiscal_year=%s and ba.account=%s where
""", (args.cost_center, args.fiscal_year, args.account), as_dict=True) 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)
if budget and budget[0].budget_amount: for budget in budget_records:
yearly_action = budget[0].action_if_annual_budget_exceeded if budget.budget_amount:
monthly_action = budget[0].action_if_accumulated_monthly_budget_exceeded yearly_action = budget.action_if_annual_budget_exceeded
monthly_action = budget.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)
@ -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])

View File

@ -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
# test_records = frappe.get_test_records('Budget') from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
class TestBudget(unittest.TestCase): class TestBudget(unittest.TestCase):
pass def test_monthly_budget_crossed_ignore(self):
budget = make_budget()
frappe.db.set_value("Budget", budget, "action_if_accumulated_monthly_budget_exceeded", "Ignore")
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):
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

View File

@ -1 +0,0 @@
Budget amounts for year and distribution for parent Cost Center.

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -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"
}

View File

@ -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

View File

@ -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 { return {
filters:[ filters: {
['Account', 'company', '=', me.frm.doc.company], company: frm.doc.company,
['Account', 'is_group', '=', '0'] is_group: 1
]
} }
});
} }
})
this.frm.set_query("parent_cost_center", function() {
return {
filters:[
['Cost Center', 'is_group', '=', '1'],
['Cost Center', 'company', '=', me.frm.doc.company],
]
} }
}); })
}
});
$.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 = '';

View File

@ -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
} }

View File

@ -16,7 +16,6 @@ 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:
@ -24,29 +23,6 @@ class CostCenter(NestedSet):
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():
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes")) frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))

View File

@ -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"

View File

@ -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)

View File

@ -3,9 +3,10 @@
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"autoname": "field:distribution_id", "autoname": "field:distribution_id",
"beta": 0,
"creation": "2013-01-10 16:34:05", "creation": "2013-01-10 16:34:05",
"custom": 0, "custom": 0,
"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**", "description": "**Monthly Distribution** helps you distribute the Budget/Target across months if you have seasonality in your business.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
@ -101,7 +102,7 @@
"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",
@ -149,8 +150,10 @@
"write": 0 "write": 0
} }
], ],
"quick_entry": 1,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC",
"track_seen": 0
} }

View File

@ -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
}
] ]
} }

View File

@ -3,22 +3,23 @@
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:
cost_center_items = cam_map.get(cost_center)
if cost_center_items:
for account, monthwise_data in cost_center_items.items(): for account, monthwise_data in cost_center_items.items():
row = [cost_center, account] row = [cost_center, account]
totals = [0, 0, 0] totals = [0, 0, 0]
@ -36,15 +37,9 @@ def execute(filters=None):
row += totals row += totals
data.append(row) 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")
@ -61,19 +56,20 @@ 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
tav_dict.target = flt(ccd.budget_allocated) * month_percentage / 100 for ad in actual_details.get(ccd.account, []):
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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@ -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).

View File

@ -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}

View File

@ -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}

View File

@ -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

View File

@ -265,3 +265,4 @@ 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

View File

@ -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"]
], ],

View 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")

View File

@ -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
} }

View File

@ -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)

View File

@ -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)