Merge pull request #6553 from saurabh6790/scrap_management
Scrap management
This commit is contained in:
commit
a02fb1541a
@ -83,8 +83,10 @@ var get_bom_material_detail= function(doc, cdt, cdn) {
|
||||
d = locals[cdt][cdn];
|
||||
$.extend(d, r.message);
|
||||
refresh_field("items");
|
||||
refresh_field("scrap_items");
|
||||
doc = locals[doc.doctype][doc.name];
|
||||
erpnext.bom.calculate_rm_cost(doc);
|
||||
erpnext.bom.calculate_scrap_materials_cost(doc);
|
||||
erpnext.bom.calculate_total(doc);
|
||||
},
|
||||
freeze: true
|
||||
@ -120,6 +122,7 @@ erpnext.bom.calculate_op_cost = function(doc) {
|
||||
refresh_field('operating_cost');
|
||||
}
|
||||
|
||||
// rm : raw material
|
||||
erpnext.bom.calculate_rm_cost = function(doc) {
|
||||
var rm = doc.items || [];
|
||||
total_rm_cost = 0;
|
||||
@ -133,6 +136,15 @@ erpnext.bom.calculate_rm_cost = function(doc) {
|
||||
cur_frm.set_value("raw_material_cost", total_rm_cost);
|
||||
}
|
||||
|
||||
//sm : scrap material
|
||||
erpnext.bom.calculate_scrap_materials_cost = function(doc) {
|
||||
var sm = doc.scrap_items || [];
|
||||
total_sm_cost = 0;
|
||||
for(var i=0;i<sm.length;i++) {
|
||||
amt = flt(sm[i].rate) * flt(sm[i].qty);
|
||||
set_multiple('BOM Scrap Item',sm[i].name, {'amount': amt}, 'scrap_items');
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate Total Cost
|
||||
erpnext.bom.calculate_total = function(doc) {
|
||||
@ -176,6 +188,7 @@ cur_frm.fields_dict['items'].grid.get_field('bom_no').get_query = function(doc,
|
||||
cur_frm.cscript.validate = function(doc, dt, dn) {
|
||||
erpnext.bom.calculate_op_cost(doc);
|
||||
erpnext.bom.calculate_rm_cost(doc);
|
||||
erpnext.bom.calculate_scrap_materials_cost(doc);
|
||||
erpnext.bom.calculate_total(doc);
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,13 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Item to be manufactured or repacked",
|
||||
"fieldname": "item",
|
||||
"fieldtype": "Link",
|
||||
@ -41,6 +43,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@ -66,6 +69,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"description": "Quantity of item obtained after manufacturing / repacking from given quantities of raw materials",
|
||||
"fieldname": "quantity",
|
||||
@ -94,6 +98,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "cb0",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -117,6 +122,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fieldname": "is_active",
|
||||
"fieldtype": "Check",
|
||||
@ -144,6 +150,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fieldname": "is_default",
|
||||
"fieldtype": "Check",
|
||||
@ -171,6 +178,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Manage cost of operations",
|
||||
"fieldname": "with_operations",
|
||||
"fieldtype": "Check",
|
||||
@ -196,6 +204,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "rm_cost_as_per",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -221,6 +230,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.rm_cost_as_per===\"Price List\"",
|
||||
"fieldname": "buying_price_list",
|
||||
"fieldtype": "Link",
|
||||
@ -247,6 +257,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"description": "Specify the operations, operating cost and give a unique Operation no to your operations.",
|
||||
"fieldname": "operations_section",
|
||||
@ -274,6 +285,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operations",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
@ -301,6 +313,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "materials_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -326,6 +339,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
@ -349,10 +363,64 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "scrap_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Scrap",
|
||||
"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,
|
||||
"columns": 0,
|
||||
"fieldname": "scrap_items",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Scrap Items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "BOM Scrap Item",
|
||||
"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,
|
||||
"columns": 0,
|
||||
"fieldname": "costing",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -378,6 +446,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "operating_cost",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -403,6 +472,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "raw_material_cost",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -428,6 +498,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "cb1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -451,6 +522,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "total_cost",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -476,6 +548,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "more_info_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -500,6 +573,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -527,6 +601,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -553,6 +628,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -578,6 +654,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "col_break23",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -601,6 +678,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -626,6 +704,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_25",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@ -650,6 +729,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
@ -657,7 +737,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Item Description",
|
||||
"label": "Item Desription",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -674,6 +754,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_27",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -698,6 +779,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "image",
|
||||
"fieldtype": "Attach",
|
||||
"hidden": 0,
|
||||
@ -723,6 +805,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "image_view",
|
||||
"fieldtype": "Image",
|
||||
"hidden": 0,
|
||||
@ -749,6 +832,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "section_break0",
|
||||
"fieldtype": "Section Break",
|
||||
@ -774,6 +858,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "exploded_items",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
@ -802,13 +887,14 @@
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-sitemap",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-05-13 18:28:53.557967",
|
||||
"modified": "2016-10-24 11:26:08.751123",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM",
|
||||
@ -824,6 +910,7 @@
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
@ -844,6 +931,7 @@
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
|
@ -74,14 +74,13 @@ class BOM(Document):
|
||||
return item
|
||||
|
||||
def validate_rm_item(self, item):
|
||||
if item[0]['name'] == self.item:
|
||||
if (item[0]['name'] in [it.item_code for it in self.items]) and item[0]['name'] == self.item:
|
||||
frappe.throw(_("Raw material cannot be same as main Item"))
|
||||
|
||||
def set_bom_material_details(self):
|
||||
for item in self.get("items"):
|
||||
ret = self.get_bom_material_detail({"item_code": item.item_code, "item_name": item.item_name, "bom_no": item.bom_no,
|
||||
"qty": item.qty})
|
||||
|
||||
for r in ret:
|
||||
if not item.get(r):
|
||||
item.set(r, ret[r])
|
||||
@ -103,12 +102,12 @@ class BOM(Document):
|
||||
|
||||
rate = self.get_rm_rate(args)
|
||||
ret_item = {
|
||||
'item_name' : item and args['item_name'] or '',
|
||||
'description' : item and args['description'] or '',
|
||||
'image' : item and args['image'] or '',
|
||||
'stock_uom' : item and args['stock_uom'] or '',
|
||||
'bom_no' : args['bom_no'],
|
||||
'rate' : rate
|
||||
'item_name' : item and args['item_name'] or '',
|
||||
'description' : item and args['description'] or '',
|
||||
'image' : item and args['image'] or '',
|
||||
'stock_uom' : item and args['stock_uom'] or '',
|
||||
'bom_no' : args['bom_no'],
|
||||
'rate' : rate
|
||||
}
|
||||
return ret_item
|
||||
|
||||
@ -377,7 +376,7 @@ class BOM(Document):
|
||||
if not d.description:
|
||||
d.description = frappe.db.get_value('Operation', d.operation, 'description')
|
||||
|
||||
def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1):
|
||||
def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_items=0):
|
||||
item_dict = {}
|
||||
|
||||
# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
|
||||
@ -406,11 +405,13 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1):
|
||||
query = query.format(table="BOM Explosion Item",
|
||||
conditions="""and item.is_sub_contracted_item = 0""")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
|
||||
elif fetch_scrap_items:
|
||||
query = query.format(table="BOM Scrap Item", conditions="")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
|
||||
else:
|
||||
query = query.format(table="BOM Item", conditions="")
|
||||
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)
|
||||
|
@ -28,9 +28,20 @@
|
||||
"quantity": 1.0
|
||||
},
|
||||
{
|
||||
"scrap_items":[
|
||||
{
|
||||
"amount": 2000.0,
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"parentfield": "items",
|
||||
"qty": 1.0,
|
||||
"rate": 2000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
}
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"amount": 5000.0,
|
||||
"amount": 10000.0,
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "items",
|
||||
@ -81,7 +92,7 @@
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop Manufactured",
|
||||
"parentfield": "items",
|
||||
"qty": 2.0,
|
||||
"qty": 3.0,
|
||||
"rate": 1000.0,
|
||||
"stock_uom": "_Test UOM"
|
||||
}
|
||||
|
248
erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
Normal file
248
erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json
Normal file
@ -0,0 +1,248 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-09-26 02:19:21.642081",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Item Code",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Item",
|
||||
"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,
|
||||
"columns": 0,
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Item Name",
|
||||
"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,
|
||||
"columns": 0,
|
||||
"fieldname": "quantity_and_rate",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Quantity and Rate",
|
||||
"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,
|
||||
"columns": 0,
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Qty",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"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,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Stock UOM",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "UOM",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-09-26 02:58:58.433348",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Scrap Item",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_seen": 0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class BOMScrapItem(Document):
|
||||
pass
|
@ -225,6 +225,10 @@ $.extend(cur_frm.cscript, {
|
||||
$.each(["description", "stock_uom", "bom_no"], function(i, field) {
|
||||
cur_frm.set_value(field, r.message[field]);
|
||||
});
|
||||
|
||||
if(r.message["set_scrap_wh_mandatory"]){
|
||||
cur_frm.toggle_reqd("scrap_warehouse", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -260,7 +264,12 @@ $.extend(cur_frm.cscript, {
|
||||
bom_no: function() {
|
||||
return this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: "set_production_order_operations"
|
||||
method: "set_production_order_operations",
|
||||
callback: function(r) {
|
||||
if(r.message["set_scrap_wh_mandatory"]){
|
||||
cur_frm.toggle_reqd("scrap_warehouse", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -451,6 +451,33 @@
|
||||
"set_only_once": 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,
|
||||
"bold": 0,
|
||||
@ -1133,7 +1160,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-09-19 02:48:09.412858",
|
||||
"modified": "2016-09-26 07:01:12.863755",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Order",
|
||||
|
@ -222,6 +222,8 @@ class ProductionOrder(Document):
|
||||
self.set('operations', operations)
|
||||
self.calculate_time()
|
||||
|
||||
return check_if_scrap_warehouse_mandatory(self.bom_no)
|
||||
|
||||
def calculate_time(self):
|
||||
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
|
||||
|
||||
@ -457,11 +459,25 @@ def get_item_details(item):
|
||||
return {}
|
||||
|
||||
res = res[0]
|
||||
|
||||
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": item, "is_default": 1})
|
||||
if not res["bom_no"]:
|
||||
variant_of= frappe.db.get_value("Item", item, "variant_of")
|
||||
if variant_of:
|
||||
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1})
|
||||
|
||||
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
|
||||
|
||||
return res
|
||||
|
||||
@frappe.whitelist()
|
||||
def check_if_scrap_warehouse_mandatory(bom_no):
|
||||
res = {"set_scrap_wh_mandatory": False }
|
||||
bom = frappe.get_doc("BOM", bom_no)
|
||||
|
||||
if len(bom.scrap_items) > 0:
|
||||
res["set_scrap_wh_mandatory"] = True
|
||||
|
||||
return res
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -233,6 +233,45 @@ class TestProductionOrder(unittest.TestCase):
|
||||
self.pro_order.reload()
|
||||
self.assertEqual(len(self.pro_order.required_items), 0)
|
||||
|
||||
def test_scrap_material_qty(self):
|
||||
prod_order = make_prod_order_test_record(planned_start_date=now(), qty=2)
|
||||
|
||||
# add raw materials to stores
|
||||
test_stock_entry.make_stock_entry(item_code="_Test Item",
|
||||
target="Stores - _TC", qty=10, basic_rate=5000.0)
|
||||
test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100",
|
||||
target="Stores - _TC", qty=10, basic_rate=1000.0)
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(prod_order.name, "Material Transfer for Manufacture", 2))
|
||||
for d in s.get("items"):
|
||||
d.s_warehouse = "Stores - _TC"
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(prod_order.name, "Manufacture", 2))
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
prod_order_details = frappe.db.get_value("Production Order", prod_order.name,
|
||||
["scrap_warehouse", "qty", "produced_qty", "bom_no"], as_dict=1)
|
||||
|
||||
scrap_item_details = get_scrap_item_details(prod_order_details.bom_no)
|
||||
|
||||
self.assertEqual(prod_order_details.produced_qty, 2)
|
||||
|
||||
for item in s.items:
|
||||
if item.bom_no and item.item_code in scrap_item_details:
|
||||
self.assertEqual(prod_order_details.scrap_warehouse, item.t_warehouse)
|
||||
self.assertEqual(flt(prod_order_details.qty)*flt(scrap_item_details[item.item_code]), item.qty)
|
||||
|
||||
def get_scrap_item_details(bom_no):
|
||||
scrap_items = {}
|
||||
for item in frappe.db.sql("""select item_code, qty from `tabBOM Scrap Item`
|
||||
where parent = %s""", bom_no, as_dict=1):
|
||||
scrap_items[item.item_code] = item.qty
|
||||
|
||||
return scrap_items
|
||||
|
||||
def make_prod_order_test_record(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@ -243,6 +282,7 @@ def make_prod_order_test_record(**args):
|
||||
pro_order.qty = args.qty or 10
|
||||
pro_order.wip_warehouse = args.wip_warehouse or "_Test Warehouse - _TC"
|
||||
pro_order.fg_warehouse = args.fg_warehouse or "_Test Warehouse 1 - _TC"
|
||||
pro_order.scrap_warehouse = args.fg_warehouse or "_Test Scrap Warehouse - _TC"
|
||||
pro_order.company = args.company or "_Test Company"
|
||||
pro_order.stock_uom = args.stock_uom or "_Test UOM"
|
||||
pro_order.set_production_order_operations()
|
||||
|
@ -148,7 +148,7 @@ class StockEntry(StockController):
|
||||
if not d.t_warehouse:
|
||||
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))
|
||||
|
||||
else:
|
||||
@ -238,6 +238,7 @@ class StockEntry(StockController):
|
||||
d.serial_no = transferred_serial_no
|
||||
|
||||
def get_stock_and_rate(self):
|
||||
self.set_production_order_details()
|
||||
self.set_transfer_qty()
|
||||
self.set_actual_qty()
|
||||
self.calculate_rate_and_amount()
|
||||
@ -252,6 +253,7 @@ class StockEntry(StockController):
|
||||
def set_basic_rate(self, force=False, update_finished_item_rate=True):
|
||||
"""get stock and incoming rate on posting date"""
|
||||
raw_material_cost = 0.0
|
||||
scrap_material_cost = 0.0
|
||||
fg_basic_rate = 0.0
|
||||
|
||||
for d in self.get('items'):
|
||||
@ -276,16 +278,28 @@ class StockEntry(StockController):
|
||||
if not d.t_warehouse:
|
||||
raw_material_cost += flt(d.basic_amount)
|
||||
|
||||
# get scrap items basic rate
|
||||
if d.bom_no:
|
||||
if not flt(d.basic_rate) and getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
|
||||
basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
|
||||
if basic_rate > 0:
|
||||
d.basic_rate = basic_rate
|
||||
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
|
||||
|
||||
if getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
|
||||
|
||||
scrap_material_cost += flt(d.basic_amount)
|
||||
|
||||
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
||||
if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate:
|
||||
self.set_basic_rate_for_finished_goods(raw_material_cost)
|
||||
self.set_basic_rate_for_finished_goods(raw_material_cost, scrap_material_cost)
|
||||
|
||||
def set_basic_rate_for_finished_goods(self, raw_material_cost):
|
||||
def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost):
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
for d in self.get("items"):
|
||||
if d.bom_no or d.t_warehouse:
|
||||
d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate"))
|
||||
d.basic_amount = flt(raw_material_cost, d.precision("basic_amount"))
|
||||
if (d.bom_no or d.t_warehouse) and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse):
|
||||
d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
|
||||
d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
|
||||
|
||||
def distribute_additional_costs(self):
|
||||
if self.purpose == "Material Issue":
|
||||
@ -302,7 +316,7 @@ class StockEntry(StockController):
|
||||
|
||||
def update_valuation_rate(self):
|
||||
for d in self.get("items"):
|
||||
d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount"))
|
||||
d.amount = flt(flt(d.basic_amount) + flt(d.additional_cost), d.precision("amount"))
|
||||
d.valuation_rate = flt(
|
||||
flt(d.basic_rate)
|
||||
+ (flt(d.additional_cost) / flt(d.transfer_qty)),
|
||||
@ -348,14 +362,14 @@ class StockEntry(StockController):
|
||||
|
||||
def validate_bom(self):
|
||||
for d in self.get('items'):
|
||||
if d.bom_no:
|
||||
if d.bom_no and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
|
||||
validate_bom_no(d.item_code, d.bom_no)
|
||||
|
||||
def validate_finished_goods(self):
|
||||
"""validation: finished good quantity should be same as manufacturing quantity"""
|
||||
items_with_target_warehouse = []
|
||||
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 != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
|
||||
frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
|
||||
format(d.idx, d.transfer_qty, self.fg_completed_qty))
|
||||
|
||||
@ -519,19 +533,7 @@ class StockEntry(StockController):
|
||||
if not self.posting_date or not self.posting_time:
|
||||
frappe.throw(_("Posting date and posting time is mandatory"))
|
||||
|
||||
if not getattr(self, "pro_doc", None):
|
||||
self.pro_doc = None
|
||||
|
||||
if self.production_order:
|
||||
# common validations
|
||||
if not self.pro_doc:
|
||||
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
|
||||
|
||||
if self.pro_doc:
|
||||
self.bom_no = self.pro_doc.bom_no
|
||||
else:
|
||||
# invalid production order
|
||||
self.production_order = None
|
||||
self.set_production_order_details()
|
||||
|
||||
if self.bom_no:
|
||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
|
||||
@ -557,7 +559,15 @@ class StockEntry(StockController):
|
||||
item["from_warehouse"] = self.pro_doc.wip_warehouse
|
||||
|
||||
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
|
||||
|
||||
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
|
||||
if self.production_order and self.purpose == "Manufacture":
|
||||
self.set_serial_nos(self.production_order)
|
||||
@ -569,6 +579,21 @@ class StockEntry(StockController):
|
||||
self.set_actual_qty()
|
||||
self.calculate_rate_and_amount()
|
||||
|
||||
def set_production_order_details(self):
|
||||
if not getattr(self, "pro_doc", None):
|
||||
self.pro_doc = None
|
||||
|
||||
if self.production_order:
|
||||
# common validations
|
||||
if not self.pro_doc:
|
||||
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
|
||||
|
||||
if self.pro_doc:
|
||||
self.bom_no = self.pro_doc.bom_no
|
||||
else:
|
||||
# invalid production order
|
||||
self.production_order = None
|
||||
|
||||
def load_items_from_bom(self):
|
||||
if self.production_order:
|
||||
item_code = self.pro_doc.production_item
|
||||
@ -607,7 +632,18 @@ class StockEntry(StockController):
|
||||
for item in item_dict.values():
|
||||
item.from_warehouse = self.from_warehouse or item.default_warehouse
|
||||
return item_dict
|
||||
|
||||
def get_bom_scrap_material(self, qty):
|
||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
||||
|
||||
# item dict = { item_code: {qty, description, stock_uom} }
|
||||
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
|
||||
fetch_exploded = 0, fetch_scrap_items = 1)
|
||||
|
||||
for item in item_dict.values():
|
||||
item.from_warehouse = ""
|
||||
return item_dict
|
||||
|
||||
def get_transfered_raw_materials(self):
|
||||
transferred_materials = frappe.db.sql("""
|
||||
select
|
||||
|
@ -6,6 +6,13 @@
|
||||
"warehouse_name": "_Test Warehouse",
|
||||
"is_group": 0
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"create_account_under": "Stock Assets - _TC",
|
||||
"doctype": "Warehouse",
|
||||
"warehouse_name": "_Test Scrap Warehouse",
|
||||
"is_group": 0
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"create_account_under": "Stock Assets - _TC",
|
||||
|
Loading…
Reference in New Issue
Block a user