[enhance] scrap management
This commit is contained in:
		
							parent
							
								
									464c27e189
								
							
						
					
					
						commit
						5a2fa8aac7
					
				| @ -143,8 +143,6 @@ erpnext.bom.calculate_sm_cost = function(doc) { | |||||||
| 	for(var i=0;i<sm.length;i++) { | 	for(var i=0;i<sm.length;i++) { | ||||||
| 		amt =	flt(sm[i].rate) * flt(sm[i].qty); | 		amt =	flt(sm[i].rate) * flt(sm[i].qty); | ||||||
| 		set_multiple('BOM Scrap Item',sm[i].name, {'amount': amt}, 'scrap_items'); | 		set_multiple('BOM Scrap Item',sm[i].name, {'amount': amt}, 'scrap_items'); | ||||||
| 		set_multiple('BOM Scrap Item',sm[i].name, |  | ||||||
| 			{'qty_consumed_per_unit': flt(sm[i].qty)/flt(doc.quantity)}, 'scrap_items'); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -425,6 +425,48 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1): | |||||||
| 
 | 
 | ||||||
| 	return item_dict | 	return item_dict | ||||||
| 
 | 
 | ||||||
|  | def get_bom_scrap_items_as_dict(bom, company, qty=1, fetch_exploded=1): | ||||||
|  | 	item_dict = {} | ||||||
|  | 
 | ||||||
|  | 	# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss | ||||||
|  | 	query = """select | ||||||
|  | 				bom_scrap_item.item_code, | ||||||
|  | 				item.item_name, | ||||||
|  | 				sum(bom_scrap_item.qty/ifnull(bom.quantity, 1)) * %(qty)s as qty, | ||||||
|  | 				item.description, | ||||||
|  | 				item.image, | ||||||
|  | 				item.stock_uom, | ||||||
|  | 				item.default_warehouse, | ||||||
|  | 				item.expense_account as expense_account, | ||||||
|  | 				item.buying_cost_center as cost_center | ||||||
|  | 			from | ||||||
|  | 				`tabBOM Scrap Item` bom_scrap_item, `tabBOM` bom, `tabItem` item | ||||||
|  | 			where | ||||||
|  | 				bom_scrap_item.parent = bom.name | ||||||
|  | 				and bom_scrap_item.docstatus < 2 | ||||||
|  | 				and bom_scrap_item.parent = %(bom)s | ||||||
|  | 				and item.name = bom_scrap_item.item_code | ||||||
|  | 				and is_stock_item = 1 | ||||||
|  | 				group by item_code, stock_uom""" | ||||||
|  | 
 | ||||||
|  | 	items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True) | ||||||
|  | 
 | ||||||
|  | 	# make unique | ||||||
|  | 	for item in items: | ||||||
|  | 		if item_dict.has_key(item.item_code): | ||||||
|  | 			item_dict[item.item_code]["qty"] += flt(item.qty) | ||||||
|  | 		else: | ||||||
|  | 			item_dict[item.item_code] = item | ||||||
|  | 
 | ||||||
|  | 	for item, item_details in item_dict.items(): | ||||||
|  | 		for d in [["Account", "expense_account", "default_expense_account"], | ||||||
|  | 			["Cost Center", "cost_center", "cost_center"], ["Warehouse", "default_warehouse", ""]]: | ||||||
|  | 				company_in_record = frappe.db.get_value(d[0], item_details.get(d[1]), "company") | ||||||
|  | 				if not item_details.get(d[1]) or (company_in_record and company != company_in_record): | ||||||
|  | 					item_dict[item][d[1]] = frappe.db.get_value("Company", company, d[2]) if d[2] else None | ||||||
|  | 
 | ||||||
|  | 	return item_dict | ||||||
|  | 
 | ||||||
| @frappe.whitelist() | @frappe.whitelist() | ||||||
| def get_bom_items(bom, company, qty=1, fetch_exploded=1): | def get_bom_items(bom, company, qty=1, fetch_exploded=1): | ||||||
| 	items = get_bom_items_as_dict(bom, company, qty, fetch_exploded).values() | 	items = get_bom_items_as_dict(bom, company, qty, fetch_exploded).values() | ||||||
|  | |||||||
| @ -451,6 +451,33 @@ | |||||||
|    "set_only_once": 0,  |    "set_only_once": 0,  | ||||||
|    "unique": 0 |    "unique": 0 | ||||||
|   },  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "scrap_warehouse",  | ||||||
|  |    "fieldtype": "Link",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "label": "Scrap 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": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|   { |   { | ||||||
|    "allow_on_submit": 0,  |    "allow_on_submit": 0,  | ||||||
|    "bold": 0,  |    "bold": 0,  | ||||||
| @ -1133,7 +1160,7 @@ | |||||||
|  "issingle": 0,  |  "issingle": 0,  | ||||||
|  "istable": 0,  |  "istable": 0,  | ||||||
|  "max_attachments": 0,  |  "max_attachments": 0,  | ||||||
|  "modified": "2016-09-19 02:48:09.412858",  |  "modified": "2016-09-26 07:01:12.863755",  | ||||||
|  "modified_by": "Administrator",  |  "modified_by": "Administrator",  | ||||||
|  "module": "Manufacturing",  |  "module": "Manufacturing",  | ||||||
|  "name": "Production Order",  |  "name": "Production Order",  | ||||||
|  | |||||||
| @ -148,7 +148,7 @@ class StockEntry(StockController): | |||||||
| 						if not d.t_warehouse: | 						if not d.t_warehouse: | ||||||
| 							frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) | 							frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) | ||||||
| 
 | 
 | ||||||
| 						elif self.pro_doc and cstr(d.t_warehouse) != self.pro_doc.fg_warehouse: | 						elif self.pro_doc and (cstr(d.t_warehouse) != self.pro_doc.fg_warehouse and cstr(d.t_warehouse) != self.pro_doc.scrap_warehouse): | ||||||
| 							frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx)) | 							frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx)) | ||||||
| 
 | 
 | ||||||
| 					else: | 					else: | ||||||
| @ -348,14 +348,14 @@ class StockEntry(StockController): | |||||||
| 
 | 
 | ||||||
| 	def validate_bom(self): | 	def validate_bom(self): | ||||||
| 		for d in self.get('items'): | 		for d in self.get('items'): | ||||||
| 			if d.bom_no: | 			if d.bom_no and (d.t_warehouse != self.pro_doc.scrap_warehouse): | ||||||
| 				validate_bom_no(d.item_code, d.bom_no) | 				validate_bom_no(d.item_code, d.bom_no) | ||||||
| 
 | 
 | ||||||
| 	def validate_finished_goods(self): | 	def validate_finished_goods(self): | ||||||
| 		"""validation: finished good quantity should be same as manufacturing quantity""" | 		"""validation: finished good quantity should be same as manufacturing quantity""" | ||||||
| 		items_with_target_warehouse = [] | 		items_with_target_warehouse = [] | ||||||
| 		for d in self.get('items'): | 		for d in self.get('items'): | ||||||
| 			if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty): | 			if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty) and (d.t_warehouse != self.pro_doc.scrap_warehouse): | ||||||
| 				frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ | 				frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ | ||||||
| 					format(d.idx, d.transfer_qty, self.fg_completed_qty)) | 					format(d.idx, d.transfer_qty, self.fg_completed_qty)) | ||||||
| 
 | 
 | ||||||
| @ -557,7 +557,15 @@ class StockEntry(StockController): | |||||||
| 							item["from_warehouse"] = self.pro_doc.wip_warehouse | 							item["from_warehouse"] = self.pro_doc.wip_warehouse | ||||||
| 
 | 
 | ||||||
| 						item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else "" | 						item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else "" | ||||||
|  | 					 | ||||||
| 					self.add_to_stock_entry_detail(item_dict) | 					self.add_to_stock_entry_detail(item_dict) | ||||||
|  | 
 | ||||||
|  | 					scrap_item_dict = self.get_bom_scrap_material(self.fg_completed_qty) | ||||||
|  | 					for item in scrap_item_dict.values(): | ||||||
|  | 						if self.pro_doc and self.pro_doc.scrap_warehouse: | ||||||
|  | 							item["to_warehouse"] = self.pro_doc.scrap_warehouse | ||||||
|  | 					self.add_to_stock_entry_detail(scrap_item_dict, bom_no=self.bom_no) | ||||||
|  | 					 | ||||||
| 			# fetch the serial_no of the first stock entry for the second stock entry | 			# fetch the serial_no of the first stock entry for the second stock entry | ||||||
| 			if self.production_order and self.purpose == "Manufacture": | 			if self.production_order and self.purpose == "Manufacture": | ||||||
| 				self.set_serial_nos(self.production_order) | 				self.set_serial_nos(self.production_order) | ||||||
| @ -607,7 +615,18 @@ class StockEntry(StockController): | |||||||
| 		for item in item_dict.values(): | 		for item in item_dict.values(): | ||||||
| 			item.from_warehouse = self.from_warehouse or item.default_warehouse | 			item.from_warehouse = self.from_warehouse or item.default_warehouse | ||||||
| 		return item_dict | 		return item_dict | ||||||
|  | 	 | ||||||
|  | 	def get_bom_scrap_material(self, qty): | ||||||
|  | 		from erpnext.manufacturing.doctype.bom.bom import get_bom_scrap_items_as_dict | ||||||
|  | 		 | ||||||
|  | 		# item dict = { item_code: {qty, description, stock_uom} } | ||||||
|  | 		item_dict = get_bom_scrap_items_as_dict(self.bom_no, self.company, qty=qty, | ||||||
|  | 			fetch_exploded = self.use_multi_level_bom) | ||||||
| 
 | 
 | ||||||
|  | 		for item in item_dict.values(): | ||||||
|  | 			item.from_warehouse = "" | ||||||
|  | 		return item_dict | ||||||
|  | 	 | ||||||
| 	def get_transfered_raw_materials(self): | 	def get_transfered_raw_materials(self): | ||||||
| 		transferred_materials = frappe.db.sql(""" | 		transferred_materials = frappe.db.sql(""" | ||||||
| 			select | 			select | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user