Sales BOM renamed to Product Bundle
This commit is contained in:
parent
c70109d0c5
commit
ed8cecbdd8
@ -322,9 +322,9 @@
|
|||||||
"read_only": 0
|
"read_only": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sales_bom_help",
|
"fieldname": "product_bundle_help",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"label": "Sales BOM Help",
|
"label": "Product Bundle Help",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
|
@ -86,23 +86,23 @@ class GrossProfitGenerator(object):
|
|||||||
self.filters = frappe._dict(filters)
|
self.filters = frappe._dict(filters)
|
||||||
self.load_invoice_items()
|
self.load_invoice_items()
|
||||||
self.load_stock_ledger_entries()
|
self.load_stock_ledger_entries()
|
||||||
self.load_sales_bom()
|
self.load_product_bundle()
|
||||||
self.load_non_stock_items()
|
self.load_non_stock_items()
|
||||||
self.process()
|
self.process()
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
self.grouped = {}
|
self.grouped = {}
|
||||||
for row in self.si_list:
|
for row in self.si_list:
|
||||||
if self.skip_row(row, self.sales_boms):
|
if self.skip_row(row, self.product_bundles):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row.base_amount = flt(row.base_net_amount)
|
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
|
# get buying amount
|
||||||
if row.item_code in sales_boms:
|
if row.item_code in product_bundles:
|
||||||
row.buying_amount = self.get_buying_amount_from_sales_bom(row, sales_boms[row.item_code])
|
row.buying_amount = self.get_buying_amount_from_product_bundle(row, product_bundles[row.item_code])
|
||||||
else:
|
else:
|
||||||
row.buying_amount = self.get_buying_amount(row, row.item_code)
|
row.buying_amount = self.get_buying_amount(row, row.item_code)
|
||||||
|
|
||||||
@ -152,13 +152,13 @@ class GrossProfitGenerator(object):
|
|||||||
|
|
||||||
self.grouped_data.append(new_row)
|
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"))):
|
if self.filters.get("group_by") != "Invoice" and not row.get(scrub(self.filters.get("group_by"))):
|
||||||
return True
|
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
|
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:
|
if bom_item.get("parent_detail_docname")==row.item_row:
|
||||||
buying_amount += self.get_buying_amount(row, bom_item.item_code)
|
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)
|
self.sle[(r.item_code, r.warehouse)].append(r)
|
||||||
|
|
||||||
def load_sales_bom(self):
|
def load_product_bundle(self):
|
||||||
self.sales_boms = {}
|
self.product_bundles = {}
|
||||||
|
|
||||||
for d in frappe.db.sql("""select parenttype, parent, parent_item,
|
for d in frappe.db.sql("""select parenttype, parent, parent_item,
|
||||||
item_code, warehouse, -1*qty as total_qty, parent_detail_docname
|
item_code, warehouse, -1*qty as total_qty, parent_detail_docname
|
||||||
from `tabPacked Item` where docstatus=1""", as_dict=True):
|
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)
|
frappe._dict()).setdefault(d.parent_item, []).append(d)
|
||||||
|
|
||||||
def load_non_stock_items(self):
|
def load_non_stock_items(self):
|
||||||
|
@ -144,7 +144,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Sales BOM",
|
"name": "Product Bundle",
|
||||||
"description": _("Bundle items at time of sale."),
|
"description": _("Bundle items at time of sale."),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -171,6 +171,9 @@ class SellingController(StockController):
|
|||||||
frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx))
|
frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx))
|
||||||
|
|
||||||
if self.doctype == "Sales Order":
|
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
|
reserved_warehouse = d.warehouse
|
||||||
if flt(d.qty) > flt(d.delivered_qty):
|
if flt(d.qty) > flt(d.delivered_qty):
|
||||||
reserved_qty_for_main_item = 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:
|
else:
|
||||||
reserved_qty_for_main_item = -flt(d.qty)
|
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"):
|
for p in self.get("packed_items"):
|
||||||
if p.parent_detail_docname == d.name and p.parent_item == d.item_code:
|
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
|
# the packing details table's qty is already multiplied with parent's qty
|
||||||
@ -218,8 +221,8 @@ class SellingController(StockController):
|
|||||||
}))
|
}))
|
||||||
return il
|
return il
|
||||||
|
|
||||||
def has_sales_bom(self, item_code):
|
def has_product_bundle(self, item_code):
|
||||||
return frappe.db.sql("""select name from `tabSales BOM`
|
return frappe.db.sql("""select name from `tabProduct Bundle`
|
||||||
where new_item_code=%s and docstatus != 2""", item_code)
|
where new_item_code=%s and docstatus != 2""", item_code)
|
||||||
|
|
||||||
def get_already_delivered_qty(self, dn, so, so_detail):
|
def get_already_delivered_qty(self, dn, so, so_detail):
|
||||||
|
@ -173,3 +173,4 @@ erpnext.patches.v5_0.update_item_desc_in_invoice
|
|||||||
erpnext.patches.v5_1.fix_against_account
|
erpnext.patches.v5_1.fix_against_account
|
||||||
erpnext.patches.v5_1.fix_credit_days_based_on
|
erpnext.patches.v5_1.fix_credit_days_based_on
|
||||||
erpnext.patches.v5_1.track_operations
|
erpnext.patches.v5_1.track_operations
|
||||||
|
erpnext.patches.v5_1.sales_bom_rename
|
||||||
|
12
erpnext/patches/v5_1/sales_bom_rename.py
Normal file
12
erpnext/patches/v5_1/sales_bom_rename.py
Normal file
@ -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)
|
@ -46,7 +46,7 @@ erpnext.feature_setup.feature_dict = {
|
|||||||
'Purchase Invoice': {'items':['brand']},
|
'Purchase Invoice': {'items':['brand']},
|
||||||
'Quotation': {'items':['brand']},
|
'Quotation': {'items':['brand']},
|
||||||
'Sales Invoice': {'items':['brand']},
|
'Sales Invoice': {'items':['brand']},
|
||||||
'Sales BOM': {'fields':['new_item_brand']},
|
'Product Bundle': {'fields':['new_item_brand']},
|
||||||
'Sales Order': {'items':['brand']},
|
'Sales Order': {'items':['brand']},
|
||||||
'Serial No': {'fields':['brand']}
|
'Serial No': {'fields':['brand']}
|
||||||
},
|
},
|
||||||
@ -95,7 +95,7 @@ erpnext.feature_setup.feature_dict = {
|
|||||||
'Purchase Voucher': {'items':['item_group']},
|
'Purchase Voucher': {'items':['item_group']},
|
||||||
'Quotation': {'items':['item_group']},
|
'Quotation': {'items':['item_group']},
|
||||||
'Sales Invoice': {'items':['item_group']},
|
'Sales Invoice': {'items':['item_group']},
|
||||||
'Sales BOM': {'fields':['serial_no']},
|
'Product Bundle': {'fields':['serial_no']},
|
||||||
'Sales Order': {'items':['item_group']},
|
'Sales Order': {'items':['item_group']},
|
||||||
'Serial No': {'fields':['item_group']},
|
'Serial No': {'fields':['item_group']},
|
||||||
'Sales Partner': {'targets':['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'],
|
'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']
|
'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': {
|
'Sales Order': {
|
||||||
'fields': ['conversion_rate','currency','base_grand_total','base_in_words','base_rounded_total',
|
'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'],
|
'base_total', 'base_net_total', 'base_discount_amount', 'base_total_taxes_and_charges'],
|
||||||
|
0
erpnext/selling/doctype/product_bundle/__init__.py
Normal file
0
erpnext/selling/doctype/product_bundle/__init__.py
Normal file
@ -7,10 +7,10 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
|||||||
|
|
||||||
cur_frm.fields_dict.new_item_code.get_query = function() {
|
cur_frm.fields_dict.new_item_code.get_query = function() {
|
||||||
return{
|
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) {
|
cur_frm.cscript.item_code = function(doc, dt, dn) {
|
||||||
var d = locals[dt][dn];
|
var d = locals[dt][dn];
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"creation": "2013-06-20 11:53:21",
|
"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,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Master",
|
"document_type": "Master",
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"label": "Items",
|
"label": "Items",
|
||||||
"oldfieldname": "sales_bom_items",
|
"oldfieldname": "sales_bom_items",
|
||||||
"oldfieldtype": "Table",
|
"oldfieldtype": "Table",
|
||||||
"options": "Sales BOM Item",
|
"options": "Product Bundle Item",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
@ -46,10 +46,10 @@
|
|||||||
"icon": "icon-sitemap",
|
"icon": "icon-sitemap",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"modified": "2015-02-20 05:05:03.719573",
|
"modified": "2015-07-06 06:11:10.534423",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales BOM",
|
"name": "Product Bundle",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
@ -8,9 +8,7 @@ from frappe import _
|
|||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SalesBOM(Document):
|
class ProductBundle(Document):
|
||||||
|
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.name = self.new_item_code
|
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
|
return frappe.db.sql("""select name, item_name, description from tabItem
|
||||||
where is_stock_item="No" and is_sales_item="Yes"
|
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",
|
%s limit %s, %s""" % (searchfield, "%s",
|
||||||
get_match_cond(doctype),"%s", "%s"),
|
get_match_cond(doctype),"%s", "%s"),
|
||||||
("%%%s%%" % txt, start, page_len))
|
("%%%s%%" % txt, start, page_len))
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
test_records = frappe.get_test_records('Sales BOM')
|
test_records = frappe.get_test_records('Product Bundle')
|
@ -1,16 +1,16 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"doctype": "Sales BOM",
|
"doctype": "Product Bundle",
|
||||||
"new_item_code": "_Test Sales BOM Item",
|
"new_item_code": "_Test Product Bundle Item",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"doctype": "Sales BOM Item",
|
"doctype": "Product Bundle Item",
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 5.0
|
"qty": 5.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"doctype": "Sales BOM Item",
|
"doctype": "Product Bundle Item",
|
||||||
"item_code": "_Test Item Home Desktop 100",
|
"item_code": "_Test Item Home Desktop 100",
|
||||||
"parentfield": "items",
|
"parentfield": "items",
|
||||||
"qty": 2.0
|
"qty": 2.0
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"creation": "2013-05-23 16:55:51.000000",
|
"creation": "2013-05-23 16:55:51",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -60,9 +60,10 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2013-12-20 19:21:38.000000",
|
"modified": "2015-07-06 06:05:18.854360",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales BOM Item",
|
"name": "Product Bundle Item",
|
||||||
"owner": "Administrator"
|
"owner": "Administrator",
|
||||||
|
"permissions": []
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# -*- coding: utf-8 -*-
|
||||||
# License: GNU General Public License v3. See license.txt
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SalesBOMItem(Document):
|
class ProductBundleItem(Document):
|
||||||
pass
|
pass
|
@ -6,7 +6,7 @@ import frappe, json
|
|||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
test_dependencies = ["Sales BOM"]
|
test_dependencies = ["Product Bundle"]
|
||||||
|
|
||||||
class TestQuotation(unittest.TestCase):
|
class TestQuotation(unittest.TestCase):
|
||||||
def test_make_sales_order(self):
|
def test_make_sales_order(self):
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
@ -1 +0,0 @@
|
|||||||
from __future__ import unicode_literals
|
|
@ -125,7 +125,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
|
existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
|
||||||
existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
|
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"), existing_reserved_qty_item1 + 50)
|
||||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
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):
|
def test_reserved_qty_for_over_delivery_with_packing_list(self):
|
||||||
# set over-delivery tolerance
|
# 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_item1 = get_reserved_qty("_Test Item")
|
||||||
existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
|
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"), existing_reserved_qty_item1 + 50)
|
||||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2014-06-03 07:18:16.914298",
|
"modified": "2015-07-06 06:11:10.534423",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Available Stock for Packing Items",
|
"name": "Available Stock for Packing Items",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"ref_doctype": "Sales BOM",
|
"ref_doctype": "Product Bundle",
|
||||||
"report_name": "Available Stock for Packing Items",
|
"report_name": "Available Stock for Packing Items",
|
||||||
"report_type": "Script Report"
|
"report_type": "Script Report"
|
||||||
}
|
}
|
@ -39,9 +39,9 @@ def get_columns():
|
|||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_sales_bom_items():
|
def get_product_bundle_items():
|
||||||
sbom_item_map = {}
|
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):
|
where docstatus < 2""", as_dict=1):
|
||||||
sbom_item_map.setdefault(sbom.parent, {}).setdefault(sbom.item_code, sbom.qty)
|
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():
|
def get_item_warehouse_quantity_map():
|
||||||
sbom_map = {}
|
sbom_map = {}
|
||||||
iwq_map = get_item_warehouse_quantity()
|
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 sbom, sbom_items in sbom_item_map.items():
|
||||||
for item, child_qty in sbom_items.items():
|
for item, child_qty in sbom_items.items():
|
||||||
|
@ -295,27 +295,27 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
set_dynamic_labels: function() {
|
set_dynamic_labels: function() {
|
||||||
this._super();
|
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(!cur_frm.fields_dict.packing_list) return;
|
||||||
if ((doc.packed_items || []).length) {
|
if ((doc.packed_items || []).length) {
|
||||||
$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true);
|
$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true);
|
||||||
|
|
||||||
if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
|
if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
|
||||||
help_msg = "<div class='alert alert-warning'>" +
|
help_msg = "<div class='alert alert-warning'>" +
|
||||||
__("For 'Sales BOM' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Sales BOM' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+
|
__("For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+
|
||||||
"</div>";
|
"</div>";
|
||||||
frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = help_msg;
|
frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = help_msg;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false);
|
$(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false);
|
||||||
if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
|
if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
|
||||||
frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = '';
|
frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refresh_field('sales_bom_help');
|
refresh_field('product_bundle_help');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"permlevel": 0
|
"permlevel": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "To track brand name in the following documents Delivery Note, Opportunity, Material Request, Item, Purchase Order, Purchase Voucher, Purchaser Receipt, Quotation, Sales Invoice, Sales BOM, Sales Order, Serial No",
|
"description": "To track brand name in the following documents Delivery Note, Opportunity, Material Request, Item, Purchase Order, Purchase Voucher, Purchaser Receipt, Quotation, Sales Invoice, Product Bundle, Sales Order, Serial No",
|
||||||
"fieldname": "fs_brands",
|
"fieldname": "fs_brands",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -205,7 +205,7 @@
|
|||||||
"icon": "icon-glass",
|
"icon": "icon-glass",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2015-02-05 05:11:38.842809",
|
"modified": "2015-07-06 06:11:10.534423",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Features Setup",
|
"name": "Features Setup",
|
||||||
|
@ -331,9 +331,9 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "sales_bom_help",
|
"fieldname": "product_bundle_help",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"label": "Sales BOM Help",
|
"label": "Product Bundle Help",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0
|
"read_only": 0
|
||||||
@ -1070,7 +1070,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2015-06-15 15:37:54.699371",
|
"modified": "2015-07-07 02:37:08.064584",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note",
|
"name": "Delivery Note",
|
||||||
|
@ -112,7 +112,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
|
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
|
||||||
prev_bal = get_balance_on(stock_in_hand_account)
|
prev_bal = get_balance_on(stock_in_hand_account)
|
||||||
|
|
||||||
dn = create_delivery_note(item_code="_Test Sales BOM Item")
|
dn = create_delivery_note(item_code="_Test Product Bundle Item")
|
||||||
|
|
||||||
stock_value_diff_rm1 = abs(frappe.db.get_value("Stock Ledger Entry",
|
stock_value_diff_rm1 = abs(frappe.db.get_value("Stock Ledger Entry",
|
||||||
{"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"},
|
{"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"},
|
||||||
@ -208,4 +208,4 @@ def create_delivery_note(**args):
|
|||||||
dn.submit()
|
dn.submit()
|
||||||
return dn
|
return dn
|
||||||
|
|
||||||
test_dependencies = ["Sales BOM"]
|
test_dependencies = ["Product Bundle"]
|
||||||
|
@ -109,7 +109,7 @@
|
|||||||
"stock_uom": "_Test UOM 1"
|
"stock_uom": "_Test UOM 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "_Test Sales BOM Item 5",
|
"description": "_Test Product Bundle Item 5",
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
@ -124,9 +124,9 @@
|
|||||||
"is_service_item": "No",
|
"is_service_item": "No",
|
||||||
"is_stock_item": "No",
|
"is_stock_item": "No",
|
||||||
"is_sub_contracted_item": "No",
|
"is_sub_contracted_item": "No",
|
||||||
"item_code": "_Test Sales BOM Item",
|
"item_code": "_Test Product Bundle Item",
|
||||||
"item_group": "_Test Item Group Desktops",
|
"item_group": "_Test Item Group Desktops",
|
||||||
"item_name": "_Test Sales BOM Item",
|
"item_name": "_Test Product Bundle Item",
|
||||||
"stock_uom": "_Test UOM"
|
"stock_uom": "_Test UOM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -13,9 +13,9 @@ from frappe.model.document import Document
|
|||||||
class PackedItem(Document):
|
class PackedItem(Document):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_sales_bom_items(item_code):
|
def get_product_bundle_items(item_code):
|
||||||
return frappe.db.sql("""select t1.item_code, t1.qty, t1.uom
|
return frappe.db.sql("""select t1.item_code, t1.qty, t1.uom
|
||||||
from `tabSales BOM Item` t1, `tabSales BOM` t2
|
from `tabProduct Bundle Item` t1, `tabProduct Bundle` t2
|
||||||
where t2.new_item_code=%s and t1.parent = t2.name""", item_code, as_dict=1)
|
where t2.new_item_code=%s and t1.parent = t2.name""", item_code, as_dict=1)
|
||||||
|
|
||||||
def get_packing_item_details(item):
|
def get_packing_item_details(item):
|
||||||
@ -58,14 +58,14 @@ def update_packing_list_item(obj, packing_item_code, qty, warehouse, line):
|
|||||||
|
|
||||||
|
|
||||||
def make_packing_list(obj, item_table_fieldname):
|
def make_packing_list(obj, item_table_fieldname):
|
||||||
"""make packing list for sales bom item"""
|
"""make packing list for Product Bundle item"""
|
||||||
|
|
||||||
if obj.get("_action") and obj._action == "update_after_submit": return
|
if obj.get("_action") and obj._action == "update_after_submit": return
|
||||||
|
|
||||||
parent_items = []
|
parent_items = []
|
||||||
for d in obj.get(item_table_fieldname):
|
for d in obj.get(item_table_fieldname):
|
||||||
if frappe.db.get_value("Sales BOM", {"new_item_code": d.item_code}):
|
if frappe.db.get_value("Product Bundle", {"new_item_code": d.item_code}):
|
||||||
for i in get_sales_bom_items(d.item_code):
|
for i in get_product_bundle_items(d.item_code):
|
||||||
update_packing_list_item(obj, i['item_code'], flt(i['qty'])*flt(d.qty), d.warehouse, d)
|
update_packing_list_item(obj, i['item_code'], flt(i['qty'])*flt(d.qty), d.warehouse, d)
|
||||||
|
|
||||||
if [d.item_code, d.name] not in parent_items:
|
if [d.item_code, d.name] not in parent_items:
|
||||||
|
@ -1 +1 @@
|
|||||||
Child Item grouped in parent Sales BOM.
|
Child Item grouped in parent Product Bundle.
|
@ -346,7 +346,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
self._test_sales_invoice_return("_Test Item", 5, 2)
|
self._test_sales_invoice_return("_Test Item", 5, 2)
|
||||||
|
|
||||||
def test_sales_invoice_return_of_packing_item(self):
|
def test_sales_invoice_return_of_packing_item(self):
|
||||||
self._test_sales_invoice_return("_Test Sales BOM Item", 25, 20)
|
self._test_sales_invoice_return("_Test Product Bundle Item", 25, 20)
|
||||||
|
|
||||||
def _test_delivery_note_return(self, item_code, delivered_qty, returned_qty):
|
def _test_delivery_note_return(self, item_code, delivered_qty, returned_qty):
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
@ -381,7 +381,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
self._test_delivery_note_return("_Test Item", 5, 2)
|
self._test_delivery_note_return("_Test Item", 5, 2)
|
||||||
|
|
||||||
def test_delivery_note_return_of_packing_item(self):
|
def test_delivery_note_return_of_packing_item(self):
|
||||||
self._test_delivery_note_return("_Test Sales BOM Item", 25, 20)
|
self._test_delivery_note_return("_Test Product Bundle Item", 25, 20)
|
||||||
|
|
||||||
def _test_sales_return_jv(self, se):
|
def _test_sales_return_jv(self, se):
|
||||||
jv = make_return_jv(se.name)
|
jv = make_return_jv(se.name)
|
||||||
@ -399,7 +399,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
self._test_sales_return_jv(se)
|
self._test_sales_return_jv(se)
|
||||||
|
|
||||||
def test_make_return_jv_for_sales_invoice_packing_item(self):
|
def test_make_return_jv_for_sales_invoice_packing_item(self):
|
||||||
se = self._test_sales_invoice_return("_Test Sales BOM Item", 25, 20)
|
se = self._test_sales_invoice_return("_Test Product Bundle Item", 25, 20)
|
||||||
self._test_sales_return_jv(se)
|
self._test_sales_return_jv(se)
|
||||||
|
|
||||||
def test_make_return_jv_for_delivery_note_non_packing_item(self):
|
def test_make_return_jv_for_delivery_note_non_packing_item(self):
|
||||||
@ -410,10 +410,10 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
self._test_sales_return_jv(se)
|
self._test_sales_return_jv(se)
|
||||||
|
|
||||||
def test_make_return_jv_for_delivery_note_packing_item(self):
|
def test_make_return_jv_for_delivery_note_packing_item(self):
|
||||||
se = self._test_delivery_note_return("_Test Sales BOM Item", 25, 20)
|
se = self._test_delivery_note_return("_Test Product Bundle Item", 25, 20)
|
||||||
self._test_sales_return_jv(se)
|
self._test_sales_return_jv(se)
|
||||||
|
|
||||||
se = self._test_delivery_note_return_against_sales_order("_Test Sales BOM Item", 25, 20)
|
se = self._test_delivery_note_return_against_sales_order("_Test Product Bundle Item", 25, 20)
|
||||||
self._test_sales_return_jv(se)
|
self._test_sales_return_jv(se)
|
||||||
|
|
||||||
def _test_delivery_note_return_against_sales_order(self, item_code, delivered_qty, returned_qty):
|
def _test_delivery_note_return_against_sales_order(self, item_code, delivered_qty, returned_qty):
|
||||||
|
@ -7,12 +7,12 @@ import frappe, json
|
|||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
data = []
|
data = []
|
||||||
parents = {
|
parents = {
|
||||||
"Sales BOM Item": "Sales BOM",
|
"Product Bundle Item": "Product Bundle",
|
||||||
"BOM Explosion Item": "BOM",
|
"BOM Explosion Item": "BOM",
|
||||||
"BOM Item": "BOM"
|
"BOM Item": "BOM"
|
||||||
}
|
}
|
||||||
|
|
||||||
for doctype in ("Sales BOM Item",
|
for doctype in ("Product Bundle Item",
|
||||||
"BOM Explosion Item" if filters.search_sub_assemblies else "BOM Item"):
|
"BOM Explosion Item" if filters.search_sub_assemblies else "BOM Item"):
|
||||||
all_boms = {}
|
all_boms = {}
|
||||||
for d in frappe.get_all(doctype, fields=["parent", "item_code"]):
|
for d in frappe.get_all(doctype, fields=["parent", "item_code"]):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user