fix: requested qty for customer provided item and rate for sales (#21299)
* fix: requested qty for customer provided item and rate for sales
* fix: requested qty for material transfer
* fix: customer provided item can be sales item
* fix: requested qty test cases
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 0e54b62..a2819af 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1926,16 +1926,6 @@
item.taxes = []
item.save()
- def test_customer_provided_parts_si(self):
- create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
- si = create_sales_invoice(item_code='CUST-0987', rate=0)
- self.assertEqual(si.get("items")[0].allow_zero_valuation_rate, 1)
- self.assertEqual(si.get("items")[0].amount, 0)
-
- # test if Sales Invoice with rate is allowed
- si2 = create_sales_invoice(item_code='CUST-0987', do_not_save=True)
- self.assertRaises(frappe.ValidationError, si2.save)
-
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args)
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4037f2f..55a2c43 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -383,9 +383,6 @@
# Customer Provided parts will have zero valuation rate
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
- if d.parenttype in ["Delivery Note", "Sales Invoice"] and d.rate:
- frappe.throw(_("Row #{0}: {1} cannot have {2} as it is a Customer Provided Item")
- .format(d.idx, frappe.bold(d.item_code), frappe.bold("Rate")))
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None, company=None):
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 47a72b2..d7a93fb 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -434,15 +434,6 @@
update_delivery_note_status(dn.name, "Closed")
self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed")
- def test_customer_provided_parts_dn(self):
- create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
- dn = create_delivery_note(item_code='CUST-0987', rate=0)
- self.assertEqual(dn.get("items")[0].allow_zero_valuation_rate, 1)
-
- # test if Delivery Note with rate is allowed against Customer Provided Item
- dn2 = create_delivery_note(item_code='CUST-0987', do_not_save=True)
- self.assertRaises(frappe.ValidationError, dn2.save)
-
def test_dn_billing_status_case1(self):
# SO -> DN -> SI
so = make_sales_order()
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 30c47c3..19924b1 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -7,7 +7,8 @@
from __future__ import unicode_literals
import frappe, unittest, erpnext
from frappe.utils import flt, today
-from erpnext.stock.doctype.material_request.material_request import raise_work_orders
+from erpnext.stock.doctype.material_request.material_request \
+ import raise_work_orders, make_stock_entry, make_purchase_order, make_supplier_quotation
from erpnext.stock.doctype.item.test_item import create_item
class TestMaterialRequest(unittest.TestCase):
@@ -15,8 +16,6 @@
erpnext.set_perpetual_inventory(0)
def test_make_purchase_order(self):
- from erpnext.stock.doctype.material_request.material_request import make_purchase_order
-
mr = frappe.copy_doc(test_records[0]).insert()
self.assertRaises(frappe.ValidationError, make_purchase_order,
@@ -30,8 +29,6 @@
self.assertEqual(len(po.get("items")), len(mr.get("items")))
def test_make_supplier_quotation(self):
- from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation
-
mr = frappe.copy_doc(test_records[0]).insert()
self.assertRaises(frappe.ValidationError, make_supplier_quotation, mr.name)
@@ -45,12 +42,9 @@
def test_make_stock_entry(self):
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
mr = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_stock_entry,
- mr.name)
+ self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name)
mr = frappe.get_doc("Material Request", mr.name)
mr.material_request_type = "Material Transfer"
@@ -62,40 +56,40 @@
def _insert_stock_entry(self, qty1, qty2, warehouse = None ):
se = frappe.get_doc({
- "company": "_Test Company",
- "doctype": "Stock Entry",
- "posting_date": "2013-03-01",
- "posting_time": "00:00:00",
- "purpose": "Material Receipt",
- "items": [
- {
- "conversion_factor": 1.0,
- "doctype": "Stock Entry Detail",
- "item_code": "_Test Item Home Desktop 100",
- "parentfield": "items",
- "basic_rate": 100,
- "qty": qty1,
- "stock_uom": "_Test UOM 1",
- "transfer_qty": qty1,
- "uom": "_Test UOM 1",
- "t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
- "cost_center": "_Test Cost Center - _TC"
- },
- {
- "conversion_factor": 1.0,
- "doctype": "Stock Entry Detail",
- "item_code": "_Test Item Home Desktop 200",
- "parentfield": "items",
- "basic_rate": 100,
- "qty": qty2,
- "stock_uom": "_Test UOM 1",
- "transfer_qty": qty2,
- "uom": "_Test UOM 1",
- "t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
- "cost_center": "_Test Cost Center - _TC"
- }
- ]
- })
+ "company": "_Test Company",
+ "doctype": "Stock Entry",
+ "posting_date": "2013-03-01",
+ "posting_time": "00:00:00",
+ "purpose": "Material Receipt",
+ "items": [
+ {
+ "conversion_factor": 1.0,
+ "doctype": "Stock Entry Detail",
+ "item_code": "_Test Item Home Desktop 100",
+ "parentfield": "items",
+ "basic_rate": 100,
+ "qty": qty1,
+ "stock_uom": "_Test UOM 1",
+ "transfer_qty": qty1,
+ "uom": "_Test UOM 1",
+ "t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
+ "cost_center": "_Test Cost Center - _TC"
+ },
+ {
+ "conversion_factor": 1.0,
+ "doctype": "Stock Entry Detail",
+ "item_code": "_Test Item Home Desktop 200",
+ "parentfield": "items",
+ "basic_rate": 100,
+ "qty": qty2,
+ "stock_uom": "_Test UOM 1",
+ "transfer_qty": qty2,
+ "uom": "_Test UOM 1",
+ "t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
+ "cost_center": "_Test Cost Center - _TC"
+ }
+ ]
+ })
se.set_stock_entry_type()
se.insert()
@@ -198,14 +192,7 @@
mr.insert()
mr.submit()
- # check if per complete is None
- mr.load_from_db()
- self.assertEqual(mr.per_ordered, 0)
- self.assertEqual(mr.get("items")[0].ordered_qty, 0)
- self.assertEqual(mr.get("items")[1].ordered_qty, 0)
-
# map a purchase order
- from erpnext.stock.doctype.material_request.material_request import make_purchase_order
po_doc = make_purchase_order(mr.name)
po_doc.supplier = "_Test Supplier"
po_doc.transaction_date = "2013-07-07"
@@ -276,10 +263,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
-
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
# map a stock entry
se_doc = make_stock_entry(mr.name)
@@ -331,8 +316,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 27.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 1.5)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
# check if per complete is as expected for Stock Entry cancelled
se.cancel()
@@ -344,8 +329,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
def test_completed_qty_for_over_transfer(self):
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
@@ -357,14 +342,7 @@
mr.insert()
mr.submit()
- # check if per complete is None
- mr.load_from_db()
- self.assertEqual(mr.per_ordered, 0)
- self.assertEqual(mr.get("items")[0].ordered_qty, 0)
- self.assertEqual(mr.get("items")[1].ordered_qty, 0)
-
# map a stock entry
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
se_doc = make_stock_entry(mr.name)
se_doc.update({
@@ -425,8 +403,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
def test_incorrect_mapping_of_stock_entry(self):
# submit material request of type Transfer
@@ -435,9 +413,6 @@
mr.insert()
mr.submit()
- # map a stock entry
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
se_doc = make_stock_entry(mr.name)
se_doc.update({
"posting_date": "2013-03-01",
@@ -468,8 +443,6 @@
mr.insert()
mr.submit()
- # map a stock entry
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
se_doc = make_stock_entry(mr.name)
self.assertEqual(se_doc.get("items")[0].s_warehouse, "_Test Warehouse - _TC")
@@ -483,8 +456,6 @@
return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))
def test_make_stock_entry_for_material_issue(self):
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
mr = frappe.copy_doc(test_records[0]).insert()
self.assertRaises(frappe.ValidationError, make_stock_entry,
@@ -503,8 +474,6 @@
return flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
"warehouse": "_Test Warehouse - _TC"}, "indented_qty"))
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
existing_requested_qty = _get_requested_qty()
mr = frappe.copy_doc(test_records[0])
@@ -594,8 +563,6 @@
def test_multi_uom_for_purchase(self):
- from erpnext.stock.doctype.material_request.material_request import make_purchase_order
-
mr = frappe.copy_doc(test_records[0])
mr.material_request_type = 'Purchase'
item = mr.items[0]
@@ -637,7 +604,6 @@
self.assertEqual(mr.per_ordered, 100)
def test_customer_provided_parts_mr(self):
- from erpnext.stock.doctype.material_request.material_request import make_stock_entry
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
existing_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC")
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index d9434e3..5697315 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -113,30 +113,28 @@
return flt(reserved_qty[0][0]) if reserved_qty else 0
def get_indented_qty(item_code, warehouse):
- # Ordered Qty is maintained in purchase UOM
- requested_qty_for_purchase_and_manufacture = frappe.db.sql("""
+ # Ordered Qty is always maintained in stock UOM
+ inward_qty = frappe.db.sql("""
select sum(mr_item.stock_qty - mr_item.ordered_qty)
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
where mr_item.item_code=%s and mr_item.warehouse=%s
- and mr.material_request_type in ('Purchase', 'Manufacture')
+ and mr.material_request_type in ('Purchase', 'Manufacture', 'Customer Provided', 'Material Transfer')
and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
and mr.status!='Stopped' and mr.docstatus=1
""", (item_code, warehouse))
- requested_qty_for_purchase_and_manufacture = flt(requested_qty_for_purchase_and_manufacture[0][0]) \
- if requested_qty_for_purchase_and_manufacture else 0
+ inward_qty = flt(inward_qty[0][0]) if inward_qty else 0
- requested_qty_for_issue_and_transfer = frappe.db.sql("""
+ outward_qty = frappe.db.sql("""
select sum(mr_item.stock_qty - mr_item.ordered_qty)
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
where mr_item.item_code=%s and mr_item.warehouse=%s
- and mr.material_request_type in ('Material Issue', 'Material Transfer')
+ and mr.material_request_type = 'Material Issue'
and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
and mr.status!='Stopped' and mr.docstatus=1
""", (item_code, warehouse))
- requested_qty_for_issue_and_transfer = flt(requested_qty_for_issue_and_transfer[0][0]) \
- if requested_qty_for_issue_and_transfer else 0
+ outward_qty = flt(outward_qty[0][0]) if outward_qty else 0
- requested_qty = requested_qty_for_purchase_and_manufacture - requested_qty_for_issue_and_transfer
+ requested_qty = inward_qty - outward_qty
return requested_qty