diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 809e3529c8..25dd3988f1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -322,9 +322,9 @@ "read_only": 0 }, { - "fieldname": "sales_bom_help", + "fieldname": "product_bundle_help", "fieldtype": "HTML", - "label": "Sales BOM Help", + "label": "Product Bundle Help", "permlevel": 0, "print_hide": 1, "read_only": 0 @@ -1252,7 +1252,7 @@ ], "icon": "icon-file-text", "idx": 1, - "is_submittable": 1, + "is_submittable": 1, "modified": "2015-07-09 17:33:28.583808", "modified_by": "Administrator", "module": "Accounts", diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 9b688109d9..8153912304 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -86,23 +86,23 @@ class GrossProfitGenerator(object): self.filters = frappe._dict(filters) self.load_invoice_items() self.load_stock_ledger_entries() - self.load_sales_bom() + self.load_product_bundle() self.load_non_stock_items() self.process() def process(self): self.grouped = {} for row in self.si_list: - if self.skip_row(row, self.sales_boms): + if self.skip_row(row, self.product_bundles): continue row.base_amount = flt(row.base_net_amount) - sales_boms = self.sales_boms.get(row.parenttype, {}).get(row.parent, frappe._dict()) + product_bundles = self.product_bundles.get(row.parenttype, {}).get(row.parent, frappe._dict()) # get buying amount - if row.item_code in sales_boms: - row.buying_amount = self.get_buying_amount_from_sales_bom(row, sales_boms[row.item_code]) + if row.item_code in product_bundles: + row.buying_amount = self.get_buying_amount_from_product_bundle(row, product_bundles[row.item_code]) else: row.buying_amount = self.get_buying_amount(row, row.item_code) @@ -152,13 +152,13 @@ class GrossProfitGenerator(object): self.grouped_data.append(new_row) - def skip_row(self, row, sales_boms): + def skip_row(self, row, product_bundles): if self.filters.get("group_by") != "Invoice" and not row.get(scrub(self.filters.get("group_by"))): return True - def get_buying_amount_from_sales_bom(self, row, sales_bom): + def get_buying_amount_from_product_bundle(self, row, product_bundle): buying_amount = 0.0 - for bom_item in sales_bom: + for bom_item in product_bundle: if bom_item.get("parent_detail_docname")==row.item_row: buying_amount += self.get_buying_amount(row, bom_item.item_code) @@ -246,13 +246,13 @@ class GrossProfitGenerator(object): self.sle[(r.item_code, r.warehouse)].append(r) - def load_sales_bom(self): - self.sales_boms = {} + def load_product_bundle(self): + self.product_bundles = {} for d in frappe.db.sql("""select parenttype, parent, parent_item, item_code, warehouse, -1*qty as total_qty, parent_detail_docname from `tabPacked Item` where docstatus=1""", as_dict=True): - self.sales_boms.setdefault(d.parenttype, frappe._dict()).setdefault(d.parent, + self.product_bundles.setdefault(d.parenttype, frappe._dict()).setdefault(d.parent, frappe._dict()).setdefault(d.parent_item, []).append(d) def load_non_stock_items(self): diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index 78c73d8822..543396462d 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -144,7 +144,7 @@ def get_data(): }, { "type": "doctype", - "name": "Sales BOM", + "name": "Product Bundle", "description": _("Bundle items at time of sale."), }, { diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 43f7cdd0fc..c709326461 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -171,6 +171,9 @@ class SellingController(StockController): frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx)) if self.doctype == "Sales Order": + if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes' or + self.has_product_bundle(d.item_code)) and not d.warehouse: + frappe.throw(_("Reserved Warehouse required for stock Item {0} in row {1}").format(d.item_code, d.idx)) reserved_warehouse = d.warehouse if flt(d.qty) > flt(d.delivered_qty): reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty) @@ -188,7 +191,7 @@ class SellingController(StockController): else: reserved_qty_for_main_item = -flt(d.qty) - if self.has_sales_bom(d.item_code): + if self.has_product_bundle(d.item_code): for p in self.get("packed_items"): if p.parent_detail_docname == d.name and p.parent_item == d.item_code: # the packing details table's qty is already multiplied with parent's qty @@ -218,8 +221,8 @@ class SellingController(StockController): })) return il - def has_sales_bom(self, item_code): - return frappe.db.sql("""select name from `tabSales BOM` + def has_product_bundle(self, item_code): + return frappe.db.sql("""select name from `tabProduct Bundle` where new_item_code=%s and docstatus != 2""", item_code) def get_already_delivered_qty(self, dn, so, so_detail): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e9e3f2d673..2b430a056a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -172,4 +172,5 @@ erpnext.patches.v5_0.item_variants erpnext.patches.v5_0.update_item_desc_in_invoice erpnext.patches.v5_1.fix_against_account erpnext.patches.v5_1.fix_credit_days_based_on -erpnext.patches.v5_1.track_operations \ No newline at end of file +erpnext.patches.v5_1.track_operations +erpnext.patches.v5_1.sales_bom_rename diff --git a/erpnext/patches/v5_1/sales_bom_rename.py b/erpnext/patches/v5_1/sales_bom_rename.py new file mode 100644 index 0000000000..e06012f3e4 --- /dev/null +++ b/erpnext/patches/v5_1/sales_bom_rename.py @@ -0,0 +1,12 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + tables = frappe.db.sql_list("show tables") + for old_dt, new_dt in [["Sales BOM Item", "Product Bundle Item"], + ["Sales BOM", "Product Bundle"]]: + if "tab"+new_dt not in tables: + frappe.rename_doc("DocType", old_dt, new_dt, force=True) diff --git a/erpnext/public/js/feature_setup.js b/erpnext/public/js/feature_setup.js index 664da664d3..d43ffc5d59 100644 --- a/erpnext/public/js/feature_setup.js +++ b/erpnext/public/js/feature_setup.js @@ -46,7 +46,7 @@ erpnext.feature_setup.feature_dict = { 'Purchase Invoice': {'items':['brand']}, 'Quotation': {'items':['brand']}, 'Sales Invoice': {'items':['brand']}, - 'Sales BOM': {'fields':['new_item_brand']}, + 'Product Bundle': {'fields':['new_item_brand']}, 'Sales Order': {'items':['brand']}, 'Serial No': {'fields':['brand']} }, @@ -95,7 +95,7 @@ erpnext.feature_setup.feature_dict = { 'Purchase Voucher': {'items':['item_group']}, 'Quotation': {'items':['item_group']}, 'Sales Invoice': {'items':['item_group']}, - 'Sales BOM': {'fields':['serial_no']}, + 'Product Bundle': {'fields':['serial_no']}, 'Sales Order': {'items':['item_group']}, 'Serial No': {'fields':['item_group']}, 'Sales Partner': {'targets':['item_group']}, @@ -129,7 +129,7 @@ erpnext.feature_setup.feature_dict = { 'base_total', 'base_net_total', 'base_discount_amount', 'base_total_taxes_and_charges'], 'items': ['base_price_list_rate','base_amount','base_rate', 'base_net_rate', 'base_net_amount'] }, - 'Sales BOM': {'fields':['currency']}, + 'Product Bundle': {'fields':['currency']}, 'Sales Order': { 'fields': ['conversion_rate','currency','base_grand_total','base_in_words','base_rounded_total', 'base_total', 'base_net_total', 'base_discount_amount', 'base_total_taxes_and_charges'], diff --git a/erpnext/selling/doctype/product_bundle/__init__.py b/erpnext/selling/doctype/product_bundle/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/selling/doctype/sales_bom/sales_bom.js b/erpnext/selling/doctype/product_bundle/product_bundle.js similarity index 82% rename from erpnext/selling/doctype/sales_bom/sales_bom.js rename to erpnext/selling/doctype/product_bundle/product_bundle.js index 0b64a5f6d1..2849be5bf3 100644 --- a/erpnext/selling/doctype/sales_bom/sales_bom.js +++ b/erpnext/selling/doctype/product_bundle/product_bundle.js @@ -7,10 +7,10 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.fields_dict.new_item_code.get_query = function() { return{ - query: "erpnext.selling.doctype.sales_bom.sales_bom.get_new_item_code" + query: "erpnext.selling.doctype.product_bundle.product_bundle.get_new_item_code" } } -cur_frm.fields_dict.new_item_code.query_description = __('Please select Item where "Is Stock Item" is "No" and "Is Sales Item" is "Yes" and there is no other Sales BOM'); +cur_frm.fields_dict.new_item_code.query_description = __('Please select Item where "Is Stock Item" is "No" and "Is Sales Item" is "Yes" and there is no other Product Bundle'); cur_frm.cscript.item_code = function(doc, dt, dn) { var d = locals[dt][dn]; diff --git a/erpnext/selling/doctype/sales_bom/sales_bom.json b/erpnext/selling/doctype/product_bundle/product_bundle.json similarity index 92% rename from erpnext/selling/doctype/sales_bom/sales_bom.json rename to erpnext/selling/doctype/product_bundle/product_bundle.json index bf366f7007..c998b23c12 100644 --- a/erpnext/selling/doctype/sales_bom/sales_bom.json +++ b/erpnext/selling/doctype/product_bundle/product_bundle.json @@ -1,7 +1,7 @@ { "allow_import": 1, "creation": "2013-06-20 11:53:21", - "description": "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have \"Is Stock Item\" as \"No\" and \"Is Sales Item\" as \"Yes\".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Sales BOM Item.\n\nNote: BOM = Bill of Materials", + "description": "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have \"Is Stock Item\" as \"No\" and \"Is Sales Item\" as \"Yes\".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials", "docstatus": 0, "doctype": "DocType", "document_type": "Master", @@ -38,7 +38,7 @@ "label": "Items", "oldfieldname": "sales_bom_items", "oldfieldtype": "Table", - "options": "Sales BOM Item", + "options": "Product Bundle Item", "permlevel": 0, "reqd": 1 } @@ -46,10 +46,10 @@ "icon": "icon-sitemap", "idx": 1, "is_submittable": 0, - "modified": "2015-02-20 05:05:03.719573", + "modified": "2015-07-06 06:11:10.534423", "modified_by": "Administrator", "module": "Selling", - "name": "Sales BOM", + "name": "Product Bundle", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/selling/doctype/sales_bom/sales_bom.py b/erpnext/selling/doctype/product_bundle/product_bundle.py similarity index 93% rename from erpnext/selling/doctype/sales_bom/sales_bom.py rename to erpnext/selling/doctype/product_bundle/product_bundle.py index ddb955f508..796b4b43ac 100644 --- a/erpnext/selling/doctype/sales_bom/sales_bom.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -8,9 +8,7 @@ from frappe import _ from frappe.model.document import Document -class SalesBOM(Document): - - +class ProductBundle(Document): def autoname(self): self.name = self.new_item_code @@ -39,7 +37,7 @@ def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select name, item_name, description from tabItem where is_stock_item="No" and is_sales_item="Yes" - and name not in (select name from `tabSales BOM`) and %s like %s + and name not in (select name from `tabProduct Bundle`) and %s like %s %s limit %s, %s""" % (searchfield, "%s", get_match_cond(doctype),"%s", "%s"), ("%%%s%%" % txt, start, page_len)) diff --git a/erpnext/selling/doctype/sales_bom/test_sales_bom.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py similarity index 76% rename from erpnext/selling/doctype/sales_bom/test_sales_bom.py rename to erpnext/selling/doctype/product_bundle/test_product_bundle.py index ad9e22becc..8c5fe12e1b 100644 --- a/erpnext/selling/doctype/sales_bom/test_sales_bom.py +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py @@ -1,7 +1,8 @@ + # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Sales BOM') +test_records = frappe.get_test_records('Product Bundle') \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_bom/test_records.json b/erpnext/selling/doctype/product_bundle/test_records.json similarity index 57% rename from erpnext/selling/doctype/sales_bom/test_records.json rename to erpnext/selling/doctype/product_bundle/test_records.json index a19bc1a2e4..b92892f1bf 100644 --- a/erpnext/selling/doctype/sales_bom/test_records.json +++ b/erpnext/selling/doctype/product_bundle/test_records.json @@ -1,16 +1,16 @@ [ { - "doctype": "Sales BOM", - "new_item_code": "_Test Sales BOM Item", + "doctype": "Product Bundle", + "new_item_code": "_Test Product Bundle Item", "items": [ { - "doctype": "Sales BOM Item", + "doctype": "Product Bundle Item", "item_code": "_Test Item", "parentfield": "items", "qty": 5.0 }, { - "doctype": "Sales BOM Item", + "doctype": "Product Bundle Item", "item_code": "_Test Item Home Desktop 100", "parentfield": "items", "qty": 2.0 diff --git a/erpnext/selling/doctype/product_bundle_item/__init__.py b/erpnext/selling/doctype/product_bundle_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/selling/doctype/sales_bom_item/sales_bom_item.json b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json similarity index 88% rename from erpnext/selling/doctype/sales_bom_item/sales_bom_item.json rename to erpnext/selling/doctype/product_bundle_item/product_bundle_item.json index c48debdd8c..d219125344 100644 --- a/erpnext/selling/doctype/sales_bom_item/sales_bom_item.json +++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json @@ -1,5 +1,5 @@ { - "creation": "2013-05-23 16:55:51.000000", + "creation": "2013-05-23 16:55:51", "docstatus": 0, "doctype": "DocType", "fields": [ @@ -60,9 +60,10 @@ ], "idx": 1, "istable": 1, - "modified": "2013-12-20 19:21:38.000000", + "modified": "2015-07-06 06:05:18.854360", "modified_by": "Administrator", "module": "Selling", - "name": "Sales BOM Item", - "owner": "Administrator" + "name": "Product Bundle Item", + "owner": "Administrator", + "permissions": [] } \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_bom_item/sales_bom_item.py b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py similarity index 54% rename from erpnext/selling/doctype/sales_bom_item/sales_bom_item.py rename to erpnext/selling/doctype/product_bundle_item/product_bundle_item.py index 27d515b333..8721bfad86 100644 --- a/erpnext/selling/doctype/sales_bom_item/sales_bom_item.py +++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.py @@ -1,10 +1,10 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt from __future__ import unicode_literals import frappe - from frappe.model.document import Document -class SalesBOMItem(Document): - pass \ No newline at end of file +class ProductBundleItem(Document): + pass diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 7496429314..3f30d05b7d 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -6,7 +6,7 @@ import frappe, json from frappe.utils import flt import unittest -test_dependencies = ["Sales BOM"] +test_dependencies = ["Product Bundle"] class TestQuotation(unittest.TestCase): def test_make_sales_order(self): diff --git a/erpnext/selling/doctype/sales_bom/__init__.py b/erpnext/selling/doctype/sales_bom/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/selling/doctype/sales_bom/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/selling/doctype/sales_bom_item/__init__.py b/erpnext/selling/doctype/sales_bom_item/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/selling/doctype/sales_bom_item/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 0565341f8a..91daa3f1cb 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -125,7 +125,7 @@ class TestSalesOrder(unittest.TestCase): existing_reserved_qty_item1 = get_reserved_qty("_Test Item") existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100") - so = make_sales_order(item_code="_Test Sales BOM Item") + so = make_sales_order(item_code="_Test Product Bundle Item") self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 50) self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), @@ -164,12 +164,12 @@ class TestSalesOrder(unittest.TestCase): def test_reserved_qty_for_over_delivery_with_packing_list(self): # set over-delivery tolerance - frappe.db.set_value('Item', "_Test Sales BOM Item", 'tolerance', 50) + frappe.db.set_value('Item', "_Test Product Bundle Item", 'tolerance', 50) existing_reserved_qty_item1 = get_reserved_qty("_Test Item") existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100") - so = make_sales_order(item_code="_Test Sales BOM Item") + so = make_sales_order(item_code="_Test Product Bundle Item") self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 50) self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), diff --git a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.json b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.json index 30b5d8478c..52da750a82 100644 --- a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.json +++ b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.json @@ -5,12 +5,12 @@ "doctype": "Report", "idx": 1, "is_standard": "Yes", - "modified": "2014-06-03 07:18:16.914298", + "modified": "2015-07-06 06:11:10.534423", "modified_by": "Administrator", "module": "Selling", "name": "Available Stock for Packing Items", "owner": "Administrator", - "ref_doctype": "Sales BOM", + "ref_doctype": "Product Bundle", "report_name": "Available Stock for Packing Items", "report_type": "Script Report" } \ No newline at end of file diff --git a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py index f1633e7c6e..40ed20c93d 100644 --- a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py +++ b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.py @@ -39,9 +39,9 @@ def get_columns(): return columns -def get_sales_bom_items(): +def get_product_bundle_items(): sbom_item_map = {} - for sbom in frappe.db.sql("""select parent, item_code, qty from `tabSales BOM Item` + for sbom in frappe.db.sql("""select parent, item_code, qty from `tabProduct Bundle Item` where docstatus < 2""", as_dict=1): sbom_item_map.setdefault(sbom.parent, {}).setdefault(sbom.item_code, sbom.qty) @@ -67,7 +67,7 @@ def get_item_warehouse_quantity(): def get_item_warehouse_quantity_map(): sbom_map = {} iwq_map = get_item_warehouse_quantity() - sbom_item_map = get_sales_bom_items() + sbom_item_map = get_product_bundle_items() for sbom, sbom_items in sbom_item_map.items(): for item, child_qty in sbom_items.items(): diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index cd65d1890b..88ef61e29d 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -295,27 +295,27 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ set_dynamic_labels: function() { this._super(); - this.set_sales_bom_help(this.frm.doc); + this.set_product_bundle_help(this.frm.doc); }, - set_sales_bom_help: function(doc) { + set_product_bundle_help: function(doc) { if(!cur_frm.fields_dict.packing_list) return; if ((doc.packed_items || []).length) { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { help_msg = "