diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 718ba311692..f5000f44ae3 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -179,9 +179,12 @@ class Account(Document): self.warehouse = None def validate_warehouse(self, warehouse): - if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}): + lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) + + if frappe.db.sql_list("""select sle.name from `tabStock Ledger Entry` sle where exists (select wh.name from + tabWarehouse wh where lft >= %s and rgt <= %s and sle.warehouse = wh.warehouse)""", (lft, rgt)): throw(_("Stock entries exist against warehouse {0}, hence you cannot re-assign or modify Warehouse").format(warehouse)) - + def update_nsm_model(self): """update lft, rgt indices for nested set model""" import frappe diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index 3ee1c42fcb2..3252788fa1b 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -3,7 +3,7 @@ frappe.treeview_settings["Account"] = { title: __("Chart Of Accounts"), get_tree_root: false, filters: [{ - fieldname: "comp", + fieldname: "company", fieldtype:"Select", options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), label: __("Company"), diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js index 3ca5232e3f1..ac82f23cb46 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js +++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js @@ -2,7 +2,7 @@ frappe.treeview_settings["Cost Center"] = { breadcrumbs: "Accounts", get_tree_root: false, filters: [{ - fieldname: "comp", + fieldname: "company", fieldtype:"Select", options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), label: __("Company"), diff --git a/erpnext/accounts/doctype/payment_tool/payment_tool.py b/erpnext/accounts/doctype/payment_tool/payment_tool.py index 7f95ade8776..5c5b393963a 100644 --- a/erpnext/accounts/doctype/payment_tool/payment_tool.py +++ b/erpnext/accounts/doctype/payment_tool/payment_tool.py @@ -7,7 +7,7 @@ from frappe import _, scrub from frappe.utils import flt from frappe.model.document import Document import json -from erpnext.accounts.doctype.account.account import get_account_currency +from erpnext.accounts.utils import get_account_currency from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate class PaymentTool(Document): diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 78e07619b6e..5cccf1bf480 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -9,6 +9,7 @@ from frappe import throw, _ from frappe.utils import formatdate # imported to enable erpnext.accounts.utils.get_account_currency +from erpnext.accounts.doctype.account.account import get_account_currency import frappe.defaults from erpnext.accounts.report.financial_statements import sort_root_accounts @@ -128,7 +129,7 @@ def add_ac(args=None): if not args: args = frappe.local.form_dict args.pop("cmd") - + ac = frappe.new_doc("Account") if args.get("ignore_permissions"): @@ -139,7 +140,7 @@ def add_ac(args=None): if not ac.parent_account: ac.parent_account = args.get("parent") - + ac.old_parent = "" ac.freeze_account = "No" if cint(ac.get("is_root")): @@ -447,7 +448,7 @@ def get_companies(): @frappe.whitelist() def get_children(): args = frappe.local.form_dict - ctype, company = args['ctype'], args['comp'] + ctype, company = args['ctype'], args['company'] fieldname = frappe.db.escape(ctype.lower().replace(' ','_')) doctype = frappe.db.escape(ctype) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ac58e9c4b12..e3a66a4b11b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -6,8 +6,7 @@ import frappe from frappe import _, throw from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate from erpnext.setup.utils import get_company_currency, get_exchange_rate -from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year -from erpnext.accounts.doctype.account.account import get_account_currency +from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency from erpnext.utilities.transaction_base import TransactionBase from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.sales_and_purchase_return import validate_return diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 1976cc6c562..1e685e5ca1f 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -321,6 +321,7 @@ def get_warehouse_account(): warehouse_account = frappe._dict() for d in frappe.db.sql("""select warehouse, name, account_currency from tabAccount - where account_type = 'Warehouse' and (warehouse is not null and warehouse != '')""", as_dict=1): + where account_type = 'Warehouse' and (warehouse is not null and warehouse != '' + and is_group != 1)""", as_dict=1): warehouse_account.setdefault(d.warehouse, d) return warehouse_account diff --git a/erpnext/patches/v7_0/group_warehouses.py b/erpnext/patches/v7_0/group_warehouses.py new file mode 100644 index 00000000000..8987d58561d --- /dev/null +++ b/erpnext/patches/v7_0/group_warehouses.py @@ -0,0 +1,45 @@ +import frappe +from frappe import _ + +def execute(): + frappe.reload_doc("stock", "doctype", "warehouse") + for company in frappe.get_all("Company", fields=["name", "abbr"]): + if not frappe.db.get_value("Warehouse", "{0} - {1}".format(_("All Warehouses"), company.abbr)): + create_default_warehouse_group(company) + + for warehouse in frappe.get_all("Warehouse", {"company": company}, ["name", "create_account_under", "parent_warehouse"]): + set_parent_to_warehouses(warehouse, company) + set_parent_to_warehouse_acounts(warehouse, company) + +def set_parent_to_warehouses(warehouse, company): + warehouse = frappe.get_doc("Warehouse", warehouse.name) + warehouse.is_group = "No" + + if not warehouse.parent_warehouse: + warehouse.parent_warehouse = "{0} - {1}".format(_("All Warehouses"), company.abbr) + + warehouse.save() + +def set_parent_to_warehouse_acounts(warehouse, company): + account = frappe.db.get_value("Account", {"warehouse": warehouse.name}) + stock_group = frappe.db.get_value("Account", {"account_type": "Stock", + "is_group": 1, "company": company.name}) + + if account: + account = frappe.get_doc("Account", account) + + if warehouse.create_account_under == stock_group or not warehouse.create_account_under: + if not warehouse.parent_warehouse: + account.parent_account = "{0} - {1}".format(_("All Warehouses"), company.abbr) + else: + account.parent_account = frappe.db.get_value("Account", {"warehouse": warehouse.parent_warehouse}) + account.save(ignore_permissions=True) + +def create_default_warehouse_group(company): + frappe.get_doc({ + "doctype": "Warehouse", + "warehouse_name": _("All Warehouses"), + "is_group": "Yes", + "company": company.name, + "parent_warehouse": "" + }).insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index c45c2411dfa..7d8829731dd 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -88,6 +88,7 @@ class Company(Document): def create_default_warehouses(self): for wh_detail in [ + {"warehouse_name": _("All Warehouses"), "is_group": "Yes"}, {"warehouse_name": _("Stores"), "is_group": "No"}, {"warehouse_name": _("Work In Progress"), "is_group": "No"}, {"warehouse_name": _("Finished Goods"), "is_group": "No"}]: @@ -101,7 +102,8 @@ class Company(Document): "warehouse_name": wh_detail["warehouse_name"], "is_group": wh_detail["is_group"], "company": self.name, - "parent_warehouse": "", + "parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr) \ + if wh_detail["is_group"] == "No" else "", "create_account_under": stock_group }) warehouse.flags.ignore_permissions = True diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index c03ceee69ca..2378e3f3e69 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -18,7 +18,7 @@ class Bin(Document): self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty) - self.validate_leaf_warehouse() + self.block_transactions_against_group_warehouse() def validate_mandatory(self): qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty'] @@ -26,11 +26,9 @@ class Bin(Document): if (not getattr(self, f, None)) or (not self.get(f)): self.set(f, 0.0) - def validate_leaf_warehouse(self): - from erpnext.stock.utils import is_leaf_warehouse - - if not is_leaf_warehouse(self.warehouse): - frappe.throw(_("Group node warehouse is not allowed to select for transactions")) + def block_transactions_against_group_warehouse(self): + from erpnext.stock.utils import is_group_warehouse + is_group_warehouse(self.warehouse) def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False): self.update_qty(args) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 1f446d2be05..2caabee7b4e 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -25,7 +25,7 @@ class StockLedgerEntry(Document): validate_warehouse_company(self.warehouse, self.company) self.scrub_posting_time() self.validate_and_set_fiscal_year() - self.validate_leaf_warehouse() + self.block_transactions_against_group_warehouse() from erpnext.accounts.utils import validate_fiscal_year validate_fiscal_year(self.posting_date, self.fiscal_year, self.meta.get_label("posting_date"), self) @@ -118,11 +118,9 @@ class StockLedgerEntry(Document): if not self.fiscal_year: self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0] - def validate_leaf_warehouse(self): - from erpnext.stock.utils import is_leaf_warehouse - - if not is_leaf_warehouse(self.warehouse): - frappe.throw(_("Group node warehouse is not allowed to select for transactions")) + def block_transactions_against_group_warehouse(self): + from erpnext.stock.utils import is_group_warehouse + is_group_warehouse(self.warehouse) def on_doctype_update(): if not frappe.db.sql("""show index from `tabStock Ledger Entry` diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index a0b7296056c..58f7f603ef2 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -57,8 +57,9 @@ class Warehouse(NestedSet): ac_doc = frappe.get_doc({ "doctype": "Account", 'account_name': self.warehouse_name, - 'parent_account': self.create_account_under, - 'is_group':0, + 'parent_account': self.parent_warehouse if self.parent_warehouse \ + else self.create_account_under, + 'is_group': 1 if self.is_group=="Yes" else 0 , 'company':self.company, "account_type": "Warehouse", "warehouse": self.name, @@ -77,6 +78,7 @@ class Warehouse(NestedSet): {"account_name": "Stock Assets", "company": self.company}) if parent_account: + frappe.db.set_value("Warehouse", self.name, "create_account_under", parent_account) self.create_account_under = parent_account else: frappe.throw(_("Please enter parent account group for warehouse {0}").format(self.name)) @@ -105,6 +107,11 @@ class Warehouse(NestedSet): if frappe.db.sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.name): throw(_("Warehouse can not be deleted as stock ledger entry exists for this warehouse.")) + + if frappe.db.sql("""select name from `tabWarehouse` where parent_warehouse = %s""", self.name): + throw(_("Child warehouse exists for this warehouse. You can not delete this warehouse.")) + + self.update_nsm_model() def before_rename(self, olddn, newdn, merge=False): # Add company abbr if not provided @@ -170,7 +177,7 @@ class Warehouse(NestedSet): def get_children(): from erpnext.stock.utils import get_stock_value_on ctype = frappe.local.form_dict.get('ctype') - company = frappe.local.form_dict.get('comp') + company = frappe.local.form_dict.get('company') parent_field = 'parent_' + ctype.lower().replace(' ', '_') parent = frappe.form_dict.get("parent") or "" @@ -194,6 +201,7 @@ def get_children(): @frappe.whitelist() def add_node(): ctype = frappe.form_dict.get('ctype') + company = frappe.form_dict.get('company') parent_field = 'parent_' + ctype.lower().replace(' ', '_') name_field = ctype.lower().replace(' ', '_') + '_name' @@ -207,7 +215,8 @@ def add_node(): doc.update({ name_field: frappe.form_dict['name_field'], parent_field: parent, - "is_group": frappe.form_dict['is_group'] + "is_group": frappe.form_dict['is_group'], + "company": company }) doc.save() diff --git a/erpnext/stock/doctype/warehouse/warehouse_tree.js b/erpnext/stock/doctype/warehouse/warehouse_tree.js index d23a5362db9..03614931a5c 100644 --- a/erpnext/stock/doctype/warehouse/warehouse_tree.js +++ b/erpnext/stock/doctype/warehouse/warehouse_tree.js @@ -4,7 +4,7 @@ frappe.treeview_settings['Warehouse'] = { get_tree_root: false, root_label: "Warehouses", filters: [{ - fieldname: "comp", + fieldname: "company", fieldtype:"Select", options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), label: __("Company"), diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 75200b340dd..696f2b0059f 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -71,9 +71,9 @@ def get_conditions(filters): conditions += " and item_code = '%s'" % frappe.db.escape(filters.get("item_code"), percent=False) if filters.get("warehouse"): - wh = frappe.get_doc("Warehouse", filters.get("warehouse")) - conditions += "and warehouse in (\ - select name from `tabWarehouse` wh where wh.lft >= %s and wh.rgt <= %s)"%(wh.lft, wh.rgt) + lft, rgt = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"]) + conditions += " and exists (select name from `tabWarehouse` wh \ + where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(lft, rgt) return conditions @@ -81,9 +81,9 @@ def get_stock_ledger_entries(filters): conditions = get_conditions(filters) return frappe.db.sql("""select item_code, warehouse, posting_date, actual_qty, valuation_rate, company, voucher_type, qty_after_transaction, stock_value_difference - from `tabStock Ledger Entry` force index (posting_sort_index) + from `tabStock Ledger Entry` sle force index (posting_sort_index) where docstatus < 2 %s order by posting_date, posting_time, name""" % - conditions, as_dict=1) + conditions, as_dict=1, debug=1) def get_item_warehouse_map(filters): iwb_map = {} diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index a65be6d8e75..b2e46701da6 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -41,7 +41,7 @@ def get_stock_ledger_entries(filters): return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date, item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate, stock_value, voucher_type, voucher_no, batch_no, serial_no, company - from `tabStock Ledger Entry` + from `tabStock Ledger Entry` sle where company = %(company)s and posting_date between %(from_date)s and %(to_date)s {sle_conditions} @@ -99,6 +99,8 @@ def get_opening_balance(filters, columns): return row def get_warehouse_condition(warehouse): - wh = frappe.get_doc("Warehouse", warehouse) - return " warehouse in (select name from `tabWarehouse` wh\ - where wh.lft >= %s and wh.rgt <= %s)"%(wh.lft, wh.rgt) \ No newline at end of file + lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) + + return " exists (select name from `tabWarehouse` wh \ + where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(lft, rgt) + \ No newline at end of file diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 2fb120a180a..409833a9391 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -63,9 +63,10 @@ def get_bin_list(filters): conditions.append("item_code = '%s' "%filters.item_code) if filters.warehouse: - wh = frappe.get_doc("Warehouse", filters.warehouse) - conditions.append(" warehouse in (select name from `tabWarehouse` wh\ - where wh.lft >= %s and wh.rgt <= %s)"%(wh.lft, wh.rgt)) + lft, rgt = frappe.db.get_value("Warehouse", filters.warehouse, ["lft", "rgt"]) + + conditions.append(" exists (select name from `tabWarehouse` wh \ + where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)"%(lft, rgt)) bin_list = frappe.db.sql("""select item_code, warehouse, actual_qty, planned_qty, indented_qty, ordered_qty, reserved_qty, reserved_qty_for_production, projected_qty @@ -104,6 +105,4 @@ def get_item_map(item_code): item["reorder_levels"] = reorder_levels.get(item.name) or [] item_map[item.name] = item - - frappe.errprint(item_map) return item_map diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 6e56c5c7e79..51d82a6dcc1 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -16,10 +16,10 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): if warehouse: - wh = frappe.get_doc("Warehouse", warehouse) + lft, rgt, is_group = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt", "is_group"]) - if wh.is_group == "Yes": - values.extend([wh.lft, wh.rgt]) + if is_group == "Yes": + values.extend([lft, rgt]) condition += "and exists (\ select name from `tabWarehouse` wh where wh.name = sle.warehouse\ and wh.lft >= %s and wh.rgt <= %s)" @@ -188,8 +188,7 @@ def validate_warehouse_company(warehouse, company): frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company), InvalidWarehouseCompany) -def is_leaf_warehouse(warehouse): - if frappe.db.get_value("Warehouse", warehouse, "is_group") == "No": - return True - return False +def is_group_warehouse(warehouse): + if frappe.db.get_value("Warehouse", warehouse, "is_group") == "Yes": + frappe.throw(_("Group node warehouse is not allowed to select for transactions")) \ No newline at end of file