Requested qty calculation logic
This commit is contained in:
parent
fad0d566f9
commit
4acd431b92
@ -47,8 +47,8 @@ doc_events = {
|
||||
"on_update": "erpnext.home.make_comment_feed"
|
||||
},
|
||||
"Stock Entry": {
|
||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_qty",
|
||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_qty"
|
||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty"
|
||||
},
|
||||
"User": {
|
||||
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
|
||||
|
@ -79,30 +79,9 @@ class MaterialRequest(BuyingController):
|
||||
# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
|
||||
# Though the creation of Material Request from a Production Plan can be rethought to fix this
|
||||
|
||||
def update_bin(self, is_submit, is_stopped):
|
||||
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""
|
||||
|
||||
from erpnext.stock.utils import update_bin
|
||||
for d in self.get('indent_details'):
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes":
|
||||
if not d.warehouse:
|
||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
||||
|
||||
qty =flt(d.qty)
|
||||
if is_stopped:
|
||||
qty = (d.qty > d.ordered_qty) and flt(flt(d.qty) - flt(d.ordered_qty)) or 0
|
||||
|
||||
args = {
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"indented_qty": (is_submit and 1 or -1) * flt(qty),
|
||||
"posting_date": self.transaction_date
|
||||
}
|
||||
update_bin(args)
|
||||
|
||||
def on_submit(self):
|
||||
frappe.db.set(self, 'status', 'Submitted')
|
||||
self.update_bin(is_submit = 1, is_stopped = 0)
|
||||
self.update_requested_qty()
|
||||
|
||||
def check_modified_date(self):
|
||||
mod_db = frappe.db.sql("""select modified from `tabMaterial Request` where name = %s""",
|
||||
@ -115,23 +94,18 @@ class MaterialRequest(BuyingController):
|
||||
|
||||
def update_status(self, status):
|
||||
self.check_modified_date()
|
||||
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
|
||||
self.update_requested_qty()
|
||||
frappe.db.set(self, 'status', cstr(status))
|
||||
frappe.msgprint(_("Status updated to {0}").format(_(status)))
|
||||
|
||||
def on_cancel(self):
|
||||
# Step 1:=> Get Purchase Common Obj
|
||||
pc_obj = frappe.get_doc('Purchase Common')
|
||||
|
||||
# Step 2:=> Check for stopped status
|
||||
pc_obj.check_for_stopped_status(self.doctype, self.name)
|
||||
|
||||
# Step 3:=> Check if Purchase Order has been submitted against current Material Request
|
||||
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item')
|
||||
# Step 4:=> Update Bin
|
||||
self.update_bin(is_submit = 0, is_stopped = (cstr(self.status) == 'Stopped') and 1 or 0)
|
||||
|
||||
# Step 5:=> Set Status
|
||||
self.update_requested_qty()
|
||||
|
||||
frappe.db.set(self,'status','Cancelled')
|
||||
|
||||
def update_completed_qty(self, mr_items=None):
|
||||
@ -162,56 +136,47 @@ class MaterialRequest(BuyingController):
|
||||
self.per_ordered = flt((per_ordered / flt(len(item_doclist))) * 100.0, 2)
|
||||
frappe.db.set_value(self.doctype, self.name, "per_ordered", self.per_ordered)
|
||||
|
||||
def update_completed_qty(doc, method):
|
||||
if doc.doctype == "Stock Entry":
|
||||
def update_requested_qty(self, mr_item_rows=None):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
from erpnext.stock.utils import get_bin
|
||||
|
||||
def _update_requested_qty(item_code, warehouse):
|
||||
requested_qty = frappe.db.sql("""select sum(mr_item.qty - ifnull(mr_item.ordered_qty, 0))
|
||||
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
|
||||
where mr_item.item_code=%s and mr_item.warehouse=%s
|
||||
and mr_item.qty > ifnull(mr_item.ordered_qty, 0) and mr_item.parent=mr.name
|
||||
and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
|
||||
|
||||
bin_doc = get_bin(item_code, warehouse)
|
||||
bin_doc.indented_qty = flt(requested_qty[0][0]) if requested_qty else 0
|
||||
bin_doc.save()
|
||||
|
||||
item_wh_list = []
|
||||
for d in self.get("indent_details"):
|
||||
if (not mr_item_rows or d.name in mr_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
|
||||
and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse:
|
||||
item_wh_list.append([d.item_code, d.warehouse])
|
||||
|
||||
for item_code, warehouse in item_wh_list:
|
||||
_update_requested_qty(item_code, warehouse)
|
||||
|
||||
def update_completed_and_requested_qty(stock_entry, method):
|
||||
if stock_entry.doctype == "Stock Entry":
|
||||
material_request_map = {}
|
||||
|
||||
for d in doc.get("mtn_details"):
|
||||
for d in stock_entry.get("mtn_details"):
|
||||
if d.material_request:
|
||||
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
|
||||
|
||||
for mr_name, mr_items in material_request_map.items():
|
||||
mr_obj = frappe.get_doc("Material Request", mr_name)
|
||||
for mr, mr_item_rows in material_request_map.items():
|
||||
if mr and mr_item_rows:
|
||||
mr_obj = frappe.get_doc("Material Request", mr)
|
||||
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr_obj.name),
|
||||
frappe.InvalidStatusError)
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError)
|
||||
|
||||
_update_requested_qty(doc, mr_obj, mr_items)
|
||||
|
||||
# update ordered percentage and qty
|
||||
mr_obj.update_completed_qty(mr_items)
|
||||
|
||||
def _update_requested_qty(doc, mr_obj, mr_items):
|
||||
"""update requested qty (before ordered_qty is updated)"""
|
||||
from erpnext.stock.utils import update_bin
|
||||
for mr_item_name in mr_items:
|
||||
mr_item = mr_obj.get("indent_details", {"name": mr_item_name})
|
||||
se_detail = doc.get("mtn_details", {"material_request": mr_obj.name,
|
||||
"material_request_item": mr_item_name})
|
||||
|
||||
if mr_item and se_detail:
|
||||
mr_item = mr_item[0]
|
||||
se_detail = se_detail[0]
|
||||
mr_item.ordered_qty = flt(mr_item.ordered_qty)
|
||||
mr_item.qty = flt(mr_item.qty)
|
||||
se_detail.transfer_qty = flt(se_detail.transfer_qty)
|
||||
|
||||
if se_detail.docstatus == 2 and mr_item.ordered_qty > mr_item.qty \
|
||||
and se_detail.transfer_qty == mr_item.ordered_qty:
|
||||
add_indented_qty = mr_item.qty
|
||||
elif se_detail.docstatus == 1 and \
|
||||
mr_item.ordered_qty + se_detail.transfer_qty > mr_item.qty:
|
||||
add_indented_qty = mr_item.qty - mr_item.ordered_qty
|
||||
else:
|
||||
add_indented_qty = se_detail.transfer_qty
|
||||
|
||||
update_bin({
|
||||
"item_code": se_detail.item_code,
|
||||
"warehouse": se_detail.t_warehouse,
|
||||
"indented_qty": (se_detail.docstatus==2 and 1 or -1) * add_indented_qty,
|
||||
"posting_date": doc.posting_date,
|
||||
})
|
||||
mr_obj.update_completed_qty(mr_item_rows)
|
||||
mr_obj.update_requested_qty(mr_item_rows)
|
||||
|
||||
def set_missing_values(source, target_doc):
|
||||
target_doc.run_method("set_missing_values")
|
||||
|
@ -58,12 +58,6 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(se.doctype, "Stock Entry")
|
||||
self.assertEquals(len(se.get("mtn_details")), len(mr.get("indent_details")))
|
||||
|
||||
def _test_requested_qty(self, qty1, qty2):
|
||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty1)
|
||||
self.assertEqual(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 200",
|
||||
"warehouse": "_Test Warehouse - _TC"}, "indented_qty")), qty2)
|
||||
|
||||
def _insert_stock_entry(self, qty1, qty2):
|
||||
se = frappe.get_doc({
|
||||
"company": "_Test Company",
|
||||
@ -103,7 +97,8 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
se.submit()
|
||||
|
||||
def test_completed_qty_for_purchase(self):
|
||||
frappe.db.sql("""delete from `tabBin`""")
|
||||
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
# submit material request of type Purchase
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
@ -115,8 +110,6 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
|
||||
# map a purchase order
|
||||
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||
po_doc = make_purchase_order(mr.name)
|
||||
@ -149,7 +142,12 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.per_ordered, 50)
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||
self._test_requested_qty(27.0, 1.5)
|
||||
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
|
||||
|
||||
po.cancel()
|
||||
# check if per complete is as expected
|
||||
@ -158,11 +156,15 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, None)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, None)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
def test_completed_qty_for_transfer(self):
|
||||
frappe.db.sql("""delete from `tabBin`""")
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`""")
|
||||
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
# submit material request of type Purchase
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
@ -175,7 +177,11 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||
|
||||
@ -226,7 +232,11 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 27.0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 1.5)
|
||||
|
||||
self._test_requested_qty(27.0, 1.5)
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
|
||||
|
||||
# check if per complete is as expected for Stock Entry cancelled
|
||||
se.cancel()
|
||||
@ -235,11 +245,15 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
def test_completed_qty_for_over_transfer(self):
|
||||
frappe.db.sql("""delete from `tabBin`""")
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`""")
|
||||
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
|
||||
existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
|
||||
|
||||
# submit material request of type Purchase
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
@ -252,8 +266,6 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
|
||||
# map a stock entry
|
||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||
|
||||
@ -297,7 +309,12 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.per_ordered, 100)
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 60.0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 3.0)
|
||||
self._test_requested_qty(0.0, 0.0)
|
||||
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2)
|
||||
|
||||
# check if per complete is as expected for Stock Entry cancelled
|
||||
se.cancel()
|
||||
@ -306,7 +323,11 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
self.assertEquals(mr.get("indent_details")[0].ordered_qty, 0)
|
||||
self.assertEquals(mr.get("indent_details")[1].ordered_qty, 0)
|
||||
|
||||
self._test_requested_qty(54.0, 3.0)
|
||||
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.assertEquals(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
|
||||
self.assertEquals(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
|
||||
|
||||
def test_incorrect_mapping_of_stock_entry(self):
|
||||
# submit material request of type Purchase
|
||||
@ -348,5 +369,9 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
mr.company = "_Test Company 1"
|
||||
self.assertRaises(InvalidWarehouseCompany, mr.insert)
|
||||
|
||||
def _get_requested_qty(self, item_code, warehouse):
|
||||
return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))
|
||||
|
||||
|
||||
test_dependencies = ["Currency Exchange"]
|
||||
test_records = frappe.get_test_records('Material Request')
|
||||
|
@ -93,11 +93,11 @@ def get_reserved_qty(item_code, warehouse):
|
||||
return flt(reserved_qty[0][0]) if reserved_qty else 0
|
||||
|
||||
def get_indented_qty(item_code, warehouse):
|
||||
indented_qty = frappe.db.sql("""select sum(pr_item.qty - ifnull(pr_item.ordered_qty, 0))
|
||||
from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr
|
||||
where pr_item.item_code=%s and pr_item.warehouse=%s
|
||||
and pr_item.qty > ifnull(pr_item.ordered_qty, 0) and pr_item.parent=pr.name
|
||||
and pr.status!='Stopped' and pr.docstatus=1""", (item_code, warehouse))
|
||||
indented_qty = frappe.db.sql("""select sum(mr_item.qty - ifnull(mr_item.ordered_qty, 0))
|
||||
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
|
||||
where mr_item.item_code=%s and mr_item.warehouse=%s
|
||||
and mr_item.qty > ifnull(mr_item.ordered_qty, 0) and mr_item.parent=mr.name
|
||||
and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
|
||||
|
||||
return flt(indented_qty[0][0]) if indented_qty else 0
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user