From 0f0b1216699e7ea279f1eb220e506172d95bc50c Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Tue, 23 May 2017 15:15:17 -0700 Subject: [PATCH 01/23] Added BOM UOM selection for items Added patch for BOM Item UOM Fixed scrap qty issue Added Scrap Qty update to patch Reverted test record for production order --- erpnext/controllers/buying_controller.py | 2 +- erpnext/manufacturing/doctype/bom/bom.js | 17 ++ erpnext/manufacturing/doctype/bom/bom.py | 39 ++- .../doctype/bom/test_records.json | 16 +- .../doctype/bom_item/bom_item.json | 260 +++++++++++++----- .../bom_replace_tool/bom_replace_tool.py | 2 +- .../bom_scrap_item/bom_scrap_item.json | 49 +++- .../production_planning_tool.py | 2 +- .../test_production_planning_tool.py | 26 +- .../production_order_stock_report.py | 2 +- erpnext/patches.txt | 3 +- .../update_stock_qty_value_in_bom_item.py | 10 + 12 files changed, 325 insertions(+), 103 deletions(-) create mode 100644 erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 5bc8bb38f8..3e35cdd471 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -252,7 +252,7 @@ class BuyingController(StockController): def get_items_from_bom(self, item_code, bom): bom_items = frappe.db.sql("""select t2.item_code, - t2.qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit, + t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit, t2.rate, t2.stock_uom, t2.name, t2.description from `tabBOM` t1, `tabBOM Item` t2, tabItem t3 where t2.parent = t1.name and t1.item = %s diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 576e46df50..42970803af 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -70,6 +70,15 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ get_bom_material_detail(doc, cdt, cdn, scrap_items); }, + conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { + if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + var item = frappe.get_doc(cdt, cdn); + frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); + item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); + refresh_field("stock_qty", item.name, item.parentfield); + this.toggle_conversion_factor(item); + } + }, }) $.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm})); @@ -296,6 +305,14 @@ frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) { }) }); +frappe.ui.form.on("BOM Item", "qty", function(frm, cdt, cdn) { + var d = locals[cdt][cdn]; + + d.stock_qty = d.qty * d.conversion_factor + refresh_field("items"); + }); + + frappe.ui.form.on("BOM Operation", "operations_remove", function(frm) { erpnext.bom.calculate_op_cost(frm.doc); erpnext.bom.calculate_total(frm.doc); diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b8a8ae8aea..5c45e5d5b6 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -7,6 +7,7 @@ from frappe.utils import cint, cstr, flt from frappe import _ from erpnext.setup.utils import get_exchange_rate from frappe.website.website_generator import WebsiteGenerator +from erpnext.stock.get_item_details import get_conversion_factor from operator import itemgetter @@ -48,7 +49,7 @@ class BOM(WebsiteGenerator): self.set_conversion_rate() from erpnext.utilities.transaction_base import validate_uom_is_integer - validate_uom_is_integer(self, "stock_uom", "qty", "BOM Item") + validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item") self.validate_materials() self.set_bom_material_details() @@ -60,6 +61,7 @@ class BOM(WebsiteGenerator): def on_update(self): self.check_recursion() + self.update_stock_qty() self.update_exploded_items() def on_submit(self): @@ -94,7 +96,7 @@ class BOM(WebsiteGenerator): 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}) + "stock_qty": item.stock_qty}) for r in ret: if not item.get(r): item.set(r, ret[r]) @@ -122,6 +124,8 @@ class BOM(WebsiteGenerator): 'description' : item and args['description'] or '', 'image' : item and args['image'] or '', 'stock_uom' : item and args['stock_uom'] or '', + 'uom' : item and args['stock_uom'] or '', + 'conversion_factor' : 1, 'bom_no' : args['bom_no'], 'rate' : rate, 'base_rate' : rate if self.company_currency() == self.currency else rate * self.conversion_rate @@ -159,7 +163,7 @@ class BOM(WebsiteGenerator): for d in self.get("items"): rate = self.get_bom_material_detail({'item_code': d.item_code, 'bom_no': d.bom_no, - 'qty': d.qty})["rate"] + 'stock_qty': d.stock_qty})["rate"] if rate: d.rate = rate @@ -239,6 +243,19 @@ class BOM(WebsiteGenerator): frappe.db.get_value('Price List', self.buying_price_list, 'currency') != self.currency: frappe.throw(_("Currency of the price list {0} is not similar with the selected currency {1}").format(self.buying_price_list, self.currency)) + + def update_stock_qty(self): + for m in self.get('items'): + + if not m.conversion_factor: + m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor']) + if m.uom and m.qty: + m.stock_qty = flt(m.conversion_factor)*flt(m.qty) + if not m.uom and m.stock_uom: + m.uom = m.stock_uom + m.qty = m.stock_qty + + def set_conversion_rate(self): self.conversion_rate = get_exchange_rate(self.currency, self.company_currency()) @@ -250,7 +267,7 @@ class BOM(WebsiteGenerator): for m in self.get('items'): if m.bom_no: validate_bom_no(m.item_code, m.bom_no) - if flt(m.qty) <= 0: + if flt(m.stock_qty) <= 0: frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx)) check_list.append(cstr(m.item_code)) unique_chk_list = set(check_list) @@ -336,9 +353,9 @@ class BOM(WebsiteGenerator): d.rate = self.get_bom_unitcost(d.bom_no) d.base_rate = flt(d.rate) * flt(self.conversion_rate) - d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.qty, self.precision("qty", d)) + d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d)) d.base_amount = d.amount * flt(self.conversion_rate) - d.qty_consumed_per_unit = flt(d.qty, self.precision("qty", d)) / flt(self.quantity, self.precision("quantity")) + d.qty_consumed_per_unit = flt(d.stock_qty, self.precision("stock_qty", d)) / flt(self.quantity, self.precision("quantity")) total_rm_cost += d.amount base_total_rm_cost += d.base_amount @@ -352,7 +369,7 @@ class BOM(WebsiteGenerator): for d in self.get('scrap_items'): d.base_rate = d.rate * self.conversion_rate - d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.qty, self.precision("qty", d)) + d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d)) d.base_amount = d.amount * self.conversion_rate total_sm_cost += d.amount base_total_sm_cost += d.base_amount @@ -370,7 +387,7 @@ class BOM(WebsiteGenerator): self.cur_exploded_items = {} for d in self.get('items'): if d.bom_no: - self.get_child_exploded_items(d.bom_no, d.qty) + self.get_child_exploded_items(d.bom_no, d.stock_qty) else: self.add_to_cur_exploded_items(frappe._dict({ 'item_code' : d.item_code, @@ -378,7 +395,7 @@ class BOM(WebsiteGenerator): 'description' : d.description, 'image' : d.image, 'stock_uom' : d.stock_uom, - 'qty' : flt(d.qty), + 'qty' : flt(d.stock_qty), 'rate' : d.base_rate, })) @@ -453,7 +470,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite query = """select bom_item.item_code, item.item_name, - sum(bom_item.qty/ifnull(bom.quantity, 1)) * %(qty)s as qty, + sum(bom_item.stock_qty/ifnull(bom.quantity, 1)) * %(qty)s as qty, item.description, item.image, item.stock_uom, @@ -521,7 +538,7 @@ def get_children(): return frappe.db.sql("""select bom_item.item_code, bom_item.bom_no as value, - bom_item.qty, + bom_item.stock_qty, if(ifnull(bom_item.bom_no, "")!="", 1, 0) as expandable, item.image, item.description diff --git a/erpnext/manufacturing/doctype/bom/test_records.json b/erpnext/manufacturing/doctype/bom/test_records.json index 5baa0cbf1d..0f1143ef7d 100644 --- a/erpnext/manufacturing/doctype/bom/test_records.json +++ b/erpnext/manufacturing/doctype/bom/test_records.json @@ -6,7 +6,7 @@ "doctype": "BOM Item", "item_code": "_Test Serialized Item With Series", "parentfield": "items", - "qty": 1.0, + "stock_qty": 1.0, "rate": 5000.0, "stock_uom": "_Test UOM" }, @@ -15,7 +15,7 @@ "doctype": "BOM Item", "item_code": "_Test Item 2", "parentfield": "items", - "qty": 2.0, + "stock_qty": 2.0, "rate": 1000.0, "stock_uom": "_Test UOM" } @@ -35,7 +35,7 @@ "doctype": "BOM Item", "item_code": "_Test Item Home Desktop 100", "parentfield": "items", - "qty": 1.0, + "stock_qty": 1.0, "rate": 2000.0, "stock_uom": "_Test UOM" } @@ -46,7 +46,7 @@ "doctype": "BOM Item", "item_code": "_Test Item", "parentfield": "items", - "qty": 1.0, + "stock_qty": 1.0, "rate": 5000.0, "stock_uom": "_Test UOM" }, @@ -55,7 +55,7 @@ "doctype": "BOM Item", "item_code": "_Test Item Home Desktop 100", "parentfield": "items", - "qty": 2.0, + "stock_qty": 2.0, "rate": 1000.0, "stock_uom": "_Test UOM" } @@ -84,7 +84,7 @@ "doctype": "BOM Item", "item_code": "_Test Item", "parentfield": "items", - "qty": 1.0, + "stock_qty": 1.0, "rate": 5000.0, "stock_uom": "_Test UOM" }, @@ -94,7 +94,7 @@ "doctype": "BOM Item", "item_code": "_Test Item Home Desktop Manufactured", "parentfield": "items", - "qty": 3.0, + "stock_qty": 3.0, "rate": 1000.0, "stock_uom": "_Test UOM" } @@ -124,7 +124,7 @@ "doctype": "BOM Item", "item_code": "_Test Item", "parentfield": "items", - "qty": 2.0, + "stock_qty": 2.0, "rate": 3000.0, "stock_uom": "_Test UOM" } diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index 2fc29a883b..56af7a10d3 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -11,6 +12,7 @@ "editable_grid": 1, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -20,7 +22,8 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, - "in_filter": 1, + "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Item Code", @@ -41,6 +44,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -51,6 +55,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Item Name", @@ -69,6 +74,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -79,6 +85,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "length": 0, @@ -96,6 +103,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -105,7 +113,8 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, - "in_filter": 1, + "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "BOM No", @@ -128,6 +137,7 @@ "width": "150px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -138,6 +148,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "length": 0, @@ -155,6 +166,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -165,6 +177,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Item Description", @@ -186,6 +199,7 @@ "width": "250px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -196,6 +210,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "length": 0, @@ -212,6 +227,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -222,6 +238,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Image", @@ -240,6 +257,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -250,6 +268,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Image View", @@ -269,6 +288,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -279,6 +299,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Quantity and Rate", @@ -296,6 +317,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -306,6 +328,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Qty", @@ -319,16 +342,168 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 1, + "fieldname": "uom", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "UOM", + "length": 0, + "no_copy": 0, + "options": "UOM", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "col_break2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Stock Qty", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "columns": 2, + "columns": 0, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Conversion Factor", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 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_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Stock UOM", + "length": 0, + "no_copy": 0, + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, "description": "See \"Rate Of Materials Based On\" in Costing Section", "fieldname": "rate", "fieldtype": "Currency", @@ -336,7 +511,8 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 1, + "in_global_search": 0, + "in_list_view": 0, "in_standard_filter": 0, "label": "Rate", "length": 0, @@ -354,6 +530,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -364,6 +541,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Amount", @@ -386,62 +564,7 @@ "width": "150px" }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 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, - "in_standard_filter": 0, - "label": "Stock UOM", - "length": 0, - "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -452,6 +575,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Basic Rate (Company Currency)", @@ -471,6 +595,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -481,6 +606,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Amount (Company Currency)", @@ -500,6 +626,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -510,6 +637,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "length": 0, @@ -527,6 +655,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -537,6 +666,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Scrap %", @@ -556,6 +686,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -566,6 +697,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Qty Consumed Per Unit", @@ -585,18 +717,18 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-12-20 12:54:34.859076", - "modified_by": "rmehta@gmail.com", + "modified": "2017-05-23 15:59:37.946963", + "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", "owner": "Administrator", @@ -604,7 +736,9 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "track_changes": 0, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py b/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py index d4d5329ace..f0a834c37b 100644 --- a/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py +++ b/erpnext/manufacturing/doctype/bom_replace_tool/bom_replace_tool.py @@ -33,7 +33,7 @@ class BOMReplaceTool(Document): from `tabBOM` where name = %s""", self.current_bom) current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0 frappe.db.sql("""update `tabBOM Item` set bom_no=%s, - rate=%s, amount=qty*%s where bom_no = %s and docstatus < 2""", + rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2""", (self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom)) def get_parent_boms(self): diff --git a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json index fe81592769..e9aebfe39c 100644 --- a/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json +++ b/erpnext/manufacturing/doctype/bom_scrap_item/bom_scrap_item.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -11,6 +12,7 @@ "editable_grid": 1, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -21,7 +23,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Item Code", "length": 0, "no_copy": 0, @@ -31,6 +35,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -38,6 +43,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -48,7 +54,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Item Name", "length": 0, "no_copy": 0, @@ -57,6 +65,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -64,6 +73,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -74,7 +84,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Quantity and Rate", "length": 0, "no_copy": 0, @@ -83,6 +95,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -90,17 +103,20 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "qty", + "fieldname": "stock_qty", "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Qty", "length": 0, "no_copy": 0, @@ -109,6 +125,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -116,6 +133,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -126,7 +144,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "Rate", "length": 0, "no_copy": 0, @@ -136,6 +156,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -143,6 +164,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -153,7 +175,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Amount", "length": 0, "no_copy": 0, @@ -163,6 +187,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -170,6 +195,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -180,7 +206,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "length": 0, "no_copy": 0, "permlevel": 0, @@ -188,6 +216,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -195,6 +224,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -205,7 +235,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Stock UOM", "length": 0, "no_copy": 0, @@ -215,6 +247,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -222,6 +255,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -232,7 +266,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Basic Rate (Company Currency)", "length": 0, "no_copy": 0, @@ -242,6 +278,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -249,6 +286,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -259,7 +297,9 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, + "in_standard_filter": 0, "label": "Basic Amount (Company Currency)", "length": 0, "no_copy": 0, @@ -269,6 +309,7 @@ "print_hide": 1, "print_hide_if_no_value": 0, "read_only": 1, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -276,17 +317,17 @@ "unique": 0 } ], + "has_web_view": 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-10-25 00:27:53.712140", + "modified": "2017-05-23 16:04:32.442287", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Scrap Item", @@ -296,7 +337,9 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "track_changes": 1, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py index dd4b2b69ab..798e17486c 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -348,7 +348,7 @@ class ProductionPlanningTool(Document): SELECT bom_item.item_code, default_material_request_type, - ifnull(%(parent_qty)s * sum(bom_item.qty/ifnull(bom.quantity, 1)), 0) as qty, + ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)), 0) as qty, item.is_sub_contracted_item as is_sub_contracted, item.default_bom as default_bom, bom_item.description as description, diff --git a/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py index ea4da0cb94..f656b2c905 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py @@ -235,9 +235,9 @@ def create_test_records(): "is_active": 1, "is_default": 1, "docstatus": 1, - "with_operations": 0}, [{"item_code": "_Test PPT Item Raw B", "doctype":"BOM Item", "qty":1, + "with_operations": 0}, [{"item_code": "_Test PPT Item Raw B", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100, "stock_uom": "_Test UOM"}, - {"item_code": "_Test PPT Item Raw C", "doctype":"BOM Item", "qty":4, "rate":100, + {"item_code": "_Test PPT Item Raw C", "doctype":"BOM Item", "stock_qty":4, "rate":100, "amount": 400,"stock_uom": "_Test UOM"}]) bom_subC = make_bom("BOM-_Test PPT Item Sub C-001",{"quantity":1, @@ -247,9 +247,9 @@ def create_test_records(): "docstatus": 1, "with_operations": 0}, [ {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", - "doctype":"BOM Item", "qty":6, "rate":100, "amount": 600}, + "doctype":"BOM Item", "stock_qty":6, "rate":100, "amount": 600}, {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", - "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "qty":2, + "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}]) bom_sCA = make_bom("BOM-_Test PPT Item SC A-001",{"quantity":1, @@ -259,7 +259,7 @@ def create_test_records(): "docstatus": 1, "with_operations": 0}, [ {"item_code": "_Test PPT Item Raw D","item_name": "_Test PPT Item Raw D", - "doctype":"BOM Item", "qty":1, "rate":100, "amount": 100}]) + "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}]) bom_sCB = make_bom("BOM-_Test PPT Item SC B-001",{"quantity":1, "item": "_Test PPT Item SC B", @@ -268,9 +268,9 @@ def create_test_records(): "docstatus": 1, "with_operations": 0}, [ {"item_code": "_Test PPT Item Raw B","item_name": "_Test PPT Item Raw B", - "doctype":"BOM Item", "qty":1, "rate":100, "amount": 100}, + "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, {"item_code": "_Test PPT Item Raw C","item_name": "_Test PPT Item Raw C", - "doctype":"BOM Item", "qty":4, "rate":100, "amount": 400}]) + "doctype":"BOM Item", "stock_qty":4, "rate":100, "amount": 400}]) bom_subA = make_bom("BOM-_Test PPT Item Sub A-001",{"quantity":1, "item": "_Test PPT Item Sub A", @@ -280,9 +280,9 @@ def create_test_records(): "with_operations": 0}, [ {"item_code": "_Test PPT Item Sub C","item_name": "_Test PPT Item Sub C", "bom_no":"BOM-_Test PPT Item Sub C-001", "doctype":"BOM Item", - "qty":1, "rate":100, "amount": 100}, + "stock_qty":1, "rate":100, "amount": 100}, {"item_code": "_Test PPT Item SC B","item_name": "_Test PPT Item SC B", - "bom_no":"BOM-_Test PPT Item SC B-001", "doctype":"BOM Item", "qty":2, + "bom_no":"BOM-_Test PPT Item SC B-001", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}]) bom_master = make_bom("BOM-_Test PPT Item Master-001",{"quantity":1, @@ -293,16 +293,16 @@ def create_test_records(): "with_operations": 0}, [ {"item_code": "_Test PPT Item Sub A","item_name": "_Test PPT Item Sub A", "bom_no":"BOM-_Test PPT Item Sub A-001", - "doctype":"BOM Item", "qty":2, "rate":100, "amount": 200}, + "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}, {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", "bom_no":"BOM-_Test PPT Item Sub B-001", - "doctype":"BOM Item", "qty":1, "rate":100, "amount": 100}, + "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", - "doctype":"BOM Item", "qty":2, "rate":100, + "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}, {"item_code": "_Test PPT Item SC A","item_name": "_Test PPT Item SC A", "bom_no":"BOM-_Test PPT Item SC A-001", - "doctype":"BOM Item", "qty":1, "rate":100, "amount": 100} + "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100} ]) diff --git a/erpnext/manufacturing/report/production_order_stock_report/production_order_stock_report.py b/erpnext/manufacturing/report/production_order_stock_report/production_order_stock_report.py index 6d586ddb22..bb79a49159 100644 --- a/erpnext/manufacturing/report/production_order_stock_report/production_order_stock_report.py +++ b/erpnext/manufacturing/report/production_order_stock_report/production_order_stock_report.py @@ -23,7 +23,7 @@ def get_item_list(prod_list, filters): item_list = frappe.db.sql("""SELECT bom_item.item_code as item_code, - ifnull(ledger.actual_qty*bom.quantity/bom_item.qty,0) as build_qty + ifnull(ledger.actual_qty*bom.quantity/bom_item.stock_qty,0) as build_qty FROM `tabBOM` as bom, `tabBOM Item` AS bom_item LEFT JOIN `tabBin` AS ledger diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0faf98e688..5d2d2f0517 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -396,4 +396,5 @@ erpnext.patches.v8_0.merge_student_batch_and_student_group erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017 erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice -erpnext.patches.v8_0.set_sales_invoice_serial_number_from_delivery_note \ No newline at end of file +erpnext.patches.v8_0.set_sales_invoice_serial_number_from_delivery_note +erpnext.patches.v8_0.update_stock_qty_value_in_bom_item \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py new file mode 100644 index 0000000000..bc69815fa2 --- /dev/null +++ b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc('manufacturing', 'doctype', 'bom_item') + frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom") + frappe.db.sql("update `tabBOM Scrap Item` set stock_qty = qty") \ No newline at end of file From a3aa6a4449b6f2a2e9ae4bcc01e41301f7521cb2 Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Fri, 2 Jun 2017 16:54:03 -0700 Subject: [PATCH 02/23] Changed Explosion Item as well --- erpnext/manufacturing/doctype/bom/bom.py | 24 +++++++++---------- .../bom_explosion_item.json | 24 +++++++++++++++---- .../doctype/bom_item/bom_item.json | 2 ++ .../production_planning_tool.py | 2 +- .../update_stock_qty_value_in_bom_item.py | 1 + 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 5c45e5d5b6..ca4a9bc125 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -395,7 +395,7 @@ class BOM(WebsiteGenerator): 'description' : d.description, 'image' : d.image, 'stock_uom' : d.stock_uom, - 'qty' : flt(d.stock_qty), + 'stock_qty' : flt(d.stock_qty), 'rate' : d.base_rate, })) @@ -404,16 +404,16 @@ class BOM(WebsiteGenerator): def add_to_cur_exploded_items(self, args): if self.cur_exploded_items.get(args.item_code): - self.cur_exploded_items[args.item_code]["qty"] += args.qty + self.cur_exploded_items[args.item_code]["stock_qty"] += args.stock_qty else: self.cur_exploded_items[args.item_code] = args - def get_child_exploded_items(self, bom_no, qty): + def get_child_exploded_items(self, bom_no, stock_qty): """ Add all items from Flat BOM of child BOM""" # Did not use qty_consumed_per_unit in the query, as it leads to rounding loss child_fb_items = frappe.db.sql("""select bom_item.item_code, bom_item.item_name, bom_item.description, - bom_item.stock_uom, bom_item.qty, bom_item.rate, - bom_item.qty / ifnull(bom.quantity, 1) as qty_consumed_per_unit + bom_item.stock_uom, bom_item.stock_qty, bom_item.rate, + bom_item.stock_qty / ifnull(bom.quantity, 1) as qty_consumed_per_unit from `tabBOM Explosion Item` bom_item, tabBOM bom where bom_item.parent = bom.name and bom.name = %s and bom.docstatus = 1""", bom_no, as_dict = 1) @@ -423,7 +423,7 @@ class BOM(WebsiteGenerator): 'item_name' : d['item_name'], 'description' : d['description'], 'stock_uom' : d['stock_uom'], - 'qty' : d['qty_consumed_per_unit']*qty, + 'stock_qty' : d['qty_consumed_per_unit']*stock_qty, 'rate' : flt(d['rate']), })) @@ -435,8 +435,8 @@ class BOM(WebsiteGenerator): ch = self.append('exploded_items', {}) for i in self.cur_exploded_items[d].keys(): ch.set(i, self.cur_exploded_items[d][i]) - ch.amount = flt(ch.qty) * flt(ch.rate) - ch.qty_consumed_per_unit = flt(ch.qty) / flt(self.quantity) + ch.amount = flt(ch.stock_qty) * flt(ch.rate) + ch.qty_consumed_per_unit = flt(ch.stock_qty) / flt(self.quantity) ch.docstatus = self.docstatus ch.db_insert() @@ -480,13 +480,13 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite from `tab{table}` bom_item, `tabBOM` bom, `tabItem` item where - bom_item.parent = bom.name - and bom_item.docstatus < 2 - and bom_item.parent = %(bom)s + bom_item.docstatus < 2 + and bom.name = %(bom)s + and bom_item.parent = bom.name and item.name = bom_item.item_code and is_stock_item = 1 {conditions} - group by item_code, stock_uom""" + group by item_code, stock_uom""" if fetch_exploded: query = query.format(table="BOM Explosion Item", diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json index f075840806..e1a3d4da53 100644 --- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json +++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "autoname": "hash", @@ -13,6 +14,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -44,6 +46,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -72,6 +75,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -101,6 +105,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -129,6 +134,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -161,6 +167,7 @@ "width": "300px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -189,6 +196,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -218,6 +226,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -248,6 +257,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -276,11 +286,12 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "qty", + "fieldname": "stock_qty", "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, @@ -289,7 +300,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Qty", + "label": "Stock Qty", "length": 0, "no_copy": 0, "oldfieldname": "qty", @@ -306,6 +317,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -337,6 +349,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -365,6 +378,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -393,6 +407,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -424,6 +439,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -455,17 +471,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-17 17:27:43.757983", + "modified": "2017-06-02 19:29:34.498719", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Explosion Item", diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index 56af7a10d3..966b89bd4c 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -424,6 +424,8 @@ "label": "Stock Qty", "length": 0, "no_copy": 0, + "oldfieldname": "stock_qty", + "oldfieldtype": "Currency", "permlevel": 0, "precision": "", "print_hide": 0, diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py index 798e17486c..050c3c1c33 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -321,7 +321,7 @@ class ProductionPlanningTool(Document): # get all raw materials with sub assembly childs # Did not use qty_consumed_per_unit in the query, as it leads to rounding loss for d in frappe.db.sql("""select fb.item_code, - ifnull(sum(fb.qty/ifnull(bom.quantity, 1)), 0) as qty, + ifnull(sum(fb.stock_qty/ifnull(bom.quantity, 1)), 0) as qty, fb.description, fb.stock_uom, item.min_order_qty from `tabBOM Explosion Item` fb, `tabBOM` bom, `tabItem` item where bom.name = fb.parent and item.name = fb.item_code diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py index bc69815fa2..9d227877ce 100644 --- a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py +++ b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py @@ -7,4 +7,5 @@ import frappe def execute(): frappe.reload_doc('manufacturing', 'doctype', 'bom_item') frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom") + frappe.db.sql("update `tabBOM Explosion Item` set stock_qty = qty") frappe.db.sql("update `tabBOM Scrap Item` set stock_qty = qty") \ No newline at end of file From 70fe968f02f9d9c482862d921ff452142115f9ba Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Sun, 4 Jun 2017 20:19:59 -0700 Subject: [PATCH 03/23] Fixed some errors --- .../doctype/production_order/test_production_order.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index 40e839323c..c736ae2555 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import unittest import frappe +from pprint import pprint from frappe.utils import flt, time_diff_in_hours, now, add_days, cint from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.manufacturing.doctype.production_order.production_order \ @@ -266,9 +267,9 @@ class TestProductionOrder(unittest.TestCase): def get_scrap_item_details(bom_no): scrap_items = {} - for item in frappe.db.sql("""select item_code, qty from `tabBOM Scrap Item` + for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item` where parent = %s""", bom_no, as_dict=1): - scrap_items[item.item_code] = item.qty + scrap_items[item.item_code] = item.stock_qty return scrap_items @@ -287,8 +288,7 @@ def make_prod_order_test_record(**args): pro_order.stock_uom = args.stock_uom or "_Test UOM" pro_order.use_multi_level_bom=0 pro_order.set_production_order_operations() - - + if args.source_warehouse: pro_order.source_warehouse = args.source_warehouse @@ -297,6 +297,7 @@ def make_prod_order_test_record(**args): if not args.do_not_save: pro_order.insert() + if not args.do_not_submit: pro_order.submit() return pro_order From a35839aa4798d04cfcb0105cdcc59279c916de90 Mon Sep 17 00:00:00 2001 From: bcornwellmott Date: Fri, 9 Jun 2017 07:36:15 -0700 Subject: [PATCH 04/23] Removing unused pprint --- .../doctype/production_order/test_production_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index c736ae2555..cdadba4825 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import unittest import frappe -from pprint import pprint from frappe.utils import flt, time_diff_in_hours, now, add_days, cint from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.manufacturing.doctype.production_order.production_order \ From fed9816213f2237590e04ee6f22ff6c0beb1bedb Mon Sep 17 00:00:00 2001 From: bcornwellmott Date: Fri, 9 Jun 2017 07:37:27 -0700 Subject: [PATCH 05/23] Removed trailing whitespace --- .../test_production_planning_tool.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py index f656b2c905..4f80b6a626 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py @@ -235,9 +235,9 @@ def create_test_records(): "is_active": 1, "is_default": 1, "docstatus": 1, - "with_operations": 0}, [{"item_code": "_Test PPT Item Raw B", "doctype":"BOM Item", "stock_qty":1, + "with_operations": 0}, [{"item_code": "_Test PPT Item Raw B", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100, "stock_uom": "_Test UOM"}, - {"item_code": "_Test PPT Item Raw C", "doctype":"BOM Item", "stock_qty":4, "rate":100, + {"item_code": "_Test PPT Item Raw C", "doctype":"BOM Item", "stock_qty":4, "rate":100, "amount": 400,"stock_uom": "_Test UOM"}]) bom_subC = make_bom("BOM-_Test PPT Item Sub C-001",{"quantity":1, @@ -249,7 +249,7 @@ def create_test_records(): {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", "doctype":"BOM Item", "stock_qty":6, "rate":100, "amount": 600}, {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", - "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "stock_qty":2, + "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}]) bom_sCA = make_bom("BOM-_Test PPT Item SC A-001",{"quantity":1, @@ -278,11 +278,11 @@ def create_test_records(): "is_default": 1, "docstatus": 1, "with_operations": 0}, [ - {"item_code": "_Test PPT Item Sub C","item_name": "_Test PPT Item Sub C", + {"item_code": "_Test PPT Item Sub C","item_name": "_Test PPT Item Sub C", "bom_no":"BOM-_Test PPT Item Sub C-001", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, {"item_code": "_Test PPT Item SC B","item_name": "_Test PPT Item SC B", - "bom_no":"BOM-_Test PPT Item SC B-001", "doctype":"BOM Item", "stock_qty":2, + "bom_no":"BOM-_Test PPT Item SC B-001", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}]) bom_master = make_bom("BOM-_Test PPT Item Master-001",{"quantity":1, @@ -298,7 +298,7 @@ def create_test_records(): "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", - "doctype":"BOM Item", "stock_qty":2, "rate":100, + "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}, {"item_code": "_Test PPT Item SC A","item_name": "_Test PPT Item SC A", "bom_no":"BOM-_Test PPT Item SC A-001", @@ -388,4 +388,4 @@ def get_requested_types(item_code): where item.item_code = %(item_code)s and item.parent = mat_req.name""", {"item_code":item_code}, as_dict=1): types.append(d.type) return types - \ No newline at end of file + From 70554465089dd6cbd904062f4264de7d33a662b8 Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Thu, 15 Jun 2017 08:52:55 -0700 Subject: [PATCH 06/23] Reload explosion and scrap items docs --- erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py index 9d227877ce..c872177e1f 100644 --- a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py +++ b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py @@ -6,6 +6,8 @@ import frappe def execute(): frappe.reload_doc('manufacturing', 'doctype', 'bom_item') + frappe.reload_doc('manufacturing', 'doctype', 'bom_explosion_item') + frappe.reload_doc('manufacturing', 'doctype', 'bom_scrap_item') frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom") frappe.db.sql("update `tabBOM Explosion Item` set stock_qty = qty") frappe.db.sql("update `tabBOM Scrap Item` set stock_qty = qty") \ No newline at end of file From 2c77165fc61a885b15bbff37756fbcaf1e9c2426 Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Fri, 16 Jun 2017 13:47:40 -0700 Subject: [PATCH 07/23] Fixed small code issues (codecy) --- erpnext/manufacturing/doctype/bom/bom.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index a6703907b2..d3c3379850 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -5,7 +5,7 @@ frappe.provide("erpnext.bom"); frappe.ui.form.on("BOM", { setup: function(frm) { - frm.add_fetch('buying_price_list', 'currency', 'currency') + frm.add_fetch('buying_price_list', 'currency', 'currency'); frm.fields_dict["items"].grid.get_field("bom_no").get_query = function(doc, cdt, cdn){ return { filters: {'currency': frm.doc.currency} @@ -306,11 +306,10 @@ frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) { }); frappe.ui.form.on("BOM Item", "qty", function(frm, cdt, cdn) { - var d = locals[cdt][cdn]; - - d.stock_qty = d.qty * d.conversion_factor - refresh_field("items"); - }); + var d = locals[cdt][cdn]; + d.stock_qty = d.qty * d.conversion_factor; + refresh_field("items"); +}); frappe.ui.form.on("BOM Operation", "operations_remove", function(frm) { From 881491cd2b2e0200ae915ae7e8a16dd9152818aa Mon Sep 17 00:00:00 2001 From: nick9822 Date: Wed, 21 Jun 2017 19:51:08 +0530 Subject: [PATCH 08/23] Removed "Asset" filter on payment account --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index cada95f2f6..e883f25780 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -224,7 +224,6 @@ cur_frm.fields_dict.cash_bank_account.get_query = function(doc) { return { filters: [ ["Account", "account_type", "in", ["Cash", "Bank"]], - ["Account", "root_type", "=", "Asset"], ["Account", "is_group", "=",0], ["Account", "company", "=", doc.company] ] From 1e96b7bbe55579dcd5973eaf63490c13139c974e Mon Sep 17 00:00:00 2001 From: Ben Cornwell-Mott Date: Wed, 21 Jun 2017 10:16:50 -0700 Subject: [PATCH 09/23] Added conversion_factor update in patch --- erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py index c872177e1f..9e95eb0c23 100644 --- a/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py +++ b/erpnext/patches/v8_0/update_stock_qty_value_in_bom_item.py @@ -8,6 +8,6 @@ def execute(): frappe.reload_doc('manufacturing', 'doctype', 'bom_item') frappe.reload_doc('manufacturing', 'doctype', 'bom_explosion_item') frappe.reload_doc('manufacturing', 'doctype', 'bom_scrap_item') - frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom") + frappe.db.sql("update `tabBOM Item` set stock_qty = qty, uom = stock_uom, conversion_factor = 1") frappe.db.sql("update `tabBOM Explosion Item` set stock_qty = qty") frappe.db.sql("update `tabBOM Scrap Item` set stock_qty = qty") \ No newline at end of file From 82e816054e094090fa0a557747bf3b0b17f41e1b Mon Sep 17 00:00:00 2001 From: nick9822 Date: Mon, 26 Jun 2017 15:29:44 +0530 Subject: [PATCH 10/23] Added report_type to payment account filter --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index e883f25780..b64e6b2faa 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -225,7 +225,8 @@ cur_frm.fields_dict.cash_bank_account.get_query = function(doc) { filters: [ ["Account", "account_type", "in", ["Cash", "Bank"]], ["Account", "is_group", "=",0], - ["Account", "company", "=", doc.company] + ["Account", "company", "=", doc.company], + ["Account", "report_type", "=", "Balance Sheet"] ] } } From 00ae424cac32b5f899b8f50b52db05010bc2ccca Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 27 Jun 2017 17:31:41 +0530 Subject: [PATCH 11/23] [regional] ability to send gst reminders to all parties --- .../buying/doctype/supplier/regional/india.js | 3 + erpnext/config/accounts.py | 4 + .../img/regional/india/gst-reminder-email.png | Bin 0 -> 111581 bytes .../img/regional/india/gst-settings.png | Bin 0 -> 122596 bytes .../regional/india/gstin-portal-update.png | Bin 0 -> 109697 bytes .../manual/en/regional/india/gst-remimders.md | 21 ++++ .../manual/en/regional/india/gst-setup.md | 67 ++++++++++++ .../user/manual/en/regional/india/index.md | 68 +----------- .../user/manual/en/regional/india/index.txt | 2 + erpnext/patches.txt | 2 +- erpnext/patches/v8_1/setup_gst_india.py | 5 +- .../regional/doctype/gst_settings/__init__.py | 0 .../doctype/gst_settings/gst_settings.js | 25 +++++ .../doctype/gst_settings/gst_settings.json | 101 ++++++++++++++++++ .../doctype/gst_settings/gst_settings.py | 98 +++++++++++++++++ erpnext/regional/india/party.js | 25 +++++ erpnext/regional/india/setup.py | 2 +- .../doctype/customer/regional/india.js | 3 + erpnext/templates/pages/regional/__init__.py | 0 .../pages/regional/india/__init__.py | 0 .../pages/regional/india/update-gstin.html | 37 +++++++ .../pages/regional/india/update_gstin.py | 40 +++++++ 22 files changed, 432 insertions(+), 71 deletions(-) create mode 100644 erpnext/buying/doctype/supplier/regional/india.js create mode 100644 erpnext/docs/assets/img/regional/india/gst-reminder-email.png create mode 100644 erpnext/docs/assets/img/regional/india/gst-settings.png create mode 100644 erpnext/docs/assets/img/regional/india/gstin-portal-update.png create mode 100644 erpnext/docs/user/manual/en/regional/india/gst-remimders.md create mode 100644 erpnext/docs/user/manual/en/regional/india/gst-setup.md create mode 100644 erpnext/docs/user/manual/en/regional/india/index.txt create mode 100644 erpnext/regional/doctype/gst_settings/__init__.py create mode 100644 erpnext/regional/doctype/gst_settings/gst_settings.js create mode 100644 erpnext/regional/doctype/gst_settings/gst_settings.json create mode 100644 erpnext/regional/doctype/gst_settings/gst_settings.py create mode 100644 erpnext/regional/india/party.js create mode 100644 erpnext/selling/doctype/customer/regional/india.js create mode 100644 erpnext/templates/pages/regional/__init__.py create mode 100644 erpnext/templates/pages/regional/india/__init__.py create mode 100644 erpnext/templates/pages/regional/india/update-gstin.html create mode 100644 erpnext/templates/pages/regional/india/update_gstin.py diff --git a/erpnext/buying/doctype/supplier/regional/india.js b/erpnext/buying/doctype/supplier/regional/india.js new file mode 100644 index 0000000000..bd710e0df7 --- /dev/null +++ b/erpnext/buying/doctype/supplier/regional/india.js @@ -0,0 +1,3 @@ +{% include "erpnext/regional/india/party.js" %} + +erpnext.setup_gst_reminder_button('Supplier'); \ No newline at end of file diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index a17679e116..6d16e9202e 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -201,6 +201,10 @@ def get_data(): { "label": _("Goods and Services Tax (GST India)"), "items": [ + { + "type": "doctype", + "name": "GST Settings", + }, { "type": "doctype", "name": "GST HSN Code", diff --git a/erpnext/docs/assets/img/regional/india/gst-reminder-email.png b/erpnext/docs/assets/img/regional/india/gst-reminder-email.png new file mode 100644 index 0000000000000000000000000000000000000000..97bcd2488b81b6a79bbf11863c5fd152710e4011 GIT binary patch literal 111581 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