Merge pull request #5518 from saurabh6790/warehouse_tree
Warehouse tree
This commit is contained in:
		
						commit
						7b6cdf4a63
					
				| @ -1,7 +1,7 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| cur_frm.list_route = "Accounts Browser/Account"; | ||||
| cur_frm.list_route = "Tree/Account"; | ||||
| 
 | ||||
| cur_frm.cscript.refresh = function(doc, cdt, cdn) { | ||||
| 	if(doc.__islocal) { | ||||
| @ -48,7 +48,7 @@ cur_frm.cscript.account_type = function(doc, cdt, cdn) { | ||||
| 
 | ||||
| cur_frm.cscript.add_toolbar_buttons = function(doc) { | ||||
| 	cur_frm.add_custom_button(__('Chart of Accounts'), | ||||
| 		function() { frappe.set_route("Accounts Browser", "Account"); }, __("View")) | ||||
| 		function() { frappe.set_route("Tree", "Account"); }, __("View")) | ||||
| 
 | ||||
| 	if (doc.is_group == 1) { | ||||
| 		cur_frm.add_custom_button(__('Group to Non-Group'), | ||||
|  | ||||
| @ -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.name)""", (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 | ||||
|  | ||||
							
								
								
									
										52
									
								
								erpnext/accounts/doctype/account/account_tree.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								erpnext/accounts/doctype/account/account_tree.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| frappe.treeview_settings["Account"] = { | ||||
| 	breadcrumbs: "Accounts", | ||||
| 	title: __("Chart Of Accounts"), | ||||
| 	get_tree_root: false, | ||||
| 	filters: [{ | ||||
| 		fieldname: "company", | ||||
| 		fieldtype:"Select", | ||||
| 		options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), | ||||
| 		label: __("Company"), | ||||
| 		default: frappe.defaults.get_default('company') ? frappe.defaults.get_default('company'): "" | ||||
| 	}], | ||||
| 	root_label: "Accounts", | ||||
| 	get_tree_nodes: 'erpnext.accounts.utils.get_children', | ||||
| 	add_tree_node: 'erpnext.accounts.utils.add_ac', | ||||
| 	menu_items:[ | ||||
| 		{ | ||||
| 			label: __('New Company'), | ||||
| 			action: function() { newdoc('Company'); }, | ||||
| 			condition: 'frappe.boot.user.can_create.indexOf("Company") === -1' | ||||
| 		} | ||||
| 	], | ||||
| 	fields: [ | ||||
| 		{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true, | ||||
| 			description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")}, | ||||
| 		{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), | ||||
| 			description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')}, | ||||
| 		{fieldtype:'Select', fieldname:'root_type', label:__('Root Type'), | ||||
| 			options: ['Asset', 'Liability', 'Equity', 'Income', 'Expense'].join('\n')}, | ||||
| 		{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'), | ||||
| 			options: ['', 'Bank', 'Cash', 'Warehouse', 'Tax', 'Chargeable'].join('\n'), | ||||
| 			description: __("Optional. This setting will be used to filter in various transactions."), | ||||
| 			depends_on: 'eval:doc.is_group==1'}, | ||||
| 		{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'), | ||||
| 			depends_on: 'eval:doc.is_group==1&&doc.account_type=="Tax"'}, | ||||
| 		{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse", | ||||
| 			depends_on: 'eval:(doc.is_group==1&&doc.account_type=="Warehouse")'}, | ||||
| 		{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency", | ||||
| 			description: __("Optional. Sets company's default currency, if not specified.")} | ||||
| 	], | ||||
| 	onrender: function(node) { | ||||
| 		var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; | ||||
| 		if (node.data && node.data.balance!==undefined) { | ||||
| 			$('<span class="balance-area pull-right text-muted small">' | ||||
| 				+ (node.data.balance_in_account_currency ? | ||||
| 					(format_currency(Math.abs(node.data.balance_in_account_currency), | ||||
| 						node.data.account_currency) + " / ") : "") | ||||
| 				+ format_currency(Math.abs(node.data.balance), node.data.company_currency) | ||||
| 				+ " " + dr_or_cr | ||||
| 				+ '</span>').insertBefore(node.$ul); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -3,7 +3,7 @@ | ||||
| 
 | ||||
| frappe.provide("erpnext.accounts"); | ||||
| 
 | ||||
| cur_frm.list_route = "Accounts Browser/Cost Center"; | ||||
| cur_frm.list_route = "Tree/Cost Center"; | ||||
| 
 | ||||
| 
 | ||||
| frappe.ui.form.on('Cost Center', { | ||||
| @ -34,7 +34,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { | ||||
| 	cur_frm.set_intro(intro_txt); | ||||
| 
 | ||||
| 	cur_frm.add_custom_button(__('Chart of Cost Centers'), | ||||
| 		function() { frappe.set_route("Accounts Browser", "Cost Center"); }, __("View")) | ||||
| 		function() { frappe.set_route("Tree", "Cost Center"); }, __("View")) | ||||
| } | ||||
| 
 | ||||
| cur_frm.cscript.parent_cost_center = function(doc, cdt, cdn) { | ||||
|  | ||||
							
								
								
									
										26
									
								
								erpnext/accounts/doctype/cost_center/cost_center_tree.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								erpnext/accounts/doctype/cost_center/cost_center_tree.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| frappe.treeview_settings["Cost Center"] = { | ||||
| 	breadcrumbs: "Accounts", | ||||
| 	get_tree_root: false, | ||||
| 	filters: [{ | ||||
| 		fieldname: "company", | ||||
| 		fieldtype:"Select", | ||||
| 		options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), | ||||
| 		label: __("Company"), | ||||
| 		default: frappe.defaults.get_default('company') ? frappe.defaults.get_default('company'): "" | ||||
| 	}], | ||||
| 	root_label: "Cost Centers", | ||||
| 	get_tree_nodes: 'erpnext.accounts.utils.get_children', | ||||
| 	add_tree_node: 'erpnext.accounts.utils.add_cc', | ||||
| 	menu_items:[ | ||||
| 		{ | ||||
| 			label: __('New Company'), | ||||
| 			action: function() { newdoc('Company'); }, | ||||
| 			condition: 'frappe.boot.user.can_create.indexOf("Company") === -1' | ||||
| 		} | ||||
| 	], | ||||
| 	fields:[ | ||||
| 		{fieldtype:'Data', fieldname:'cost_center_name', label:__('New Cost Center Name'), reqd:true}, | ||||
| 		{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), | ||||
| 			description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')} | ||||
| 	] | ||||
| } | ||||
| @ -1 +0,0 @@ | ||||
| Tree view browser for Chart of Accounts and Chart of Cost Centers | ||||
| @ -1 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| @ -1,334 +0,0 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| // tree of chart of accounts / cost centers
 | ||||
| // multiple companies
 | ||||
| // add node
 | ||||
| // edit node
 | ||||
| // see ledger
 | ||||
| 
 | ||||
| frappe.pages["Accounts Browser"].on_page_load  = function(wrapper){ | ||||
| 	frappe.ui.make_app_page({ | ||||
| 		parent: wrapper, | ||||
| 		single_column: true | ||||
| 	}) | ||||
| 
 | ||||
| 	frappe.breadcrumbs.add("Accounts"); | ||||
| 
 | ||||
| 	var main = wrapper.page.main, | ||||
| 		chart_area = $("<div>") | ||||
| 			.css({"margin-bottom": "15px", "min-height": "200px"}) | ||||
| 			.appendTo(main), | ||||
| 		help_area = $('<hr><div style="padding: 0px 15px;">'+ | ||||
| 		'<h4>'+__('Quick Help')+'</h4>'+ | ||||
| 		'<ol>'+ | ||||
| 			'<li>'+__('To add child nodes, explore tree and click on the node under which you want to add more nodes.')+'</li>'+ | ||||
| 			'<li>'+ | ||||
| 			      __('Accounting Entries can be made against leaf nodes. Entries against Groups are not allowed.')+ | ||||
| 		    '</li>'+ | ||||
| 			'<li>'+__('Please do NOT create Accounts for Customers and Suppliers. They are created directly from the Customer / Supplier masters.')+'</li>'+ | ||||
| 			'<li>'+ | ||||
| 			     '<b>'+__('To create a Bank Account')+'</b>: '+ | ||||
| 			      __('Go to the appropriate group (usually Application of Funds > Current Assets > Bank Accounts and create a new Account (by clicking on Add Child) of type "Bank"')+ | ||||
| 			'</li>'+ | ||||
| 			'<li>'+ | ||||
| 			      '<b>'+__('To create a Tax Account') +'</b>: '+ | ||||
| 			      __('Go to the appropriate group (usually Source of Funds > Current Liabilities > Taxes and Duties and create a new Account (by clicking on Add Child) of type "Tax" and do mention the Tax rate.')+ | ||||
| 			'</li>'+ | ||||
| 		'</ol>'+ | ||||
| 		'<p>'+__('Please setup your chart of accounts before you start Accounting Entries')+'</p></div>').appendTo(main); | ||||
| 
 | ||||
| 	if (frappe.boot.user.can_create.indexOf("Company") !== -1) { | ||||
| 		wrapper.page.add_menu_item(__('New Company'), function() { newdoc('Company'); }, true); | ||||
| 	} | ||||
| 
 | ||||
| 	wrapper.page.add_menu_item(__('Refresh'), function() { | ||||
| 			wrapper.$company_select.change(); | ||||
| 		}); | ||||
| 
 | ||||
| 	wrapper.page.set_primary_action(__('New'), function() { | ||||
| 		erpnext.account_chart && erpnext.account_chart.make_new(); | ||||
| 	}, "octicon octicon-plus"); | ||||
| 
 | ||||
| 	var company_list = $.map(locals[':Company'], function(c) { return c.name; }).sort(); | ||||
| 
 | ||||
| 	// company-select
 | ||||
| 	wrapper.$company_select = wrapper.page.add_select("Company", company_list) | ||||
| 		.change(function() { | ||||
| 			var ctype = frappe.get_route()[1] || 'Account'; | ||||
| 			erpnext.account_chart = new erpnext.AccountsChart(ctype, $(this).val(), | ||||
| 				chart_area.get(0), wrapper.page); | ||||
| 		}) | ||||
| 
 | ||||
| 	if(frappe.defaults.get_default('company')) { | ||||
| 		wrapper.$company_select.val(frappe.defaults.get_default('company')); | ||||
| 	} | ||||
| 	wrapper.$company_select.change(); | ||||
| } | ||||
| 
 | ||||
| frappe.pages["Accounts Browser"].on_page_show = function(wrapper){ | ||||
| 	// set route
 | ||||
| 	var ctype = frappe.get_route()[1] || 'Account'; | ||||
| 
 | ||||
| 	if(frappe.route_options) { | ||||
| 		if(frappe.route_options.company) { | ||||
| 			wrapper.$company_select.val(frappe.route_options.company).change(); | ||||
| 		} | ||||
| 		frappe.route_options = null; | ||||
| 	} else if(erpnext.account_chart && erpnext.account_chart.ctype != ctype) { | ||||
| 		wrapper.$company_select.change(); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| erpnext.AccountsChart = Class.extend({ | ||||
| 	init: function(ctype, company, wrapper, page) { | ||||
| 		$(wrapper).empty(); | ||||
| 		var me = this; | ||||
| 		me.ctype = ctype; | ||||
| 		me.can_create = frappe.model.can_create(this.ctype); | ||||
| 		me.can_delete = frappe.model.can_delete(this.ctype); | ||||
| 		me.can_write = frappe.model.can_write(this.ctype); | ||||
| 		me.page = page; | ||||
| 		me.set_title(); | ||||
| 
 | ||||
| 		// __("Accounts"), __("Cost Centers")
 | ||||
| 
 | ||||
| 		me.company = company; | ||||
| 		this.tree = new frappe.ui.Tree({ | ||||
| 			parent: $(wrapper), | ||||
| 			label: ctype==="Account" ? "Accounts" : "Cost Centers", | ||||
| 			args: {ctype: ctype, comp: company}, | ||||
| 			method: 'erpnext.accounts.page.accounts_browser.accounts_browser.get_children', | ||||
| 			click: function(link) { | ||||
| 				// bold
 | ||||
| 				$('.bold').removeClass('bold'); // deselect
 | ||||
| 				$(link).parent().find('.balance-area:first').addClass('bold'); // select
 | ||||
| 
 | ||||
| 			}, | ||||
| 			toolbar: [ | ||||
| 				{ toggle_btn: true }, | ||||
| 				{ | ||||
| 					label: __("Open"), | ||||
| 					condition: function(node) { return !node.root }, | ||||
| 					click: function(node, btn) { | ||||
| 						 frappe.set_route("Form", me.ctype, node.label); | ||||
| 					} | ||||
| 				}, | ||||
| 				{ | ||||
| 					condition: function(node) { return node.expandable; }, | ||||
| 					label: __("Add Child"), | ||||
| 					click: function() { | ||||
| 						me.make_new() | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				}, | ||||
| 				{ | ||||
| 					condition: function(node) { | ||||
| 						return !node.root && me.ctype === 'Account' | ||||
| 							&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1 | ||||
| 					}, | ||||
| 					label: __("View Ledger"), | ||||
| 					click: function(node, btn) { | ||||
| 						frappe.route_options = { | ||||
| 							"account": node.label, | ||||
| 							"from_date": sys_defaults.year_start_date, | ||||
| 							"to_date": sys_defaults.year_end_date, | ||||
| 							"company": me.company | ||||
| 						}; | ||||
| 						frappe.set_route("query-report", "General Ledger"); | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				}, | ||||
| 				{ | ||||
| 					condition: function(node) { return !node.root && me.can_write }, | ||||
| 					label: __("Rename"), | ||||
| 					click: function(node) { | ||||
| 						frappe.model.rename_doc(me.ctype, node.label, function(new_name) { | ||||
| 							node.reload_parent(); | ||||
| 						}); | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				}, | ||||
| 				{ | ||||
| 					condition: function(node) { return !node.root && me.can_delete }, | ||||
| 					label: __("Delete"), | ||||
| 					click: function(node) { | ||||
| 						frappe.model.delete_doc(me.ctype, node.label, function() { | ||||
| 							node.parent.remove(); | ||||
| 						}); | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				} | ||||
| 			], | ||||
| 			onrender: function(node) { | ||||
| 				var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; | ||||
| 				if (me.ctype == 'Account' && node.data && node.data.balance!==undefined) { | ||||
| 					$('<span class="balance-area pull-right text-muted small">' | ||||
| 						+ (node.data.balance_in_account_currency ? | ||||
| 							(format_currency(Math.abs(node.data.balance_in_account_currency), | ||||
| 								node.data.account_currency) + " / ") : "") | ||||
| 						+ format_currency(Math.abs(node.data.balance), node.data.company_currency) | ||||
| 						+ " " + dr_or_cr | ||||
| 						+ '</span>').insertBefore(node.$ul); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	}, | ||||
| 	set_title: function(val) { | ||||
| 		var chart_str = this.ctype=="Account" ? __("Chart of Accounts") : __("Chart of Cost Centers"); | ||||
| 		if(val) { | ||||
| 			this.page.set_title(chart_str + " - " + cstr(val)); | ||||
| 		} else { | ||||
| 			this.page.set_title(chart_str); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	make_new: function() { | ||||
| 		if(this.ctype=='Account') { | ||||
| 			this.new_account(); | ||||
| 		} else { | ||||
| 			this.new_cost_center(); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	new_account: function() { | ||||
| 		var me = this; | ||||
| 
 | ||||
| 		var node = me.tree.get_selected_node(); | ||||
| 
 | ||||
| 		if(!(node && node.expandable)) { | ||||
| 			frappe.msgprint(__("Select a group node first.")); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		// the dialog
 | ||||
| 		var d = new frappe.ui.Dialog({ | ||||
| 			title:__('New Account'), | ||||
| 			fields: [ | ||||
| 				{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true, | ||||
| 					description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")}, | ||||
| 				{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), | ||||
| 					description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')}, | ||||
| 				{fieldtype:'Select', fieldname:'root_type', label:__('Root Type'), | ||||
| 					options: ['Asset', 'Liability', 'Equity', 'Income', 'Expense'].join('\n'), | ||||
| 				}, | ||||
| 				{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'), | ||||
| 					options: ['', 'Bank', 'Cash', 'Warehouse', 'Tax', 'Chargeable'].join('\n'), | ||||
| 					description: __("Optional. This setting will be used to filter in various transactions.") }, | ||||
| 				{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')}, | ||||
| 				{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"}, | ||||
| 				{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency", | ||||
| 					description: __("Optional. Sets company's default currency, if not specified.")} | ||||
| 			] | ||||
| 		}) | ||||
| 
 | ||||
| 		var fd = d.fields_dict; | ||||
| 
 | ||||
| 		// account type if ledger
 | ||||
| 		$(fd.is_group.input).change(function() { | ||||
| 			if($(this).prop("checked")) { | ||||
| 				$(fd.account_type.wrapper).toggle(false); | ||||
| 				$(fd.tax_rate.wrapper).toggle(false); | ||||
| 				$(fd.warehouse.wrapper).toggle(false); | ||||
| 			} else { | ||||
| 				$(fd.account_type.wrapper).toggle(node.root ? false : true); | ||||
| 				fd.account_type.$input.trigger("change"); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		// tax rate if tax
 | ||||
| 		$(fd.account_type.input).change(function() { | ||||
| 			$(fd.tax_rate.wrapper).toggle(fd.account_type.get_value()==='Tax'); | ||||
| 			$(fd.warehouse.wrapper).toggle(fd.account_type.get_value()==='Warehouse'); | ||||
| 		}) | ||||
| 
 | ||||
| 		// create
 | ||||
| 		d.set_primary_action(__("Create New"), function() { | ||||
| 			var btn = this; | ||||
| 			var v = d.get_values(); | ||||
| 			if(!v) return; | ||||
| 
 | ||||
| 			if(v.account_type==="Warehouse" && !v.warehouse) { | ||||
| 				msgprint(__("Warehouse is required")); | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			var node = me.tree.get_selected_node(); | ||||
| 			v.parent_account = node.label; | ||||
| 			v.company = me.company; | ||||
| 
 | ||||
| 			if(node.root) { | ||||
| 				v.is_root = 1; | ||||
| 				v.parent_account = null; | ||||
| 			} else { | ||||
| 				v.is_root = 0; | ||||
| 				v.root_type = null; | ||||
| 			} | ||||
| 
 | ||||
| 			return frappe.call({ | ||||
| 				args: v, | ||||
| 				method: 'erpnext.accounts.utils.add_ac', | ||||
| 				callback: function(r) { | ||||
| 					d.hide(); | ||||
| 					if(node.expanded) { | ||||
| 						node.toggle_node(); | ||||
| 					} | ||||
| 					node.load(); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 
 | ||||
| 		// show
 | ||||
| 		d.on_page_show = function() { | ||||
| 			$(fd.is_group.input).change(); | ||||
| 			$(fd.account_type.input).change(); | ||||
| 		} | ||||
| 
 | ||||
| 		$(fd.is_group.input).prop("checked", false).change(); | ||||
| 		 | ||||
| 		// In case of root, show root type and hide account_type, is_group
 | ||||
| 		$(fd.root_type.wrapper).toggle(node.root); | ||||
| 		$(fd.is_group.wrapper).toggle(!node.root); | ||||
| 		 | ||||
| 		d.show(); | ||||
| 	}, | ||||
| 
 | ||||
| 	new_cost_center: function(){ | ||||
| 		var me = this; | ||||
| 		// the dialog
 | ||||
| 		var d = new frappe.ui.Dialog({ | ||||
| 			title:__('New Cost Center'), | ||||
| 			fields: [ | ||||
| 				{fieldtype:'Data', fieldname:'cost_center_name', label:__('New Cost Center Name'), reqd:true}, | ||||
| 				{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), | ||||
| 					description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')}, | ||||
| 				{fieldtype:'Button', fieldname:'create_new', label:__('Create New') } | ||||
| 			] | ||||
| 		}); | ||||
| 
 | ||||
| 		// create
 | ||||
| 		$(d.fields_dict.create_new.input).click(function() { | ||||
| 			var v = d.get_values(); | ||||
| 			if(!v) return; | ||||
| 
 | ||||
| 			var node = me.tree.get_selected_node(); | ||||
| 
 | ||||
| 			v.parent_cost_center = node.label; | ||||
| 			v.company = me.company; | ||||
| 
 | ||||
| 			return frappe.call({ | ||||
| 				args: v, | ||||
| 				method: 'erpnext.accounts.utils.add_cc', | ||||
| 				callback: function(r) { | ||||
| 					d.hide(); | ||||
| 					if(node.expanded) { | ||||
| 						node.toggle_node(); | ||||
| 					} | ||||
| 					node.load(); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 		d.show(); | ||||
| 	} | ||||
| }); | ||||
| @ -1,22 +0,0 @@ | ||||
| { | ||||
|  "creation": "2012-06-14 15:07:28.000000", | ||||
|  "docstatus": 0, | ||||
|  "doctype": "Page", | ||||
|  "icon": "icon-sitemap", | ||||
|  "idx": 1, | ||||
|  "modified": "2013-07-11 14:39:42.000000", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Accounts", | ||||
|  "name": "accounts-browser", | ||||
|  "owner": "Administrator", | ||||
|  "page_name": "Accounts Browser", | ||||
|  "roles": [ | ||||
|   { | ||||
|    "role": "Accounts User" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Accounts Manager" | ||||
|   } | ||||
|  ], | ||||
|  "standard": "Yes" | ||||
| } | ||||
| @ -1,57 +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 | ||||
| import frappe.defaults | ||||
| from frappe.utils import flt | ||||
| from erpnext.accounts.utils import get_balance_on | ||||
| from erpnext.accounts.report.financial_statements import sort_root_accounts | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_companies(): | ||||
| 	"""get a list of companies based on permission""" | ||||
| 	return [d.name for d in frappe.get_list("Company", fields=["name"], | ||||
| 		order_by="name")] | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_children(): | ||||
| 	args = frappe.local.form_dict | ||||
| 	ctype, company = args['ctype'], args['comp'] | ||||
| 	fieldname = frappe.db.escape(ctype.lower().replace(' ','_')) | ||||
| 	doctype = frappe.db.escape(ctype) | ||||
| 
 | ||||
| 	# root | ||||
| 	if args['parent'] in ("Accounts", "Cost Centers"): | ||||
| 		fields = ", root_type, report_type, account_currency" if ctype=="Account" else "" | ||||
| 		acc = frappe.db.sql(""" select | ||||
| 			name as value, is_group as expandable {fields} | ||||
| 			from `tab{doctype}` | ||||
| 			where ifnull(`parent_{fieldname}`,'') = '' | ||||
| 			and `company` = %s	and docstatus<2 | ||||
| 			order by name""".format(fields=fields, fieldname = fieldname, doctype=doctype), | ||||
| 				company, as_dict=1) | ||||
| 
 | ||||
| 		if args["parent"]=="Accounts": | ||||
| 			sort_root_accounts(acc) | ||||
| 	else: | ||||
| 		# other | ||||
| 		fields = ", account_currency" if ctype=="Account" else "" | ||||
| 		acc = frappe.db.sql("""select | ||||
| 			name as value, is_group as expandable, parent_{fieldname} as parent {fields} | ||||
| 	 		from `tab{doctype}` | ||||
| 			where ifnull(`parent_{fieldname}`,'') = %s | ||||
| 			and docstatus<2 | ||||
| 			order by name""".format(fields=fields, fieldname=fieldname, doctype=doctype), | ||||
| 				args['parent'], as_dict=1) | ||||
| 
 | ||||
| 	if ctype == 'Account': | ||||
| 		company_currency = frappe.db.get_value("Company", company, "default_currency") | ||||
| 		for each in acc: | ||||
| 			each["company_currency"] = company_currency | ||||
| 			each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False)) | ||||
| 
 | ||||
| 			if each.account_currency != company_currency: | ||||
| 				each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"))) | ||||
| 
 | ||||
| 	return acc | ||||
| @ -10,6 +10,8 @@ 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 | ||||
| 
 | ||||
| class FiscalYearError(frappe.ValidationError): pass | ||||
| 
 | ||||
| @ -127,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"): | ||||
| @ -135,6 +137,10 @@ def add_ac(args=None): | ||||
| 		args.pop("ignore_permissions") | ||||
| 
 | ||||
| 	ac.update(args) | ||||
| 
 | ||||
| 	if not ac.parent_account: | ||||
| 		ac.parent_account = args.get("parent") | ||||
| 	 | ||||
| 	ac.old_parent = "" | ||||
| 	ac.freeze_account = "No" | ||||
| 	if cint(ac.get("is_root")): | ||||
| @ -153,6 +159,10 @@ def add_cc(args=None): | ||||
| 
 | ||||
| 	cc = frappe.new_doc("Cost Center") | ||||
| 	cc.update(args) | ||||
| 
 | ||||
| 	if not cc.parent_cost_center: | ||||
| 		cc.parent_cost_center = args.get("parent") | ||||
| 
 | ||||
| 	cc.old_parent = "" | ||||
| 	cc.insert() | ||||
| 	return cc.name | ||||
| @ -428,3 +438,51 @@ def get_account_name(account_type=None, root_type=None, is_group=None, account_c | ||||
| 		"account_currency": account_currency or frappe.defaults.get_defaults().currency, | ||||
| 		"company": company or frappe.defaults.get_defaults().company | ||||
| 	}, "name") | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_companies(): | ||||
| 	"""get a list of companies based on permission""" | ||||
| 	return [d.name for d in frappe.get_list("Company", fields=["name"], | ||||
| 		order_by="name")] | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_children(): | ||||
| 	args = frappe.local.form_dict | ||||
| 	doctype, company = args['doctype'], args['company'] | ||||
| 	fieldname = frappe.db.escape(doctype.lower().replace(' ','_')) | ||||
| 	doctype = frappe.db.escape(doctype) | ||||
| 
 | ||||
| 	# root | ||||
| 	if args['parent'] in ("Accounts", "Cost Centers"): | ||||
| 		fields = ", root_type, report_type, account_currency" if doctype=="Account" else "" | ||||
| 		acc = frappe.db.sql(""" select | ||||
| 			name as value, is_group as expandable {fields} | ||||
| 			from `tab{doctype}` | ||||
| 			where ifnull(`parent_{fieldname}`,'') = '' | ||||
| 			and `company` = %s	and docstatus<2 | ||||
| 			order by name""".format(fields=fields, fieldname = fieldname, doctype=doctype), | ||||
| 				company, as_dict=1) | ||||
| 
 | ||||
| 		if args["parent"]=="Accounts": | ||||
| 			sort_root_accounts(acc) | ||||
| 	else: | ||||
| 		# other | ||||
| 		fields = ", account_currency" if doctype=="Account" else "" | ||||
| 		acc = frappe.db.sql("""select | ||||
| 			name as value, is_group as expandable, parent_{fieldname} as parent {fields} | ||||
| 			from `tab{doctype}` | ||||
| 			where ifnull(`parent_{fieldname}`,'') = %s | ||||
| 			and docstatus<2 | ||||
| 			order by name""".format(fields=fields, fieldname=fieldname, doctype=doctype), | ||||
| 				args['parent'], as_dict=1) | ||||
| 
 | ||||
| 	if doctype == 'Account': | ||||
| 		company_currency = frappe.db.get_value("Company", company, "default_currency") | ||||
| 		for each in acc: | ||||
| 			each["company_currency"] = company_currency | ||||
| 			each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False)) | ||||
| 
 | ||||
| 			if each.account_currency != company_currency: | ||||
| 				each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"))) | ||||
| 
 | ||||
| 	return acc | ||||
|  | ||||
| @ -51,10 +51,10 @@ def get_data(): | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"name": "Accounts Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"label": _("Chart of Accounts"), | ||||
| 					"route": "Accounts Browser/Account", | ||||
| 					"route": "Tree/Account", | ||||
| 					"description": _("Tree of financial accounts."), | ||||
| 					"doctype": "Account", | ||||
| 				}, | ||||
| @ -193,10 +193,10 @@ def get_data(): | ||||
| 			"items": [ | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"name": "Accounts Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"label": _("Chart of Cost Centers"), | ||||
| 					"route": "Accounts Browser/Cost Center", | ||||
| 					"route": "Tree/Cost Center", | ||||
| 					"description": _("Tree of financial Cost Centers."), | ||||
| 					"doctype": "Cost Center", | ||||
| 				}, | ||||
|  | ||||
| @ -97,10 +97,10 @@ def get_data(): | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"label": _("Item Group"), | ||||
| 					"link": "Sales Browser/Item Group", | ||||
| 					"link": "Tree/Item Group", | ||||
| 					"description": _("Tree of Item Groups."), | ||||
| 					"doctype": "Item Group", | ||||
| 				}, | ||||
|  | ||||
| @ -92,27 +92,27 @@ def get_data(): | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"label": _("Customer Group"), | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"link": "Sales Browser/Customer Group", | ||||
| 					"link": "Tree/Customer Group", | ||||
| 					"description": _("Manage Customer Group Tree."), | ||||
| 					"doctype": "Customer Group", | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"label": _("Territory"), | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"link": "Sales Browser/Territory", | ||||
| 					"link": "Tree/Territory", | ||||
| 					"description": _("Manage Territory Tree."), | ||||
| 					"doctype": "Territory", | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"label": _("Sales Person"), | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"link": "Sales Browser/Sales Person", | ||||
| 					"link": "Tree/Sales Person", | ||||
| 					"description": _("Manage Sales Person Tree."), | ||||
| 					"doctype": "Sales Person", | ||||
| 				}, | ||||
|  | ||||
| @ -30,9 +30,9 @@ def get_data(): | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"label": _("Customer Group"), | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"link": "Sales Browser/Customer Group", | ||||
| 					"link": "Tree/Customer Group", | ||||
| 					"description": _("Manage Customer Group Tree."), | ||||
| 					"doctype": "Customer Group", | ||||
| 				}, | ||||
| @ -69,10 +69,10 @@ def get_data(): | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"label": _("Item Group"), | ||||
| 					"link": "Sales Browser/Item Group", | ||||
| 					"link": "Tree/Item Group", | ||||
| 					"description": _("Tree of Item Groups."), | ||||
| 					"doctype": "Item Group", | ||||
| 				}, | ||||
| @ -101,9 +101,9 @@ def get_data(): | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"label": _("Territory"), | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"link": "Sales Browser/Territory", | ||||
| 					"link": "Tree/Territory", | ||||
| 					"description": _("Manage Territory Tree."), | ||||
| 					"doctype": "Territory", | ||||
| 				}, | ||||
| @ -115,9 +115,9 @@ def get_data(): | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"label": _("Sales Person"), | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"link": "Sales Browser/Sales Person", | ||||
| 					"link": "Tree/Sales Person", | ||||
| 					"description": _("Manage Sales Person Tree."), | ||||
| 					"doctype": "Sales Person", | ||||
| 				}, | ||||
|  | ||||
| @ -78,10 +78,10 @@ def get_data(): | ||||
| 				}, | ||||
| 				{ | ||||
| 					"type": "page", | ||||
| 					"name": "Sales Browser", | ||||
| 					"name": "Tree", | ||||
| 					"icon": "icon-sitemap", | ||||
| 					"label": _("Item Group"), | ||||
| 					"link": "Sales Browser/Item Group", | ||||
| 					"link": "Tree/Item Group", | ||||
| 					"description": _("Tree of Item Groups."), | ||||
| 					"doctype": "Item Group", | ||||
| 				}, | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -24,7 +24,7 @@ frappe.ui.form.on("BOM", { | ||||
| 				frm.events.update_cost(frm); | ||||
| 			}); | ||||
| 			frm.add_custom_button(__("Browse BOM"), function() { | ||||
| 				frappe.set_route("bom-browser", frm.doc.name); | ||||
| 				frappe.set_route("Tree", "BOM"); | ||||
| 			}); | ||||
| 		} | ||||
| 
 | ||||
|  | ||||
| @ -438,3 +438,14 @@ def validate_bom_no(item, bom_no): | ||||
| 	if item and not (bom.item.lower() == item.lower() or \ | ||||
| 		bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower()): | ||||
| 		frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item)) | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_children(parent=None): | ||||
| 	if parent: | ||||
| 		return frappe.db.sql("""select item_code, | ||||
| 			bom_no as value, qty, | ||||
| 			if(ifnull(bom_no, "")!="", 1, 0) as expandable | ||||
| 			from `tabBOM Item` | ||||
| 			where parent=%s | ||||
| 			order by idx | ||||
| 			""", parent, as_dict=True) | ||||
							
								
								
									
										35
									
								
								erpnext/manufacturing/doctype/bom/bom_tree.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								erpnext/manufacturing/doctype/bom/bom_tree.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| frappe.treeview_settings["BOM"] = { | ||||
| 	get_tree_nodes: 'erpnext.manufacturing.doctype.bom.bom.get_children', | ||||
| 	filters: [ | ||||
| 		{ | ||||
| 			fieldname: "bom", | ||||
| 			fieldtype:"Link", | ||||
| 			options: "BOM", | ||||
| 			label: __("BOM") | ||||
| 		} | ||||
| 	], | ||||
| 	title: "BOM", | ||||
| 	breadcrumb: "Manufacturing", | ||||
| 	disable_add_node: true, | ||||
| 	root_label: "bom", //fieldname from filters
 | ||||
| 	get_label: function(node) { | ||||
| 		if(node.data.qty) { | ||||
| 			return node.data.qty + " x " + node.data.item_code; | ||||
| 		} else { | ||||
| 			return node.data.item_code || node.data.value; | ||||
| 		} | ||||
| 	}, | ||||
| 	toolbar: [ | ||||
| 		{toggle_btn: true}, | ||||
| 		{ | ||||
| 			label:__("Edit"), | ||||
| 			condition: function(node) { | ||||
| 				return node.expandable; | ||||
| 			}, | ||||
| 			click: function(node) { | ||||
| 				 | ||||
| 				frappe.set_route("Form", "BOM", node.data.value); | ||||
| 			} | ||||
| 		} | ||||
| 	], | ||||
| } | ||||
| @ -1,89 +0,0 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| frappe.pages['bom-browser'].on_page_load = function(wrapper) { | ||||
| 	var page = frappe.ui.make_app_page({ | ||||
| 		parent: wrapper, | ||||
| 		title: 'BOM Browser', | ||||
| 		single_column: true | ||||
| 	}); | ||||
| 
 | ||||
| 	page.main.css({ | ||||
| 		"min-height": "300px", | ||||
| 		"padding-bottom": "25px" | ||||
| 	}); | ||||
| 
 | ||||
| 	page.tree_area = $('<div class="padding"><p class="text-muted">'+ | ||||
| 		__("Select BOM to start") | ||||
| 		+'</p></div>').appendTo(page.main); | ||||
| 
 | ||||
| 	frappe.breadcrumbs.add(frappe.breadcrumbs.last_module || "Manufacturing"); | ||||
| 
 | ||||
| 	var make_tree = function() { | ||||
| 		erpnext.bom_tree = new erpnext.BOMTree(page.$bom_select.val(), page, page.tree_area); | ||||
| 	} | ||||
| 
 | ||||
| 	page.$bom_select = wrapper.page.add_field({fieldname: "bom", | ||||
| 		fieldtype:"Link", options: "BOM", label: __("BOM")}).$input | ||||
| 		.change(function() { | ||||
| 			make_tree(); | ||||
| 		}); | ||||
| 
 | ||||
| 	page.set_secondary_action(__('Refresh'), function() { | ||||
| 		make_tree(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| frappe.pages['bom-browser'].on_page_show = function(wrapper){ | ||||
| 	// set from route
 | ||||
| 	var bom = null; | ||||
| 	if(frappe.get_route()[1]) { | ||||
| 		var bom = frappe.get_route().splice(1).join("/"); | ||||
| 	} | ||||
| 	if(frappe.route_options && frappe.route_options.bom) { | ||||
| 		var bom = frappe.route_options.bom; | ||||
| 	} | ||||
| 	if(bom) { | ||||
| 		wrapper.page.$bom_select.val(bom).trigger("change"); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| erpnext.BOMTree = Class.extend({ | ||||
| 	init: function(root, page, parent) { | ||||
| 		$(parent).empty(); | ||||
| 		var me = this; | ||||
| 		me.page = page; | ||||
| 		me.bom = page.$bom_select.val(); | ||||
| 		me.can_read = frappe.model.can_read("BOM"); | ||||
| 		me.can_create = frappe.boot.user.can_create.indexOf("BOM") !== -1 || | ||||
| 					frappe.boot.user.in_create.indexOf("BOM") !== -1; | ||||
| 		me.can_write = frappe.model.can_write("BOM"); | ||||
| 		me.can_delete = frappe.model.can_delete("BOM"); | ||||
| 		this.tree = new frappe.ui.Tree({ | ||||
| 			parent: $(parent), | ||||
| 			label: me.bom, | ||||
| 			args: {parent: me.bom}, | ||||
| 			method: 'erpnext.manufacturing.page.bom_browser.bom_browser.get_children', | ||||
| 			toolbar: [ | ||||
| 				{toggle_btn: true}, | ||||
| 				{ | ||||
| 					label:__("Edit"), | ||||
| 					condition: function(node) { | ||||
| 						return node.expandable; | ||||
| 					}, | ||||
| 					click: function(node) { | ||||
| 						frappe.set_route("Form", "BOM", node.data.value); | ||||
| 					} | ||||
| 				} | ||||
| 			], | ||||
| 			get_label: function(node) { | ||||
| 				if(node.data.qty) { | ||||
| 					return node.data.qty + " x " + node.data.item_code; | ||||
| 				} else { | ||||
| 					return node.data.item_code || node.data.value; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| }); | ||||
| @ -1,21 +0,0 @@ | ||||
| { | ||||
|  "content": null,  | ||||
|  "creation": "2015-05-25 02:57:33.472044",  | ||||
|  "docstatus": 0,  | ||||
|  "doctype": "Page",  | ||||
|  "modified": "2015-05-25 02:57:33.472044",  | ||||
|  "modified_by": "Administrator",  | ||||
|  "module": "Manufacturing",  | ||||
|  "name": "bom-browser",  | ||||
|  "owner": "Administrator",  | ||||
|  "page_name": "bom-browser",  | ||||
|  "roles": [ | ||||
|   { | ||||
|    "role": "Manufacturing User" | ||||
|   } | ||||
|  ],  | ||||
|  "script": null,  | ||||
|  "standard": "Yes",  | ||||
|  "style": null,  | ||||
|  "title": "BOM Browser" | ||||
| } | ||||
| @ -1,15 +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 | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_children(parent): | ||||
| 	return frappe.db.sql("""select item_code, | ||||
| 		bom_no as value, qty, | ||||
| 		if(ifnull(bom_no, "")!="", 1, 0) as expandable | ||||
| 		from `tabBOM Item` | ||||
| 		where parent=%s | ||||
| 		order by idx | ||||
| 		""", parent, as_dict=True) | ||||
| @ -273,3 +273,4 @@ execute:frappe.rename_doc("DocType", "Payments", "Sales Invoice Payment", force= | ||||
| erpnext.patches.v7_0.update_mins_to_first_response | ||||
| erpnext.patches.v6_20x.repost_valuation_rate_for_negative_inventory | ||||
| erpnext.patches.v7_0.re_route | ||||
| erpnext.patches.v7_0.create_warehouse_nestedset | ||||
|  | ||||
							
								
								
									
										48
									
								
								erpnext/patches/v7_0/create_warehouse_nestedset.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								erpnext/patches/v7_0/create_warehouse_nestedset.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| 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", filters={"company": company.name}, fields=["name", "create_account_under", | ||||
| 			"parent_warehouse", "is_group"]): | ||||
| 			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 = "Yes" if warehouse.is_group == "Yes" else "No" | ||||
| 	 | ||||
| 	if not warehouse.parent_warehouse and warehouse.name != "{0} - {1}".format(_("All Warehouses"), company.abbr): | ||||
| 		warehouse.parent_warehouse = "{0} - {1}".format(_("All Warehouses"), company.abbr) | ||||
| 	 | ||||
| 	warehouse.save(ignore_permissions=True) | ||||
| 
 | ||||
| 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 and account != "{0} - {1}".format(_("All Warehouses"), company.abbr): | ||||
| 		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.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) | ||||
| @ -20,12 +20,12 @@ $(document).bind('toolbar_setup', function() { | ||||
| 
 | ||||
| // doctypes created via tree
 | ||||
| $.extend(frappe.create_routes, { | ||||
| 	"Customer Group": "Sales Browser/Customer Group", | ||||
| 	"Territory": "Sales Browser/Territory", | ||||
| 	"Item Group": "Sales Browser/Item Group", | ||||
| 	"Sales Person": "Sales Browser/Sales Person", | ||||
| 	"Account": "Accounts Browser/Account", | ||||
| 	"Cost Center": "Accounts Browser/Cost Center" | ||||
| 	"Customer Group": "Tree/Customer Group", | ||||
| 	"Territory": "Tree/Territory", | ||||
| 	"Item Group": "Tree/Item Group", | ||||
| 	"Sales Person": "Tree/Sales Person", | ||||
| 	"Account": "Tree/Account", | ||||
| 	"Cost Center": "Tree/Cost Center" | ||||
| }); | ||||
| 
 | ||||
| // preferred modules for breadcrumbs
 | ||||
|  | ||||
| @ -71,7 +71,11 @@ $.extend(erpnext.queries, { | ||||
| 
 | ||||
| 	warehouse: function(doc) { | ||||
| 		return { | ||||
| 			filters: [["Warehouse", "company", "in", ["", cstr(doc.company)]]] | ||||
| 			filters: [ | ||||
| 				["Warehouse", "company", "in", ["", cstr(doc.company)]], | ||||
| 				["Warehouse", "is_group", "=", "No"] | ||||
| 				 | ||||
| 			] | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| @ -1 +0,0 @@ | ||||
| Tree editor for Territory, Customer Group, Item Group, Sales Partner | ||||
| @ -1 +0,0 @@ | ||||
| from __future__ import unicode_literals | ||||
| @ -1,166 +0,0 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| frappe.pages["Sales Browser"].on_page_load = function(wrapper){ | ||||
| 	var page = frappe.ui.make_app_page({ | ||||
| 		parent: wrapper, | ||||
| 		single_column: true, | ||||
| 	}); | ||||
| 
 | ||||
| 	wrapper.page.add_menu_item(__('Refresh'), function() { | ||||
| 			wrapper.make_tree(); | ||||
| 		}); | ||||
| 
 | ||||
| 	wrapper.make_tree = function() { | ||||
| 		var ctype = frappe.get_route()[1] || 'Territory'; | ||||
| 		return frappe.call({ | ||||
| 			method: 'erpnext.selling.page.sales_browser.sales_browser.get_children', | ||||
| 			args: {ctype: ctype}, | ||||
| 			callback: function(r) { | ||||
| 				var root = r.message[0]["value"]; | ||||
| 				erpnext.sales_chart = new erpnext.SalesChart(ctype, root, page, | ||||
| 					page.main.css({ | ||||
| 						"min-height": "300px", | ||||
| 						"padding-bottom": "25px" | ||||
| 					})); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	wrapper.make_tree(); | ||||
| } | ||||
| 
 | ||||
| frappe.pages['Sales Browser'].on_page_show = function(wrapper){ | ||||
| 	// set route
 | ||||
| 	var ctype = frappe.get_route()[1] || 'Territory'; | ||||
| 
 | ||||
| 	wrapper.page.set_title(__('{0} Tree',[__(ctype)])); | ||||
| 
 | ||||
| 	if(erpnext.sales_chart && erpnext.sales_chart.ctype != ctype) { | ||||
| 		wrapper.make_tree(); | ||||
| 	} | ||||
| 
 | ||||
| 	frappe.breadcrumbs.add(frappe.breadcrumbs.last_module || "Selling"); | ||||
| }; | ||||
| 
 | ||||
| erpnext.SalesChart = Class.extend({ | ||||
| 	init: function(ctype, root, page, parent) { | ||||
| 		$(parent).empty(); | ||||
| 		var me = this; | ||||
| 		me.ctype = ctype; | ||||
| 		me.page = page; | ||||
| 		me.can_read = frappe.model.can_read(this.ctype); | ||||
| 		me.can_create = frappe.boot.user.can_create.indexOf(this.ctype) !== -1 || | ||||
| 					frappe.boot.user.in_create.indexOf(this.ctype) !== -1; | ||||
| 		me.can_write = frappe.model.can_write(this.ctype); | ||||
| 		me.can_delete = frappe.model.can_delete(this.ctype); | ||||
| 
 | ||||
| 		me.page.set_primary_action(__("New"), function() { | ||||
| 			me.new_node(); | ||||
| 		}, "octicon octicon-plus"); | ||||
| 
 | ||||
| 		this.tree = new frappe.ui.Tree({ | ||||
| 			parent: $(parent), | ||||
| 			label: __(root), | ||||
| 			args: {ctype: ctype}, | ||||
| 			method: 'erpnext.selling.page.sales_browser.sales_browser.get_children', | ||||
| 			toolbar: [ | ||||
| 				{toggle_btn: true}, | ||||
| 				{ | ||||
| 					label:__("Edit"), | ||||
| 					condition: function(node) { | ||||
| 						return !node.root && me.can_read; | ||||
| 					}, | ||||
| 					click: function(node) { | ||||
| 						frappe.set_route("Form", me.ctype, node.label); | ||||
| 					} | ||||
| 				}, | ||||
| 				{ | ||||
| 					label:__("Add Child"), | ||||
| 					condition: function(node) { return me.can_create && node.expandable; }, | ||||
| 					click: function(node) { | ||||
| 						me.new_node(); | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				}, | ||||
| 				{ | ||||
| 					label:__("Rename"), | ||||
| 					condition: function(node) { return !node.root && me.can_write; }, | ||||
| 					click: function(node) { | ||||
| 						frappe.model.rename_doc(me.ctype, node.label, function(new_name) { | ||||
| 							node.$a.html(new_name); | ||||
| 						}); | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				}, | ||||
| 				{ | ||||
| 					label:__("Delete"), | ||||
| 					condition: function(node) { return !node.root && me.can_delete; }, | ||||
| 					click: function(node) { | ||||
| 						frappe.model.delete_doc(me.ctype, node.label, function() { | ||||
| 							node.parent.remove(); | ||||
| 						}); | ||||
| 					}, | ||||
| 					btnClass: "hidden-xs" | ||||
| 				} | ||||
| 
 | ||||
| 			] | ||||
| 		}); | ||||
| 	}, | ||||
| 	new_node: function() { | ||||
| 		var me = this; | ||||
| 		var node = me.tree.get_selected_node(); | ||||
| 
 | ||||
| 		if(!(node && node.expandable)) { | ||||
| 			frappe.msgprint(__("Select a group node first.")); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		var fields = [ | ||||
| 			{fieldtype:'Data', fieldname: 'name_field', | ||||
| 				label:__('New {0} Name',[__(me.ctype)]), reqd:true}, | ||||
| 			{fieldtype:'Select', fieldname:'is_group', label:__('Group Node'), options:'No\nYes', | ||||
| 				description: __("Further nodes can be only created under 'Group' type nodes")} | ||||
| 		] | ||||
| 
 | ||||
| 		if(me.ctype == "Sales Person") { | ||||
| 			fields.splice(-1, 0, {fieldtype:'Link', fieldname:'employee', label:__('Employee'), | ||||
| 				options:'Employee', description: __("Please enter Employee Id of this sales person")}); | ||||
| 		} | ||||
| 
 | ||||
| 		// the dialog
 | ||||
| 		var d = new frappe.ui.Dialog({ | ||||
| 			title: __('New {0}',[__(me.ctype)]), | ||||
| 			fields: fields | ||||
| 		}) | ||||
| 
 | ||||
| 		d.set_value("is_group", "No"); | ||||
| 		// create
 | ||||
| 		d.set_primary_action(__("Create New"), function() { | ||||
| 			var btn = this; | ||||
| 			var v = d.get_values(); | ||||
| 			if(!v) return; | ||||
| 
 | ||||
| 			var node = me.tree.get_selected_node(); | ||||
| 
 | ||||
| 			v.parent = node.label; | ||||
| 			v.ctype = me.ctype; | ||||
| 
 | ||||
| 			return frappe.call({ | ||||
| 				method: 'erpnext.selling.page.sales_browser.sales_browser.add_node', | ||||
| 				args: v, | ||||
| 				callback: function(r) { | ||||
| 					if(!r.exc) { | ||||
| 						d.hide(); | ||||
| 						if(node.expanded) { | ||||
| 							node.toggle_node(); | ||||
| 						} | ||||
| 						node.reload(); | ||||
| 					} | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 
 | ||||
| 		d.show(); | ||||
| 	}, | ||||
| }); | ||||
| @ -1,37 +0,0 @@ | ||||
| { | ||||
|  "creation": "2012-06-14 15:07:26.000000", | ||||
|  "docstatus": 0, | ||||
|  "doctype": "Page", | ||||
|  "icon": "icon-sitemap", | ||||
|  "idx": 1, | ||||
|  "modified": "2013-07-11 14:43:56.000000", | ||||
|  "modified_by": "Administrator", | ||||
|  "module": "Selling", | ||||
|  "name": "sales-browser", | ||||
|  "owner": "Administrator", | ||||
|  "page_name": "Sales Browser", | ||||
|  "roles": [ | ||||
|   { | ||||
|    "role": "Sales Master Manager" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Material Master Manager" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Accounts Manager" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Sales Master Manager" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Purchase Manager" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Purchase Master Manager" | ||||
|   }, | ||||
|   { | ||||
|    "role": "Material Manager" | ||||
|   } | ||||
|  ], | ||||
|  "standard": "Yes" | ||||
| } | ||||
| @ -1,37 +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 | ||||
| 
 | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_children(): | ||||
| 	ctype = frappe.local.form_dict.get('ctype') | ||||
| 	parent_field = 'parent_' + ctype.lower().replace(' ', '_') | ||||
| 	parent = frappe.form_dict.get("parent") or "" | ||||
| 
 | ||||
| 	return frappe.db.sql("""select name as value, | ||||
| 		if(is_group='Yes', 1, 0) as expandable | ||||
| 		from `tab{ctype}` | ||||
| 		where docstatus < 2 | ||||
| 		and ifnull(`{parent_field}`,'') = %s | ||||
| 		order by name""".format(ctype=frappe.db.escape(ctype), parent_field=frappe.db.escape(parent_field)), | ||||
| 		parent, as_dict=1) | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def add_node(): | ||||
| 	ctype = frappe.form_dict.get('ctype') | ||||
| 	parent_field = 'parent_' + ctype.lower().replace(' ', '_') | ||||
| 	name_field = ctype.lower().replace(' ', '_') + '_name' | ||||
| 
 | ||||
| 	doc = frappe.new_doc(ctype) | ||||
| 	doc.update({ | ||||
| 		name_field: frappe.form_dict['name_field'], | ||||
| 		parent_field: frappe.form_dict['parent'], | ||||
| 		"is_group": frappe.form_dict['is_group'] | ||||
| 	}) | ||||
| 	if ctype == "Sales Person": | ||||
| 		doc.employee = frappe.form_dict.get('employee') | ||||
| 
 | ||||
| 	doc.save() | ||||
| @ -18,11 +18,11 @@ frappe.ui.form.on("Company", { | ||||
| 				!frm.doc.__onload.transactions_exist)); | ||||
| 
 | ||||
| 			frm.add_custom_button(__('Cost Centers'), function() { | ||||
| 				frappe.set_route('Accounts Browser', 'Cost Center', {'company': frm.doc.name}) | ||||
| 				frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name}) | ||||
| 			}) | ||||
| 
 | ||||
| 			frm.add_custom_button(__('Chart of Accounts'), function() { | ||||
| 				frappe.set_route('Accounts Browser', 'Account', {'company': frm.doc.name}) | ||||
| 				frappe.set_route('Tree', 'Account', {'company': frm.doc.name}) | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
|  | ||||
| @ -87,15 +87,23 @@ class Company(Document): | ||||
| 				.format(self.country.lower()))(self) | ||||
| 
 | ||||
| 	def create_default_warehouses(self): | ||||
| 		for whname in (_("Stores"), _("Work In Progress"), _("Finished Goods")): | ||||
| 			if not frappe.db.exists("Warehouse", whname + " - " + self.abbr): | ||||
| 		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"}]: | ||||
| 			 | ||||
| 			if not frappe.db.exists("Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)): | ||||
| 				stock_group = frappe.db.get_value("Account", {"account_type": "Stock", | ||||
| 					"is_group": 1, "company": self.name}) | ||||
| 				if stock_group: | ||||
| 					warehouse = frappe.get_doc({ | ||||
| 						"doctype":"Warehouse", | ||||
| 						"warehouse_name": whname, | ||||
| 						"warehouse_name": wh_detail["warehouse_name"], | ||||
| 						"is_group": wh_detail["is_group"], | ||||
| 						"company": self.name, | ||||
| 						"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 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| cur_frm.list_route = "Sales Browser/Customer Group"; | ||||
| cur_frm.list_route = "Tree/Customer Group"; | ||||
| 
 | ||||
| cur_frm.cscript.refresh = function(doc, cdt, cdn) { | ||||
| 	cur_frm.cscript.set_root_readonly(doc); | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| 
 | ||||
| frappe.ui.form.on("Item Group", { | ||||
| 	onload: function(frm) { | ||||
| 		frm.list_route = "Sales Browser/Item Group"; | ||||
| 		frm.list_route = "Tree/Item Group"; | ||||
| 
 | ||||
| 		//get query select item group
 | ||||
| 		frm.fields_dict['parent_item_group'].get_query = function(doc,cdt,cdn) { | ||||
| @ -19,7 +19,7 @@ frappe.ui.form.on("Item Group", { | ||||
| 	refresh: function(frm) { | ||||
| 		frm.trigger("set_root_readonly"); | ||||
| 		frm.add_custom_button(__("Item Group Tree"), function() { | ||||
| 			frappe.set_route("Sales Browser", "Item Group"); | ||||
| 			frappe.set_route("Tree", "Item Group"); | ||||
| 		}, "icon-sitemap"); | ||||
| 	}, | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| cur_frm.list_route = "Sales Browser/Sales Person"; | ||||
| cur_frm.list_route = "Tree/Sales Person"; | ||||
| 
 | ||||
| cur_frm.cscript.refresh = function(doc, cdt, cdn) { | ||||
| 	cur_frm.cscript.set_root_readonly(doc); | ||||
|  | ||||
							
								
								
									
										11
									
								
								erpnext/setup/doctype/sales_person/sales_person_tree.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								erpnext/setup/doctype/sales_person/sales_person_tree.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| frappe.treeview_settings["Sales Person"] = { | ||||
| 	fields: [ | ||||
| 		{fieldtype:'Data', fieldname: 'name_field', | ||||
| 			label:__('New Sales Person Name'), reqd:true}, | ||||
| 		{fieldtype:'Link', fieldname:'employee', | ||||
| 			label:__('Employee'), options:'Employee', | ||||
| 			description: __("Please enter Employee Id of this sales person")}, | ||||
| 		{fieldtype:'Select', fieldname:'is_group', label:__('Group Node'), options:'No\nYes', | ||||
| 			description: __("Further nodes can be only created under 'Group' type nodes")} | ||||
| 	], | ||||
| } | ||||
| @ -1,7 +1,7 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| cur_frm.list_route = "Sales Browser/Territory"; | ||||
| cur_frm.list_route = "Tree/Territory"; | ||||
| 
 | ||||
| cur_frm.cscript.refresh = function(doc, cdt, cdn) { | ||||
| 	cur_frm.cscript.set_root_readonly(doc); | ||||
|  | ||||
| @ -52,26 +52,26 @@ def update_page_info(bootinfo): | ||||
| 	bootinfo.page_info.update({ | ||||
| 		"Chart of Accounts": { | ||||
| 			"title": "Chart of Accounts", | ||||
| 			"route": "Accounts Browser/Account" | ||||
| 			"route": "Tree/Account" | ||||
| 		}, | ||||
| 		"Chart of Cost Centers": { | ||||
| 			"title": "Chart of Cost Centers", | ||||
| 			"route": "Accounts Browser/Cost Center" | ||||
| 			"route": "Tree/Cost Center" | ||||
| 		}, | ||||
| 		"Item Group Tree": { | ||||
| 			"title": "Item Group Tree", | ||||
| 			"route": "Sales Browser/Item Group" | ||||
| 			"route": "Tree/Item Group" | ||||
| 		}, | ||||
| 		"Customer Group Tree": { | ||||
| 			"title": "Customer Group Tree", | ||||
| 			"route": "Sales Browser/Customer Group" | ||||
| 			"route": "Tree/Customer Group" | ||||
| 		}, | ||||
| 		"Territory Tree": { | ||||
| 			"title": "Territory Tree", | ||||
| 			"route": "Sales Browser/Territory" | ||||
| 			"route": "Tree/Territory" | ||||
| 		}, | ||||
| 		"Sales Person Tree": { | ||||
| 			"title": "Sales Person Tree", | ||||
| 			"route": "Sales Browser/Sales Person" | ||||
| 			"route": "Tree/Sales Person" | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| 
 | ||||
| from __future__ import unicode_literals | ||||
| import frappe | ||||
| from frappe import _ | ||||
| from frappe.utils import flt, nowdate | ||||
| import frappe.defaults | ||||
| from frappe.model.document import Document | ||||
| @ -15,13 +16,19 @@ class Bin(Document): | ||||
| 		self.validate_mandatory() | ||||
| 
 | ||||
| 		self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \ | ||||
| 		 	flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty) | ||||
| 			flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty) | ||||
| 		 | ||||
| 		self.block_transactions_against_group_warehouse() | ||||
| 
 | ||||
| 	def validate_mandatory(self): | ||||
| 		qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty'] | ||||
| 		for f in qf: | ||||
| 			if (not getattr(self, f, None)) or (not self.get(f)): | ||||
| 				self.set(f, 0.0) | ||||
| 	 | ||||
| 	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) | ||||
|  | ||||
| @ -160,6 +160,28 @@ $.extend(erpnext.item, { | ||||
| 		frm.fields_dict.supplier_items.grid.get_field("supplier").get_query = function(doc, cdt, cdn) { | ||||
| 			return { query: "erpnext.controllers.queries.supplier_query" } | ||||
| 		} | ||||
| 		 | ||||
| 		frm.fields_dict['default_warehouse'].get_query = function(doc) { | ||||
| 			return { | ||||
| 				filters: { "is_group": "No" } | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		frm.fields_dict.reorder_levels.grid.get_field("warehouse_group").get_query = function(doc, cdt, cdn) { | ||||
| 			return { | ||||
| 				filters: { "is_group": "Yes" } | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		frm.fields_dict.reorder_levels.grid.get_field("warehouse").get_query = function(doc, cdt, cdn) { | ||||
| 			var d = locals[cdt][cdn]; | ||||
| 			return { | ||||
| 				filters: { | ||||
| 					"is_group": "No", | ||||
| 					"parent_warehouse": d.warehouse_group | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	}, | ||||
| 
 | ||||
|  | ||||
| @ -227,6 +227,35 @@ | ||||
|        "warehouse_reorder_qty": 20 | ||||
|       } | ||||
|   ] | ||||
|  }, | ||||
|  { | ||||
|   "default_warehouse": "_Test Warehouse Group-C1 - _TC", | ||||
|   "description": "_Test Item 1", | ||||
|   "doctype": "Item", | ||||
|   "expense_account": "_Test Account Cost for Goods Sold - _TC", | ||||
|   "cost_center": "_Test Cost Center - _TC", | ||||
|   "has_batch_no": 0, | ||||
|   "has_serial_no": 0, | ||||
|   "income_account": "Sales - _TC", | ||||
|   "inspection_required": 0, | ||||
|   "is_stock_item": 1, | ||||
|   "is_sub_contracted_item": 0, | ||||
|   "item_code": "_Test Item Warehouse Group Wise Reorder", | ||||
|   "item_group": "_Test Item Group", | ||||
|   "item_name": "_Test Item Warehouse Group Wise Reorder", | ||||
|   "apply_warehouse_wise_reorder_level": 1, | ||||
|   "reorder_levels": [ | ||||
|    { | ||||
| 	"warehouse_group": "_Test Warehouse Group - _TC", | ||||
|     "material_request_type": "Purchase", | ||||
|     "warehouse": "_Test Warehouse Group-C1 - _TC", | ||||
|     "warehouse_reorder_level": 20, | ||||
|     "warehouse_reorder_qty": 20 | ||||
|    } | ||||
|   ], | ||||
|   "stock_uom": "_Test UOM", | ||||
|   "show_in_website": 1, | ||||
|   "website_warehouse": "_Test Warehouse Group-C1 - _TC" | ||||
|  } | ||||
| 
 | ||||
| ] | ||||
|  | ||||
| @ -3,11 +3,39 @@ | ||||
|  "allow_import": 0,  | ||||
|  "allow_rename": 0,  | ||||
|  "autoname": "hash",  | ||||
|  "beta": 0,  | ||||
|  "creation": "2013-03-07 11:42:59",  | ||||
|  "custom": 0,  | ||||
|  "docstatus": 0,  | ||||
|  "doctype": "DocType",  | ||||
|  "document_type": "Setup",  | ||||
|  "fields": [ | ||||
|   { | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "fieldname": "warehouse_group",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "label": "Warehouse Group",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Warehouse",  | ||||
|    "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,  | ||||
| @ -16,6 +44,7 @@ | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "label": "Warehouse",  | ||||
| @ -24,6 +53,7 @@ | ||||
|    "options": "Warehouse",  | ||||
|    "permlevel": 0,  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 1,  | ||||
| @ -39,6 +69,7 @@ | ||||
|    "fieldtype": "Float",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "label": "Re-order Level",  | ||||
| @ -46,6 +77,7 @@ | ||||
|    "no_copy": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 1,  | ||||
| @ -61,6 +93,7 @@ | ||||
|    "fieldtype": "Float",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "label": "Re-order Qty",  | ||||
| @ -68,6 +101,7 @@ | ||||
|    "no_copy": 0,  | ||||
|    "permlevel": 0,  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
| @ -83,6 +117,7 @@ | ||||
|    "fieldtype": "Select",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "label": "Material Request Type",  | ||||
| @ -91,6 +126,7 @@ | ||||
|    "options": "Purchase\nTransfer",  | ||||
|    "permlevel": 0,  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 1,  | ||||
| @ -102,18 +138,21 @@ | ||||
|  "hide_heading": 0,  | ||||
|  "hide_toolbar": 0,  | ||||
|  "idx": 1,  | ||||
|  "image_view": 0,  | ||||
|  "in_create": 1,  | ||||
|  "in_dialog": 0,  | ||||
|  "is_submittable": 0,  | ||||
|  "issingle": 0,  | ||||
|  "istable": 1,  | ||||
|  "max_attachments": 0,  | ||||
|  "modified": "2015-11-16 06:29:48.492627",  | ||||
|  "modified": "2016-06-20 15:52:01.978593",  | ||||
|  "modified_by": "Administrator",  | ||||
|  "module": "Stock",  | ||||
|  "name": "Item Reorder",  | ||||
|  "owner": "Administrator",  | ||||
|  "permissions": [],  | ||||
|  "quick_entry": 0,  | ||||
|  "read_only": 0,  | ||||
|  "read_only_onload": 0 | ||||
|  "read_only_onload": 0,  | ||||
|  "track_seen": 0 | ||||
| } | ||||
| @ -79,8 +79,11 @@ class TestStockEntry(unittest.TestCase): | ||||
| 
 | ||||
| 	def test_auto_material_request_for_variant(self): | ||||
| 		self._test_auto_material_request("_Test Variant Item-S") | ||||
| 
 | ||||
| 	def _test_auto_material_request(self, item_code, material_request_type="Purchase"): | ||||
| 	 | ||||
| 	def test_auto_material_request_for_warehouse_group(self): | ||||
| 		self._test_auto_material_request("_Test Item Warehouse Group Wise Reorder", warehouse="_Test Warehouse Group-C1 - _TC") | ||||
| 		 | ||||
| 	def _test_auto_material_request(self, item_code, material_request_type="Purchase", warehouse="_Test Warehouse - _TC"): | ||||
| 		item = frappe.get_doc("Item", item_code) | ||||
| 
 | ||||
| 		if item.variant_of: | ||||
| @ -89,14 +92,14 @@ class TestStockEntry(unittest.TestCase): | ||||
| 			template = item | ||||
| 
 | ||||
| 		projected_qty, actual_qty = frappe.db.get_value("Bin", {"item_code": item_code, | ||||
| 			"warehouse": "_Test Warehouse - _TC"}, ["projected_qty", "actual_qty"]) or [0, 0] | ||||
| 			"warehouse": warehouse}, ["projected_qty", "actual_qty"]) or [0, 0] | ||||
| 
 | ||||
| 		# stock entry reqd for auto-reorder | ||||
| 		create_stock_reconciliation(item_code=item_code, warehouse="_Test Warehouse - _TC", | ||||
| 		create_stock_reconciliation(item_code=item_code, warehouse=warehouse, | ||||
| 			qty = actual_qty + abs(projected_qty) + 10, rate=100) | ||||
| 
 | ||||
| 		projected_qty = frappe.db.get_value("Bin", {"item_code": item_code, | ||||
| 			"warehouse": "_Test Warehouse - _TC"}, "projected_qty") or 0 | ||||
| 			"warehouse": warehouse}, "projected_qty") or 0 | ||||
| 
 | ||||
| 		frappe.db.set_value("Stock Settings", None, "auto_indent", 1) | ||||
| 
 | ||||
|  | ||||
| @ -25,6 +25,7 @@ class StockLedgerEntry(Document): | ||||
| 		validate_warehouse_company(self.warehouse, self.company) | ||||
| 		self.scrub_posting_time() | ||||
| 		self.validate_and_set_fiscal_year() | ||||
| 		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) | ||||
| @ -117,6 +118,9 @@ class StockLedgerEntry(Document): | ||||
| 		if not self.fiscal_year: | ||||
| 			self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0] | ||||
| 
 | ||||
| 	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` | ||||
|  | ||||
| @ -116,10 +116,11 @@ def create_stock_reconciliation(**args): | ||||
| def set_valuation_method(item_code, valuation_method): | ||||
| 	frappe.db.set_value("Item", item_code, "valuation_method", valuation_method) | ||||
| 
 | ||||
| 	for warehouse in frappe.get_all("Warehouse", filters={"company": "_Test Company"}): | ||||
| 		update_entries_after({ | ||||
| 			"item_code": item_code, | ||||
| 			"warehouse": warehouse.name | ||||
| 		}, allow_negative_stock=1) | ||||
| 	for warehouse in frappe.get_all("Warehouse", filters={"company": "_Test Company"}, fields=["name", "is_group"]): | ||||
| 		if warehouse.is_group == "No": | ||||
| 			update_entries_after({ | ||||
| 				"item_code": item_code, | ||||
| 				"warehouse": warehouse.name | ||||
| 			}, allow_negative_stock=1) | ||||
| 
 | ||||
| test_dependencies = ["Item", "Warehouse"] | ||||
|  | ||||
| @ -3,35 +3,68 @@ | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Stock Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Fixed Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse 1" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Fixed Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse 2" | ||||
|   "warehouse_name": "_Test Warehouse", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Stock Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Rejected Warehouse" | ||||
|   "warehouse_name": "_Test Warehouse", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Fixed Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse 1", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Fixed Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse 2", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "create_account_under": "Stock Assets - _TC", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Rejected Warehouse", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company 1", | ||||
|   "create_account_under": "Stock Assets - _TC1", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse 2" | ||||
|   "warehouse_name": "_Test Warehouse 2", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse No Account" | ||||
|   "warehouse_name": "_Test Warehouse No Account", | ||||
|   "is_group": "No" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse Group", | ||||
|   "is_group": "Yes" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse Group-C1", | ||||
|   "is_group": "No", | ||||
|   "parent_warehouse": "_Test Warehouse Group - _TC" | ||||
|  }, | ||||
|  { | ||||
|   "company": "_Test Company", | ||||
|   "doctype": "Warehouse", | ||||
|   "warehouse_name": "_Test Warehouse Group-C2", | ||||
|   "is_group": "No", | ||||
|   "parent_warehouse": "_Test Warehouse Group - _TC" | ||||
|  } | ||||
| ] | ||||
|  | ||||
| @ -4,4 +4,22 @@ from __future__ import unicode_literals | ||||
| 
 | ||||
| 
 | ||||
| import frappe | ||||
| test_records = frappe.get_test_records('Warehouse') | ||||
| import unittest | ||||
| test_records = frappe.get_test_records('Warehouse') | ||||
| 
 | ||||
| class TestWarehouse(unittest.TestCase): | ||||
| 	def test_parent_warehouse(self): | ||||
| 		parent_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC") | ||||
| 		self.assertEquals(parent_warehouse.is_group, "Yes") | ||||
| 		 | ||||
| 	def test_warehouse_hierarchy(self): | ||||
| 		p_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC") | ||||
| 		 | ||||
| 		child_warehouses =  frappe.db.sql("""select name, is_group, parent_warehouse from `tabWarehouse` wh | ||||
| 			where wh.lft > %s and wh.rgt < %s""", (p_warehouse.lft, p_warehouse.rgt), as_dict=1) | ||||
| 		 | ||||
| 		for child_warehouse in child_warehouses: | ||||
| 			self.assertEquals(p_warehouse.name, child_warehouse.parent_warehouse) | ||||
| 			self.assertEquals(child_warehouse.is_group, "No") | ||||
| 		 | ||||
| 		 | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||
| // License: GNU General Public License v3. See license.txt
 | ||||
| 
 | ||||
| cur_frm.list_route = "Tree/Warehouse"; | ||||
| 
 | ||||
| frappe.ui.form.on("Warehouse", { | ||||
| 	refresh: function(frm) { | ||||
| 		frm.toggle_display('warehouse_name', frm.doc.__islocal); | ||||
| @ -17,10 +19,20 @@ frappe.ui.form.on("Warehouse", { | ||||
| 				frappe.set_route("query-report", "General Ledger"); | ||||
| 			}); | ||||
|  		} | ||||
| 		 | ||||
| 		frm.fields_dict['parent_warehouse'].get_query = function(doc) { | ||||
| 			return { | ||||
| 				filters: { | ||||
| 					"is_group": "Yes", | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| cur_frm.set_query("create_account_under", function() { | ||||
| 	return { | ||||
| 		filters: { | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
|  "allow_copy": 0,  | ||||
|  "allow_import": 1,  | ||||
|  "allow_rename": 1,  | ||||
|  "beta": 0,  | ||||
|  "creation": "2013-03-07 18:50:32",  | ||||
|  "custom": 0,  | ||||
|  "description": "A logical Warehouse against which stock entries are made.",  | ||||
| @ -420,6 +421,159 @@ | ||||
|    "search_index": 0,  | ||||
|    "set_only_once": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|   { | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "fieldname": "tree_details",  | ||||
|    "fieldtype": "Section Break",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "label": "Tree Details",  | ||||
|    "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": "parent_warehouse",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 1,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "label": "Parent Warehouse",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Warehouse",  | ||||
|    "permlevel": 0,  | ||||
|    "precision": "",  | ||||
|    "print_hide": 0,  | ||||
|    "print_hide_if_no_value": 0,  | ||||
|    "read_only": 0,  | ||||
|    "report_hide": 0,  | ||||
|    "reqd": 0,  | ||||
|    "search_index": 1,  | ||||
|    "set_only_once": 0,  | ||||
|    "unique": 0 | ||||
|   },  | ||||
|   { | ||||
|    "allow_on_submit": 0,  | ||||
|    "bold": 0,  | ||||
|    "collapsible": 0,  | ||||
|    "fieldname": "is_group",  | ||||
|    "fieldtype": "Select",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 1,  | ||||
|    "label": "Has Child Node",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "\nYes\nNo",  | ||||
|    "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": "lft",  | ||||
|    "fieldtype": "Int",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "label": "lft",  | ||||
|    "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": "rgt",  | ||||
|    "fieldtype": "Int",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "label": "rgt",  | ||||
|    "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": "old_parent",  | ||||
|    "fieldtype": "Link",  | ||||
|    "hidden": 0,  | ||||
|    "ignore_user_permissions": 0,  | ||||
|    "ignore_xss_filter": 0,  | ||||
|    "in_filter": 0,  | ||||
|    "in_list_view": 0,  | ||||
|    "label": "Old Parent",  | ||||
|    "length": 0,  | ||||
|    "no_copy": 0,  | ||||
|    "options": "Warehouse",  | ||||
|    "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 | ||||
|   } | ||||
|  ],  | ||||
|  "hide_heading": 0,  | ||||
| @ -432,7 +586,7 @@ | ||||
|  "issingle": 0,  | ||||
|  "istable": 0,  | ||||
|  "max_attachments": 0,  | ||||
|  "modified": "2016-04-18 05:44:24.837579",  | ||||
|  "modified": "2016-05-23 21:25:21.396188",  | ||||
|  "modified_by": "Administrator",  | ||||
|  "module": "Stock",  | ||||
|  "name": "Warehouse",  | ||||
|  | ||||
| @ -5,10 +5,11 @@ from __future__ import unicode_literals | ||||
| import frappe | ||||
| from frappe.utils import cint, validate_email_add | ||||
| from frappe import throw, msgprint, _ | ||||
| from frappe.utils.nestedset import NestedSet | ||||
| 
 | ||||
| from frappe.model.document import Document | ||||
| 
 | ||||
| class Warehouse(Document): | ||||
| class Warehouse(NestedSet): | ||||
| 	nsm_parent_field = 'parent_warehouse' | ||||
| 	 | ||||
| 	def autoname(self): | ||||
| 		suffix = " - " + frappe.db.get_value("Company", self.company, "abbr") | ||||
| 		if not self.warehouse_name.endswith(suffix): | ||||
| @ -45,6 +46,7 @@ class Warehouse(Document): | ||||
| 
 | ||||
| 	def on_update(self): | ||||
| 		self.create_account_head() | ||||
| 		self.update_nsm_model() | ||||
| 
 | ||||
| 	def create_account_head(self): | ||||
| 		if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): | ||||
| @ -55,8 +57,9 @@ class Warehouse(Document): | ||||
| 					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, | ||||
| @ -75,13 +78,16 @@ class Warehouse(Document): | ||||
| 				{"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)) | ||||
| 		elif frappe.db.get_value("Account", self.create_account_under, "company") != self.company: | ||||
| 			frappe.throw(_("Warehouse {0}: Parent account {1} does not bolong to the company {2}") | ||||
| 				.format(self.name, self.create_account_under, self.company)) | ||||
| 
 | ||||
| 	 | ||||
| 	def update_nsm_model(self): | ||||
| 		frappe.utils.nestedset.update_nsm(self) | ||||
| 
 | ||||
| 	def on_trash(self): | ||||
| 		# delete bin | ||||
| @ -101,6 +107,11 @@ class Warehouse(Document): | ||||
| 		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 | ||||
| @ -161,3 +172,51 @@ class Warehouse(Document): | ||||
| 
 | ||||
| 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) | ||||
| 		frappe.db.auto_commit_on_many_writes = 0 | ||||
| 
 | ||||
| @frappe.whitelist() | ||||
| def get_children(): | ||||
| 	from erpnext.stock.utils import get_stock_value_on | ||||
| 	doctype = frappe.local.form_dict.get('doctype') | ||||
| 	company = frappe.local.form_dict.get('company') | ||||
| 	 | ||||
| 	parent_field = 'parent_' + doctype.lower().replace(' ', '_') | ||||
| 	parent = frappe.form_dict.get("parent") or "" | ||||
| 	 | ||||
| 	if parent == "Warehouses": | ||||
| 		parent = "" | ||||
| 
 | ||||
| 	warehouses = frappe.db.sql("""select name as value, | ||||
| 		if(is_group='Yes', 1, 0) as expandable | ||||
| 		from `tab{doctype}` | ||||
| 		where docstatus < 2 | ||||
| 		and ifnull(`{parent_field}`,'') = %s and `company` = %s | ||||
| 		order by name""".format(doctype=frappe.db.escape(doctype), parent_field=frappe.db.escape(parent_field)), | ||||
| 		(parent, company), as_dict=1) | ||||
| 	 | ||||
| 	# return warehouses | ||||
| 	for wh in warehouses: | ||||
| 		wh["balance"] = get_stock_value_on(warehouse=wh.value) | ||||
| 	return warehouses | ||||
| 		 | ||||
| @frappe.whitelist() | ||||
| def add_node(): | ||||
| 	doctype = frappe.form_dict.get('doctype') | ||||
| 	company = frappe.form_dict.get('company') | ||||
| 	parent_field = 'parent_' + doctype.lower().replace(' ', '_') | ||||
| 	name_field = doctype.lower().replace(' ', '_') + '_name' | ||||
| 	 | ||||
| 	doc = frappe.new_doc(doctype) | ||||
| 	 | ||||
| 	parent = frappe.form_dict['parent'] | ||||
| 	 | ||||
| 	if cint(frappe.form_dict['is_root']): | ||||
| 		parent = None | ||||
| 
 | ||||
| 	doc.update({ | ||||
| 		name_field: frappe.form_dict['name_field'], | ||||
| 		parent_field: parent, | ||||
| 		"is_group": frappe.form_dict['is_group'], | ||||
| 		"company": company | ||||
| 	}) | ||||
| 	 | ||||
| 	doc.save() | ||||
|  | ||||
							
								
								
									
										20
									
								
								erpnext/stock/doctype/warehouse/warehouse_tree.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								erpnext/stock/doctype/warehouse/warehouse_tree.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| frappe.treeview_settings['Warehouse'] = { | ||||
| 	get_tree_nodes: "erpnext.stock.doctype.warehouse.warehouse.get_children", | ||||
| 	add_tree_node: "erpnext.stock.doctype.warehouse.warehouse.add_node", | ||||
| 	get_tree_root: false, | ||||
| 	root_label: "Warehouses", | ||||
| 	filters: [{ | ||||
| 		fieldname: "company", | ||||
| 		fieldtype:"Select", | ||||
| 		options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), | ||||
| 		label: __("Company"), | ||||
| 		default: frappe.defaults.get_default('company') ? frappe.defaults.get_default('company'): "" | ||||
| 	}], | ||||
| 	onrender: function(node) { | ||||
| 		if (node.data && node.data.balance!==undefined) { | ||||
| 			$('<span class="balance-area pull-right text-muted small">' | ||||
| 			+ format_currency(Math.abs(node.data.balance), node.data.company_currency) | ||||
| 			+ '</span>').insertBefore(node.$ul); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -37,7 +37,7 @@ def _reorder_item(): | ||||
| 
 | ||||
| 	item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider) | ||||
| 
 | ||||
| 	def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type): | ||||
| 	def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None): | ||||
| 		if warehouse not in warehouse_company: | ||||
| 			# a disabled warehouse | ||||
| 			return | ||||
| @ -46,7 +46,10 @@ def _reorder_item(): | ||||
| 		reorder_qty = flt(reorder_qty) | ||||
| 
 | ||||
| 		# projected_qty will be 0 if Bin does not exist | ||||
| 		projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse)) | ||||
| 		if warehouse_group: | ||||
| 			projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse_group)) | ||||
| 		else: | ||||
| 			projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse)) | ||||
| 
 | ||||
| 		if (reorder_level or reorder_qty) and projected_qty < reorder_level: | ||||
| 			deficiency = reorder_level - projected_qty | ||||
| @ -70,7 +73,7 @@ def _reorder_item(): | ||||
| 		if item.get("reorder_levels"): | ||||
| 			for d in item.get("reorder_levels"): | ||||
| 				add_to_material_request(item_code, d.warehouse, d.warehouse_reorder_level, | ||||
| 					d.warehouse_reorder_qty, d.material_request_type) | ||||
| 					d.warehouse_reorder_qty, d.material_request_type, warehouse_group=d.warehouse_group) | ||||
| 
 | ||||
| 	if material_requests: | ||||
| 		return create_material_request(material_requests) | ||||
| @ -82,9 +85,17 @@ def get_item_warehouse_projected_qty(items_to_consider): | ||||
| 		from tabBin where item_code in ({0}) | ||||
| 			and (warehouse != "" and warehouse is not null)"""\ | ||||
| 		.format(", ".join(["%s"] * len(items_to_consider))), items_to_consider): | ||||
| 
 | ||||
| 		 | ||||
| 		item_warehouse_projected_qty.setdefault(item_code, {})[warehouse] = flt(projected_qty) | ||||
| 
 | ||||
| 		 | ||||
| 		warehouse_doc = frappe.get_doc("Warehouse", warehouse) | ||||
| 		 | ||||
| 		if warehouse_doc.parent_warehouse: | ||||
| 			if not item_warehouse_projected_qty.get(item_code, {}).get(warehouse_doc.parent_warehouse): | ||||
| 				item_warehouse_projected_qty.setdefault(item_code, {})[warehouse_doc.parent_warehouse] = flt(projected_qty) | ||||
| 			else: | ||||
| 				item_warehouse_projected_qty[item_code][warehouse_doc.parent_warehouse] += flt(projected_qty) | ||||
| 				 | ||||
| 	return item_warehouse_projected_qty | ||||
| 
 | ||||
| def create_material_request(material_requests): | ||||
|  | ||||
| @ -71,7 +71,9 @@ def get_conditions(filters): | ||||
| 		conditions += " and item_code = '%s'" % frappe.db.escape(filters.get("item_code"), percent=False) | ||||
| 
 | ||||
| 	if filters.get("warehouse"): | ||||
| 		conditions += " and warehouse = '%s'" % frappe.db.escape(filters.get("warehouse"), percent=False) | ||||
| 		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 | ||||
| 
 | ||||
| @ -79,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 = {} | ||||
|  | ||||
| @ -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} | ||||
| @ -73,7 +73,7 @@ def get_sle_conditions(filters): | ||||
| 		conditions.append("""item_code in (select name from tabItem | ||||
| 			{item_conditions})""".format(item_conditions=item_conditions)) | ||||
| 	if filters.get("warehouse"): | ||||
| 		conditions.append("warehouse=%(warehouse)s") | ||||
| 		conditions.append(get_warehouse_condition(filters.get("warehouse"))) | ||||
| 	if filters.get("voucher_no"): | ||||
| 		conditions.append("voucher_no=%(voucher_no)s") | ||||
| 
 | ||||
| @ -86,7 +86,7 @@ def get_opening_balance(filters, columns): | ||||
| 	from erpnext.stock.stock_ledger import get_previous_sle | ||||
| 	last_entry = get_previous_sle({ | ||||
| 		"item_code": filters.item_code, | ||||
| 		"warehouse": filters.warehouse, | ||||
| 		"warehouse": get_warehouse_condition(filters.warehouse), | ||||
| 		"posting_date": filters.from_date, | ||||
| 		"posting_time": "00:00:00" | ||||
| 	}) | ||||
| @ -96,4 +96,11 @@ def get_opening_balance(filters, columns): | ||||
| 	for i, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')): | ||||
| 			row[i] = last_entry.get(v, 0) | ||||
| 		 | ||||
| 	return row | ||||
| 	return row | ||||
| 	 | ||||
| def get_warehouse_condition(warehouse): | ||||
| 	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) | ||||
| 	 | ||||
| @ -57,16 +57,20 @@ def get_data(filters): | ||||
| 	return data | ||||
| 
 | ||||
| def get_bin_list(filters): | ||||
| 	bin_filters = frappe._dict() | ||||
| 	conditions = [] | ||||
| 	 | ||||
| 	if filters.item_code: | ||||
| 		bin_filters.item_code = filters.item_code | ||||
| 		conditions.append("item_code = '%s' "%filters.item_code) | ||||
| 		 | ||||
| 	if filters.warehouse: | ||||
| 		bin_filters.warehouse = filters.warehouse | ||||
| 		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.get_all("Bin", fields=["item_code", "warehouse", | ||||
| 		"actual_qty", "planned_qty", "indented_qty", "ordered_qty", "reserved_qty", | ||||
| 		"reserved_qty_for_production", "projected_qty"], | ||||
| 		filters=bin_filters, order_by="item_code, warehouse") | ||||
| 	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 | ||||
| 		from tabBin where %s order by item_code, warehouse """% " and ".join(conditions), as_dict=1,debug=1) | ||||
| 
 | ||||
| 	return bin_list | ||||
| 
 | ||||
|  | ||||
| @ -15,24 +15,34 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None): | ||||
| 	values, condition = [posting_date], "" | ||||
| 
 | ||||
| 	if warehouse: | ||||
| 		values.append(warehouse) | ||||
| 		condition += " AND warehouse = %s" | ||||
| 		 | ||||
| 		lft, rgt, is_group = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt", "is_group"]) | ||||
| 		 | ||||
| 		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)" | ||||
| 		 | ||||
| 		else: | ||||
| 			values.append(warehouse) | ||||
| 			condition += " AND warehouse = %s" | ||||
| 
 | ||||
| 	if item_code: | ||||
| 		values.append(item_code) | ||||
| 		condition.append(" AND item_code = %s") | ||||
| 
 | ||||
| 	stock_ledger_entries = frappe.db.sql(""" | ||||
| 		SELECT item_code, stock_value | ||||
| 		FROM `tabStock Ledger Entry` | ||||
| 		SELECT item_code, stock_value, name, warehouse | ||||
| 		FROM `tabStock Ledger Entry` sle | ||||
| 		WHERE posting_date <= %s {0} | ||||
| 		ORDER BY timestamp(posting_date, posting_time) DESC, name DESC | ||||
| 	""".format(condition), values, as_dict=1) | ||||
| 
 | ||||
| 	sle_map = {} | ||||
| 	for sle in stock_ledger_entries: | ||||
| 		sle_map.setdefault(sle.item_code, flt(sle.stock_value)) | ||||
| 
 | ||||
| 		sle_map[sle.item_code] = sle_map.get(sle.item_code, 0.0) + flt(sle.stock_value) | ||||
| 		 | ||||
| 	return sum(sle_map.values()) | ||||
| 
 | ||||
| def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None, with_valuation_rate=False): | ||||
| @ -177,3 +187,8 @@ def validate_warehouse_company(warehouse, company): | ||||
| 	if warehouse_company and warehouse_company != company: | ||||
| 		frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company), | ||||
| 			InvalidWarehouseCompany) | ||||
| 
 | ||||
| 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")) | ||||
| 	 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user