fix: Budget against accounting dimensions (#21268)
* fix: Budget warning against custom accounting dimension * fix: Codacy Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
parent
168babfebc
commit
fa8396feb5
@ -9,6 +9,7 @@ from frappe.utils import flt, getdate, add_months, get_last_day, fmt_money, nowd
|
|||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class BudgetError(frappe.ValidationError): pass
|
class BudgetError(frappe.ValidationError): pass
|
||||||
class DuplicateBudgetError(frappe.ValidationError): pass
|
class DuplicateBudgetError(frappe.ValidationError): pass
|
||||||
@ -98,24 +99,26 @@ def validate_expense_against_budget(args):
|
|||||||
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
if not (args.get('account') and args.get('cost_center')) and args.item_code:
|
||||||
args.cost_center, args.account = get_item_details(args)
|
args.cost_center, args.account = get_item_details(args)
|
||||||
|
|
||||||
if not (args.cost_center or args.project) and not args.account:
|
if not args.account:
|
||||||
return
|
return
|
||||||
|
|
||||||
for budget_against in ['project', 'cost_center']:
|
for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
|
||||||
if (args.get(budget_against) and args.account
|
if (args.get(budget_against) and args.account
|
||||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
||||||
|
|
||||||
if args.project and budget_against == 'project':
|
doctype = frappe.unscrub(budget_against)
|
||||||
condition = "and b.project=%s" % frappe.db.escape(args.project)
|
|
||||||
args.budget_against_field = "Project"
|
|
||||||
|
|
||||||
elif args.cost_center and budget_against == 'cost_center':
|
if frappe.get_cached_value('DocType', doctype, 'is_tree'):
|
||||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
|
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
|
||||||
condition = """and exists(select name from `tabCost Center`
|
condition = """and exists(select name from `tab%s`
|
||||||
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt)
|
where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
|
||||||
args.budget_against_field = "Cost Center"
|
args.is_tree = True
|
||||||
|
else:
|
||||||
|
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
|
||||||
|
args.is_tree = False
|
||||||
|
|
||||||
args.budget_against = args.get(budget_against)
|
args.budget_against_field = budget_against
|
||||||
|
args.budget_against_doctype = doctype
|
||||||
|
|
||||||
budget_records = frappe.db.sql("""
|
budget_records = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
@ -132,9 +135,7 @@ def validate_expense_against_budget(args):
|
|||||||
b.name=ba.parent and b.fiscal_year=%s
|
b.name=ba.parent and b.fiscal_year=%s
|
||||||
and ba.account=%s and b.docstatus=1
|
and ba.account=%s and b.docstatus=1
|
||||||
{condition}
|
{condition}
|
||||||
""".format(condition=condition,
|
""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
|
||||||
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
|
|
||||||
(args.fiscal_year, args.account), as_dict=True)
|
|
||||||
|
|
||||||
if budget_records:
|
if budget_records:
|
||||||
validate_budget_records(args, budget_records)
|
validate_budget_records(args, budget_records)
|
||||||
@ -230,7 +231,7 @@ def get_ordered_amount(args, budget):
|
|||||||
|
|
||||||
def get_other_condition(args, budget, for_doc):
|
def get_other_condition(args, budget, for_doc):
|
||||||
condition = "expense_account = '%s'" % (args.expense_account)
|
condition = "expense_account = '%s'" % (args.expense_account)
|
||||||
budget_against_field = frappe.scrub(args.get("budget_against_field"))
|
budget_against_field = args.get("budget_against_field")
|
||||||
|
|
||||||
if budget_against_field and args.get(budget_against_field):
|
if budget_against_field and args.get(budget_against_field):
|
||||||
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
|
||||||
@ -246,19 +247,30 @@ def get_other_condition(args, budget, for_doc):
|
|||||||
return condition
|
return condition
|
||||||
|
|
||||||
def get_actual_expense(args):
|
def get_actual_expense(args):
|
||||||
|
if not args.budget_against_doctype:
|
||||||
|
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
|
||||||
|
|
||||||
|
budget_against_field = args.get('budget_against_field')
|
||||||
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
||||||
if args.get("month_end_date") else ""
|
if args.get("month_end_date") else ""
|
||||||
if args.budget_against_field == "Cost Center":
|
|
||||||
lft_rgt = frappe.db.get_value(args.budget_against_field,
|
if args.is_tree:
|
||||||
args.budget_against, ["lft", "rgt"], as_dict=1)
|
lft_rgt = frappe.db.get_value(args.budget_against_doctype,
|
||||||
|
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
|
||||||
|
|
||||||
args.update(lft_rgt)
|
args.update(lft_rgt)
|
||||||
condition2 = """and exists(select name from `tabCost Center`
|
|
||||||
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
|
|
||||||
|
|
||||||
elif args.budget_against_field == "Project":
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)"
|
where lft>=%(lft)s and rgt<=%(rgt)s
|
||||||
|
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
|
||||||
|
budget_against_field=budget_against_field)
|
||||||
|
else:
|
||||||
|
condition2 = """and exists(select name from `tab{doctype}`
|
||||||
|
where name=gle.{budget_against} and
|
||||||
|
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
|
||||||
|
budget_against = budget_against_field)
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
amount = flt(frappe.db.sql("""
|
||||||
select sum(gle.debit) - sum(gle.credit)
|
select sum(gle.debit) - sum(gle.credit)
|
||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
where gle.account=%(account)s
|
where gle.account=%(account)s
|
||||||
@ -267,7 +279,9 @@ def get_actual_expense(args):
|
|||||||
and gle.company=%(company)s
|
and gle.company=%(company)s
|
||||||
and gle.docstatus=1
|
and gle.docstatus=1
|
||||||
{condition2}
|
{condition2}
|
||||||
""".format(condition1=condition1, condition2=condition2), (args))[0][0])
|
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
|
||||||
|
|
||||||
|
return amount
|
||||||
|
|
||||||
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 = {}
|
||||||
|
@ -13,7 +13,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
|
|||||||
|
|
||||||
class TestBudget(unittest.TestCase):
|
class TestBudget(unittest.TestCase):
|
||||||
def test_monthly_budget_crossed_ignore(self):
|
def test_monthly_budget_crossed_ignore(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop1(self):
|
def test_monthly_budget_crossed_stop1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_exception_approver_role(self):
|
def test_exception_approver_role(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_crossed_stop2(self):
|
def test_monthly_budget_crossed_stop2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop1(self):
|
def test_yearly_budget_crossed_stop1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_yearly_budget_crossed_stop2(self):
|
def test_yearly_budget_crossed_stop2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation1(self):
|
def test_monthly_budget_on_cancellation1(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center")
|
budget = make_budget(budget_against="Cost Center")
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_on_cancellation2(self):
|
def test_monthly_budget_on_cancellation2(self):
|
||||||
set_total_expense_zero("2013-02-28", "Project")
|
set_total_expense_zero("2013-02-28", "project")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Project")
|
budget = make_budget(budget_against="Project")
|
||||||
|
|
||||||
@ -201,8 +201,8 @@ class TestBudget(unittest.TestCase):
|
|||||||
budget.cancel()
|
budget.cancel()
|
||||||
|
|
||||||
def test_monthly_budget_against_group_cost_center(self):
|
def test_monthly_budget_against_group_cost_center(self):
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center")
|
set_total_expense_zero("2013-02-28", "cost_center")
|
||||||
set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC")
|
set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
|
||||||
|
|
||||||
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
|
||||||
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
|
||||||
@ -241,25 +241,30 @@ class TestBudget(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
|
||||||
if budget_against_field == "Project":
|
if budget_against_field == "project":
|
||||||
budget_against = "_Test Project"
|
budget_against = "_Test Project"
|
||||||
else:
|
else:
|
||||||
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
budget_against = budget_against_CC or "_Test Cost Center - _TC"
|
||||||
existing_expense = get_actual_expense(frappe._dict({
|
|
||||||
|
args = frappe._dict({
|
||||||
"account": "_Test Account Cost for Goods Sold - _TC",
|
"account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
"monthly_end_date": posting_date,
|
"monthly_end_date": posting_date,
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"fiscal_year": "_Test Fiscal Year 2013",
|
"fiscal_year": "_Test Fiscal Year 2013",
|
||||||
"budget_against_field": budget_against_field,
|
"budget_against_field": budget_against_field,
|
||||||
"budget_against": budget_against
|
})
|
||||||
}))
|
|
||||||
|
if not args.get(budget_against_field):
|
||||||
|
args[budget_against_field] = budget_against
|
||||||
|
|
||||||
|
existing_expense = get_actual_expense(args)
|
||||||
|
|
||||||
if existing_expense:
|
if existing_expense:
|
||||||
if budget_against_field == "Cost Center":
|
if budget_against_field == "cost_center":
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
|
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
|
||||||
elif budget_against_field == "Project":
|
elif budget_against_field == "project":
|
||||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
|
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user