Merge branch 'develop' of https://github.com/frappe/erpnext into dimensions
diff --git a/erpnext/accounts/doctype/accounting_dimension/__init__.py b/erpnext/accounts/doctype/accounting_dimension/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension/__init__.py
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
new file mode 100644
index 0000000..0676731
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Accounting Dimension', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json
new file mode 100644
index 0000000..13902d6
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json
@@ -0,0 +1,55 @@
+{
+ "autoname": "field:label",
+ "creation": "2019-05-04 18:13:37.002352",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "document_type",
+ "label",
+ "fieldname"
+ ],
+ "fields": [
+ {
+ "fieldname": "label",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Label",
+ "unique": 1
+ },
+ {
+ "fieldname": "fieldname",
+ "fieldtype": "Data",
+ "label": "Fieldname"
+ },
+ {
+ "fieldname": "document_type",
+ "fieldtype": "Link",
+ "label": "Document Type",
+ "options": "DocType",
+ "reqd": 1
+ }
+ ],
+ "modified": "2019-05-09 15:30:55.339917",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Accounting Dimension",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
new file mode 100644
index 0000000..e56492c
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, 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
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe import scrub
+from frappe.utils import cstr
+
+class AccountingDimension(Document):
+
+ def before_insert(self):
+ self.set_fieldname_and_label()
+ self.make_accounting_dimension_in_accounting_doctypes()
+
+ def on_trash(self):
+ self.delete_accounting_dimension()
+
+ def set_fieldname_and_label(self):
+ if not self.label:
+ self.label = cstr(self.document_type)
+
+ if not self.fieldname:
+ self.fieldname = scrub(self.label)
+
+ def make_accounting_dimension_in_accounting_doctypes(self):
+ last_created_accounting_dimension = get_last_created_accounting_dimension()
+
+ doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "BOM", "Sales Order", "Purchase Order",
+ "Stock Entry", "Budget", "Payroll Entry", "Delivery Note"]
+
+ df = {
+ "fieldname": self.fieldname,
+ "label": self.label,
+ "fieldtype": "Link",
+ "options": self.document_type,
+ "insert_after": last_created_accounting_dimension if last_created_accounting_dimension else "project"
+ }
+
+ for doctype in doclist:
+
+ if doctype == "Budget":
+ df.update({
+ "depends_on": "eval:doc.budget_against == '{0}'".format(self.document_type)
+ })
+
+ create_custom_field(doctype, df)
+
+ property_setter = frappe.db.exists("Property Setter", "Budget-budget_against-options")
+
+ if property_setter:
+ else:
+ frappe.get_doc({
+ "doctype": "Property Setter",
+ "doc_type": "Budget",
+ "fieldname": "budget_against"
+ })
+ else:
+ create_custom_field(doctype, df)
+
+ def delete_accounting_dimension(self):
+ doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "BOM", "Sales Order", "Purchase Order",
+ "Stock Entry", "Budget", "Payroll Entry", "Delivery Note"]
+
+ frappe.db.sql("""
+ DELETE FROM `tabCustom Field`
+ WHERE fieldname = %s
+ AND dt IN (%s)""" %
+ ('%s', ', '.join(['%s']* len(doclist))), tuple([self.fieldname] + doclist))
+
+ frappe.db.sql("""
+ DELETE FROM `tabProperty Setter`
+ WHERE field_name = %s
+ AND doc_type IN (%s)""" %
+ ('%s', ', '.join(['%s']* len(doclist))), tuple([self.fieldname] + doclist))
+
+ for doc in doclist:
+ frappe.clear_cache(doctype=doc)
+
+
+def get_last_created_accounting_dimension():
+ last_created_accounting_dimension = frappe.db.sql("select fieldname, max(creation) from `tabAccounting Dimension`", as_dict=1)
+
+ if last_created_accounting_dimension[0]:
+ return last_created_accounting_dimension[0].fieldname
+
+def get_accounting_dimensions():
+ accounting_dimensions = frappe.get_all("Accounting Dimension", fields=["fieldname"])
+
+ return [d.fieldname for d in accounting_dimensions]
diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
new file mode 100644
index 0000000..b4368c4
--- /dev/null
+++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestAccountingDimension(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
index cd9f9d9..b2072f0 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
@@ -62,3 +62,11 @@
},
]
}
+
+let dimension_filters = erpnext.get_dimension_filters();
+
+dimension_filters.then((dimensions) => {
+ dimensions.forEach((dimension) => {
+ frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
+ });
+});
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index fe8de36..fb4f5d0 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -19,9 +19,12 @@
else:
cost_centers = get_cost_centers(filters)
+ print(cost_centers)
+
period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
cam_map = get_cost_center_account_month_map(filters)
+ print(cam_map)
data = []
for cost_center in cost_centers:
cost_center_items = cam_map.get(cost_center)
@@ -45,8 +48,8 @@
if(filters.get("show_cumulative")):
last_total = period_data[0] - period_data[1]
-
- period_data[2] = period_data[0] - period_data[1]
+
+ period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1]
if filters["period"] != "Yearly" :
@@ -56,7 +59,7 @@
return columns, data
def validate_filters(filters):
- if filters.get("budget_against")=="Project" and filters.get("cost_center"):
+ if filters.get("budget_against") != "Cost Center" and filters.get("cost_center"):
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
def get_columns(filters):
@@ -92,8 +95,11 @@
if filters.get("budget_against") == "Cost Center":
cond = "order by lft"
- return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
- {cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
+ if filters.get("budget_against") in ["Cost Center", "Project"]:
+ return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
+ {cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
+ else:
+ return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against")))
#Get cost center & target details
def get_cost_center_target_details(filters):
@@ -109,7 +115,7 @@
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
(filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
-
+
#Get target distribution details of accounts of cost center
def get_target_distribution_details(filters):
@@ -118,7 +124,7 @@
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
-
+
return target_details
#Get actual details from gl entry
@@ -129,7 +135,7 @@
if filters.get("budget_against") == "Cost Center":
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
-
+
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
@@ -153,6 +159,7 @@
def get_cost_center_account_month_map(filters):
import datetime
cost_center_target_details = get_cost_center_target_details(filters)
+ print(cost_center_target_details)
tdd = get_target_distribution_details(filters)
cam_map = {}
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 2140315..7f0cbaa 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -16,6 +16,7 @@
from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate)
from six import itervalues
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False,
company=None, reset_period_on_fy_change=True):
@@ -348,20 +349,23 @@
additional_conditions += " and account in ({})"\
.format(", ".join([frappe.db.escape(d) for d in accounts]))
+ gl_filters = {
+ "company": company,
+ "from_date": from_date,
+ "to_date": to_date,
+ }
+
+ for key, value in filters.items():
+ if value:
+ gl_filters.update({
+ key: value
+ })
+
gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
where company=%(company)s
{additional_conditions}
and posting_date <= %(to_date)s
- order by account, posting_date""".format(additional_conditions=additional_conditions),
- {
- "company": company,
- "from_date": from_date,
- "to_date": to_date,
- "cost_center": filters.cost_center,
- "project": filters.project,
- "finance_book": filters.get("finance_book")
- },
- as_dict=True)
+ order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True)
if filters and filters.get('presentation_currency'):
convert_to_presentation_currency(gl_entries, get_currency(filters))
@@ -375,6 +379,8 @@
def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions = []
+ accounting_dimensions = get_accounting_dimensions()
+
if ignore_closing_entries:
additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'")
@@ -395,6 +401,11 @@
if filters.get("finance_book"):
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
+ if accounting_dimensions:
+ for dimension in accounting_dimensions:
+ if filters.get(dimension):
+ additional_conditions.append("{0} in (%({0})s)".format(dimension))
+
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
def get_cost_centers_with_children(cost_centers):
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 2826760..481107e 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -214,3 +214,17 @@
}
]
}
+
+let dimension_filters = erpnext.get_dimension_filters();
+
+dimension_filters.then((dimensions) => {
+ dimensions.forEach((dimension) => {
+ frappe.query_reports["General Ledger"].filters.push({
+ "fieldname": dimension["fieldname"],
+ "label": __(dimension["label"]),
+ "fieldtype": "Link",
+ "options": dimension["document_type"]
+ });
+ });
+});
+
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 44ca8d3..033b9ba 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -10,6 +10,7 @@
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
from six import iteritems
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
def execute(filters=None):
if not filters:
@@ -195,6 +196,13 @@
if match_conditions:
conditions.append(match_conditions)
+ accounting_dimensions = get_accounting_dimensions()
+
+ if accounting_dimensions:
+ for dimension in accounting_dimensions:
+ if filters.get(dimension):
+ conditions.append("{0} in (%({0})s)".format(dimension))
+
return "and {}".format(" and ".join(conditions)) if conditions else ""
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 0672b2d..fda1a40 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -16,6 +16,7 @@
from erpnext.accounts.doctype.pricing_rule.utils import validate_pricing_rules
from erpnext.exceptions import InvalidCurrency
from six import text_type
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
@@ -365,6 +366,14 @@
'party': None,
'project': self.get("project")
})
+
+ accounting_dimensions = get_accounting_dimensions()
+ dimension_dict = frappe._dict()
+
+ for dimension in accounting_dimensions:
+ dimension_dict[dimension] = self.get(dimension)
+
+ gl_dict.update(dimension_dict)
gl_dict.update(args)
if not account_currency:
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index 36746cd..2e4b8f2 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -63,7 +63,7 @@
};
function get_filters(){
- return [
+ let filters = [
{
"fieldname":"company",
"label": __("Company"),
@@ -149,4 +149,21 @@
"options": erpnext.get_presentation_currency_list()
}
]
+
+ let dimension_filters = erpnext.get_dimension_filters()
+
+ dimension_filters.then((dimensions) => {
+ dimensions.forEach((dimension) => {
+ filters.push({
+ "fieldname": dimension["fieldname"],
+ "label": __(dimension["label"]),
+ "fieldtype": "Link",
+ "options": dimension["document_type"]
+ });
+ });
+ });
+
+ return filters;
}
+
+
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 6860d6a..540b5ea 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -62,6 +62,14 @@
$btn.on("click", function() {
me.show_serial_batch_selector(grid_row.frm, grid_row.doc);
});
+ },
+
+ get_dimension_filters: async function() {
+ let dimensions = await frappe.db.get_list('Accounting Dimension', {
+ fields: ['label', 'fieldname', 'document_type'],
+ });
+
+ return dimensions;
}
});