[feature] New document for Budget
This commit is contained in:
parent
434ae21785
commit
b9bc7d6df3
0
erpnext/accounts/doctype/budget/__init__.py
Normal file
0
erpnext/accounts/doctype/budget/__init__.py
Normal file
31
erpnext/accounts/doctype/budget/budget.js
Normal file
31
erpnext/accounts/doctype/budget/budget.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Budget', {
|
||||||
|
onload: function(frm) {
|
||||||
|
frm.set_query("cost_center", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
frm.set_query("account", "accounts", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company,
|
||||||
|
report_type: "Profit and Loss"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
frm.set_query("monthly_distribution", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
fiscal_year: frm.doc.fiscal_year
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
314
erpnext/accounts/doctype/budget/budget.json
Normal file
314
erpnext/accounts/doctype/budget/budget.json
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 1,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2016-05-16 11:42:29.632528",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Cost Center",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Cost Center",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 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,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Fiscal Year",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 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": "monthly_distribution",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Monthly Distribution",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Monthly Distribution",
|
||||||
|
"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,
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"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,
|
||||||
|
"default": "Stop",
|
||||||
|
"fieldname": "action_if_annual_budget_exceeded",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Action if Annual Budget Exceeded",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "\nStop\nWarn\nIgnore",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 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,
|
||||||
|
"default": "Warn",
|
||||||
|
"description": "",
|
||||||
|
"fieldname": "action_if_accumulated_monthly_budget_exceeded",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Action if Accumulated Monthly Budget Exceeded",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "\nStop\nWarn\nIgnore",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 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": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Company",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 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": "amended_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Amended From",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Budget",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"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,
|
||||||
|
"fieldname": "accounts",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"label": "Budget Accounts",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Budget Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 1,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 0,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2016-05-16 13:12:10.439375",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Budget",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"amend": 1,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 1,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 1,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
||||||
99
erpnext/accounts/doctype/budget/budget.py
Normal file
99
erpnext/accounts/doctype/budget/budget.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import flt, getdate, add_months, get_last_day
|
||||||
|
from frappe.model.naming import make_autoname
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class BudgetError(frappe.ValidationError): pass
|
||||||
|
|
||||||
|
class Budget(Document):
|
||||||
|
def autoname(self):
|
||||||
|
self.name = make_autoname(self.cost_center + "/" + self.fiscal_year + "/.###")
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.validate_duplicate()
|
||||||
|
|
||||||
|
def validate_duplicate(self):
|
||||||
|
existing_budget = frappe.db.get_value("Budget", {"cost_center": self.cost_center,
|
||||||
|
"fiscal_year": self.fiscal_year, "name": ["!=", self.name], "docstatus": ["!=", 2]})
|
||||||
|
if existing_budget:
|
||||||
|
frappe.throw(_("Another Budget record {0} already exists against {1} for fiscal year {2}")
|
||||||
|
.format(existing_budget, self.cost_center, self.fiscal_year))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_expense_against_budget(args):
|
||||||
|
args = frappe._dict(args)
|
||||||
|
if frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"}):
|
||||||
|
budget = frappe.db.sql("""
|
||||||
|
select ba.budget_amount, b.monthly_distribution,
|
||||||
|
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded
|
||||||
|
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
|
||||||
|
""", (args.cost_center, args.fiscal_year, args.account), as_dict=True)
|
||||||
|
|
||||||
|
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"]:
|
||||||
|
budget_amount = get_accumulated_monthly_budget(budget[0].monthly_distribution,
|
||||||
|
args.posting_date, args.fiscal_year, budget[0].budget_amount)
|
||||||
|
|
||||||
|
args["month_end_date"] = get_last_day(args.posting_date)
|
||||||
|
|
||||||
|
action_for, action = _("Accumulated Monthly"), monthly_action
|
||||||
|
|
||||||
|
elif yearly_action in ["Stop", "Warn"]:
|
||||||
|
budget_amount = flt(budget[0].budget_amount)
|
||||||
|
action_for, action = _("Annual"), yearly_action
|
||||||
|
|
||||||
|
if action_for:
|
||||||
|
actual_expense = get_actual_expense(args)
|
||||||
|
if 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)
|
||||||
|
|
||||||
|
if action=="Stop":
|
||||||
|
frappe.throw(msg, BudgetError)
|
||||||
|
else:
|
||||||
|
frappe.msgprint(msg)
|
||||||
|
|
||||||
|
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
|
||||||
|
distribution = {}
|
||||||
|
if monthly_distribution:
|
||||||
|
for d in frappe.db.sql("""select mdp.month, mdp.percentage_allocation
|
||||||
|
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
||||||
|
where mdp.parent=md.name and md.fiscal_year=%s""", fiscal_year, as_dict=1):
|
||||||
|
distribution.setdefault(d.month, d.percentage_allocation)
|
||||||
|
|
||||||
|
dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
|
||||||
|
accumulated_percentage = 0.0
|
||||||
|
|
||||||
|
while(dt <= getdate(posting_date)):
|
||||||
|
if monthly_distribution:
|
||||||
|
accumulated_percentage += distribution.get(getdate(dt).strftime("%B"), 0)
|
||||||
|
else:
|
||||||
|
accumulated_percentage += 100.0/12
|
||||||
|
|
||||||
|
dt = add_months(dt, 1)
|
||||||
|
|
||||||
|
return annual_budget * accumulated_percentage / 100
|
||||||
|
|
||||||
|
def get_actual_expense(args):
|
||||||
|
args["condition"] = " and posting_date <= '%s'" % \
|
||||||
|
args.month_end_date if args.get("month_end_date") else ""
|
||||||
|
|
||||||
|
return flt(frappe.db.sql("""
|
||||||
|
select sum(debit) - sum(credit)
|
||||||
|
from `tabGL Entry`
|
||||||
|
where account='%(account)s' and cost_center='%(cost_center)s'
|
||||||
|
and fiscal_year='%(fiscal_year)s' and company='%(company)s' and docstatus=1 %(condition)s
|
||||||
|
""" % (args))[0][0])
|
||||||
12
erpnext/accounts/doctype/budget/test_budget.py
Normal file
12
erpnext/accounts/doctype/budget/test_budget.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
# test_records = frappe.get_test_records('Budget')
|
||||||
|
|
||||||
|
class TestBudget(unittest.TestCase):
|
||||||
|
pass
|
||||||
0
erpnext/accounts/doctype/budget_account/__init__.py
Normal file
0
erpnext/accounts/doctype/budget_account/__init__.py
Normal file
110
erpnext/accounts/doctype/budget_account/budget_account.json
Normal file
110
erpnext/accounts/doctype/budget_account/budget_account.json
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2016-05-16 11:54:09.286135",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"fieldname": "account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Account",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 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": "column_break_2",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"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,
|
||||||
|
"fieldname": "budget_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Budget Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"in_dialog": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 1,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2016-05-16 11:55:29.586591",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Budget Account",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
||||||
10
erpnext/accounts/doctype/budget_account/budget_account.py
Normal file
10
erpnext/accounts/doctype/budget_account/budget_account.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class BudgetAccount(Document):
|
||||||
|
pass
|
||||||
@ -4,7 +4,7 @@
|
|||||||
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.utils import get_actual_expense, BudgetError, get_fiscal_year
|
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError, get_fiscal_year
|
||||||
from erpnext.exceptions import InvalidAccountCurrency
|
from erpnext.exceptions import InvalidAccountCurrency
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe.utils import flt, cstr, cint
|
from frappe.utils import flt, cstr, cint
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from erpnext.accounts.utils import validate_expense_against_budget
|
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||||
|
|
||||||
|
|
||||||
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import nowdate, cstr, flt, cint, now, getdate, add_months
|
from frappe.utils import nowdate, cstr, flt, cint, now, getdate
|
||||||
from frappe import throw, _
|
from frappe import throw, _
|
||||||
from frappe.utils import formatdate
|
from frappe.utils import formatdate
|
||||||
import frappe.desk.reportview
|
import frappe.desk.reportview
|
||||||
@ -13,7 +13,6 @@ import frappe.desk.reportview
|
|||||||
from erpnext.accounts.doctype.account.account import get_account_currency
|
from erpnext.accounts.doctype.account.account import get_account_currency
|
||||||
|
|
||||||
class FiscalYearError(frappe.ValidationError): pass
|
class FiscalYearError(frappe.ValidationError): pass
|
||||||
class BudgetError(frappe.ValidationError): pass
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
|
def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
|
||||||
@ -321,72 +320,6 @@ def get_stock_and_account_difference(account_list=None, posting_date=None):
|
|||||||
|
|
||||||
return difference
|
return difference
|
||||||
|
|
||||||
def validate_expense_against_budget(args):
|
|
||||||
args = frappe._dict(args)
|
|
||||||
if frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"}):
|
|
||||||
budget = frappe.db.sql("""
|
|
||||||
select bd.budget_allocated, cc.distribution_id
|
|
||||||
from `tabCost Center` cc, `tabBudget Detail` bd
|
|
||||||
where cc.name=bd.parent and cc.name=%s and account=%s and bd.fiscal_year=%s
|
|
||||||
""", (args.cost_center, args.account, args.fiscal_year), as_dict=True)
|
|
||||||
|
|
||||||
if budget and budget[0].budget_allocated:
|
|
||||||
yearly_action, monthly_action = frappe.db.get_value("Company", args.company,
|
|
||||||
["yearly_bgt_flag", "monthly_bgt_flag"])
|
|
||||||
action_for = action = ""
|
|
||||||
|
|
||||||
if monthly_action in ["Stop", "Warn"]:
|
|
||||||
budget_amount = get_allocated_budget(budget[0].distribution_id,
|
|
||||||
args.posting_date, args.fiscal_year, budget[0].budget_allocated)
|
|
||||||
|
|
||||||
args["month_end_date"] = frappe.db.sql("select LAST_DAY(%s)",
|
|
||||||
args.posting_date)[0][0]
|
|
||||||
action_for, action = _("Monthly"), monthly_action
|
|
||||||
|
|
||||||
elif yearly_action in ["Stop", "Warn"]:
|
|
||||||
budget_amount = budget[0].budget_allocated
|
|
||||||
action_for, action = _("Annual"), yearly_action
|
|
||||||
|
|
||||||
if action_for:
|
|
||||||
actual_expense = get_actual_expense(args)
|
|
||||||
if actual_expense > budget_amount:
|
|
||||||
frappe.msgprint(_("{0} budget for Account {1} against Cost Center {2} will exceed by {3}").format(
|
|
||||||
_(action_for), args.account, args.cost_center, cstr(actual_expense - budget_amount)))
|
|
||||||
if action=="Stop":
|
|
||||||
raise BudgetError
|
|
||||||
|
|
||||||
def get_allocated_budget(distribution_id, posting_date, fiscal_year, yearly_budget):
|
|
||||||
if distribution_id:
|
|
||||||
distribution = {}
|
|
||||||
for d in frappe.db.sql("""select mdp.month, mdp.percentage_allocation
|
|
||||||
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
|
||||||
where mdp.parent=md.name and md.fiscal_year=%s""", fiscal_year, as_dict=1):
|
|
||||||
distribution.setdefault(d.month, d.percentage_allocation)
|
|
||||||
|
|
||||||
dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
|
|
||||||
budget_percentage = 0.0
|
|
||||||
|
|
||||||
while(dt <= getdate(posting_date)):
|
|
||||||
if distribution_id:
|
|
||||||
budget_percentage += distribution.get(getdate(dt).strftime("%B"), 0)
|
|
||||||
else:
|
|
||||||
budget_percentage += 100.0/12
|
|
||||||
|
|
||||||
dt = add_months(dt, 1)
|
|
||||||
|
|
||||||
return yearly_budget * budget_percentage / 100
|
|
||||||
|
|
||||||
def get_actual_expense(args):
|
|
||||||
args["condition"] = " and posting_date<='%s'" % args.month_end_date \
|
|
||||||
if args.get("month_end_date") else ""
|
|
||||||
|
|
||||||
return flt(frappe.db.sql("""
|
|
||||||
select sum(debit) - sum(credit)
|
|
||||||
from `tabGL Entry`
|
|
||||||
where account='%(account)s' and cost_center='%(cost_center)s'
|
|
||||||
and fiscal_year='%(fiscal_year)s' and company='%(company)s' %(condition)s
|
|
||||||
""" % (args))[0][0])
|
|
||||||
|
|
||||||
def get_currency_precision(currency=None):
|
def get_currency_precision(currency=None):
|
||||||
if not currency:
|
if not currency:
|
||||||
currency = frappe.db.get_value("Company",
|
currency = frappe.db.get_value("Company",
|
||||||
|
|||||||
@ -200,6 +200,11 @@ def get_data():
|
|||||||
"description": _("Tree of financial Cost Centers."),
|
"description": _("Tree of financial Cost Centers."),
|
||||||
"doctype": "Cost Center",
|
"doctype": "Cost Center",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Budget",
|
||||||
|
"description": _("Define budget for a financial year.")
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "report",
|
"type": "report",
|
||||||
"name": "Budget Variance Report",
|
"name": "Budget Variance Report",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user