diff --git a/stock/doctype/material_request/material_request.py b/stock/doctype/material_request/material_request.py index 708700d512..77426762f9 100644 --- a/stock/doctype/material_request/material_request.py +++ b/stock/doctype/material_request/material_request.py @@ -205,7 +205,7 @@ class DocType(BuyingController): self.doc.per_ordered = flt((per_ordered / flt(len(item_doclist))) * 100.0, 2) webnotes.conn.set_value(self.doc.doctype, self.doc.name, "per_ordered", self.doc.per_ordered) - + def update_completed_qty(controller, caller_method): if controller.doc.doctype == "Stock Entry": material_request_map = {} @@ -215,18 +215,43 @@ def update_completed_qty(controller, caller_method): if d.material_request not in material_request_map: material_request_map[d.material_request] = [] material_request_map[d.material_request].append(d.material_request_item) - webnotes.get_obj("Warehouse", d.t_warehouse).update_bin({ - "item_code": d.item_code, - "indented_qty": (d.docstatus==2 and 1 or -1) * d.transfer_qty, - "posting_date": controller.doc.posting_date, - }) for mr_name, mr_items in material_request_map.items(): mr_obj = webnotes.get_obj("Material Request", mr_name, with_children=1) mr_doctype = webnotes.get_doctype("Material Request") + if mr_obj.doc.status in ["Stopped", "Cancelled"]: msgprint(_("Material Request") + ": %s, " % mr_obj.doc.name + _(mr_doctype.get_label("status")) + " = %s. " % _(mr_obj.doc.status) - + _("Cannot continue."), raise_exception=True) + + _("Cannot continue."), raise_exception=webnotes.InvalidStatusError) - mr_obj.update_completed_qty(mr_items) \ No newline at end of file + _update_requested_qty(controller, mr_obj, mr_items) + + # update ordered percentage and qty + mr_obj.update_completed_qty(mr_items) + +def _update_requested_qty(controller, mr_obj, mr_items): + """update requested qty (before ordered_qty is updated)""" + for mr_item_name in mr_items: + mr_item = mr_obj.doclist.getone({"parentfield": "indent_details", "name": mr_item_name}) + se_detail = controller.doclist.getone({"parentfield": "mtn_details", + "material_request": mr_obj.doc.name, "material_request_item": mr_item_name}) + + 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 + + webnotes.get_obj("Warehouse", se_detail.t_warehouse).update_bin({ + "item_code": se_detail.item_code, + "indented_qty": (se_detail.docstatus==2 and 1 or -1) * add_indented_qty, + "posting_date": controller.doc.posting_date, + }) diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py index 3c2f421e95..ae40858564 100644 --- a/stock/doctype/material_request/test_material_request.py +++ b/stock/doctype/material_request/test_material_request.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import webnotes, unittest +from webnotes.utils import flt class TestMaterialRequest(unittest.TestCase): def _test_expected(self, doclist, expected_values): @@ -11,10 +12,10 @@ class TestMaterialRequest(unittest.TestCase): self.assertEquals(val, doclist[i].fields.get(fieldname)) def _test_requested_qty(self, qty1, qty2): - self.assertEqual(webnotes.conn.get_value("Bin", {"item_code": "_Test Item Home Desktop 100", - "warehouse": "_Test Warehouse"}, "indented_qty"), qty1) - self.assertEqual(webnotes.conn.get_value("Bin", {"item_code": "_Test Item Home Desktop 200", - "warehouse": "_Test Warehouse"}, "indented_qty"), qty2) + self.assertEqual(flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item Home Desktop 100", + "warehouse": "_Test Warehouse"}, "indented_qty")), qty1) + self.assertEqual(flt(webnotes.conn.get_value("Bin", {"item_code": "_Test Item Home Desktop 200", + "warehouse": "_Test Warehouse"}, "indented_qty")), qty2) def test_completed_qty_for_purchase(self): webnotes.conn.sql("""delete from `tabBin`""") @@ -124,6 +125,95 @@ class TestMaterialRequest(unittest.TestCase): self._test_expected(mr.doclist, [{"per_ordered": 0}, {"ordered_qty": 0}, {"ordered_qty": 0}]) self._test_requested_qty(54.0, 3.0) + def test_completed_qty_for_over_transfer(self): + webnotes.conn.sql("""delete from `tabBin`""") + + # submit material request of type Purchase + mr = webnotes.bean(copy=test_records[0]) + mr.doc.material_request_type = "Transfer" + mr.insert() + mr.submit() + + # check if per complete is None + self._test_expected(mr.doclist, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": None}]) + + self._test_requested_qty(54.0, 3.0) + + # map a stock entry + se_doclist = webnotes.map_doclist([["Material Request", "Stock Entry"], + ["Material Request Item", "Stock Entry Detail"]], mr.doc.name) + se_doclist[0].fields.update({ + "posting_date": "2013-03-01", + "posting_time": "00:00" + }) + se_doclist[1].fields.update({ + "qty": 60.0, + "transfer_qty": 60.0, + "s_warehouse": "_Test Warehouse 1", + "incoming_rate": 1.0 + }) + se_doclist[2].fields.update({ + "qty": 3.0, + "transfer_qty": 3.0, + "s_warehouse": "_Test Warehouse 1", + "incoming_rate": 1.0 + }) + + # check for stopped status of Material Request + se = webnotes.bean(copy=se_doclist) + se.insert() + mr.obj.update_status('Stopped') + self.assertRaises(webnotes.ValidationError, se.submit) + self.assertRaises(webnotes.ValidationError, se.cancel) + + mr.obj.update_status('Submitted') + se = webnotes.bean(copy=se_doclist) + se.insert() + se.submit() + + # check if per complete is as expected + mr.load_from_db() + self._test_expected(mr.doclist, [{"per_ordered": 100}, {"ordered_qty": 60.0}, {"ordered_qty": 3.0}]) + self._test_requested_qty(0.0, 0.0) + + # check if per complete is as expected for Stock Entry cancelled + se.cancel() + mr.load_from_db() + self._test_expected(mr.doclist, [{"per_ordered": 0}, {"ordered_qty": 0}, {"ordered_qty": 0}]) + self._test_requested_qty(54.0, 3.0) + + def test_incorrect_mapping_of_stock_entry(self): + # submit material request of type Purchase + mr = webnotes.bean(copy=test_records[0]) + mr.doc.material_request_type = "Transfer" + mr.insert() + mr.submit() + + # map a stock entry + se_doclist = webnotes.map_doclist([["Material Request", "Stock Entry"], + ["Material Request Item", "Stock Entry Detail"]], mr.doc.name) + se_doclist[0].fields.update({ + "posting_date": "2013-03-01", + "posting_time": "00:00" + }) + se_doclist[1].fields.update({ + "qty": 60.0, + "transfer_qty": 60.0, + "s_warehouse": "_Test Warehouse", + "t_warehouse": "_Test Warehouse 1", + "incoming_rate": 1.0 + }) + se_doclist[2].fields.update({ + "qty": 3.0, + "transfer_qty": 3.0, + "s_warehouse": "_Test Warehouse 1", + "incoming_rate": 1.0 + }) + + # check for stopped status of Material Request + se = webnotes.bean(copy=se_doclist) + self.assertRaises(webnotes.MappingMismatchError, se.insert) + test_records = [ [ { diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 78d06ce910..05d7460773 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -52,6 +52,8 @@ class DocType(TransactionBase): self.validate_finished_goods() self.validate_return_reference_doc() + self.validate_with_material_request() + def on_submit(self): self.update_serial_no(1) self.update_stock_ledger(0) @@ -580,6 +582,18 @@ class DocType(TransactionBase): 'supplier_name' : res and res[0][0] or '', 'supplier_address' : addr and addr[0] or ''} return ret + + def validate_with_material_request(self): + for item in self.doclist.get({"parentfield": "mtn_details"}): + if item.material_request: + mreq_item = webnotes.conn.get_value("Material Request Item", + {"name": item.material_request_item, "parent": item.material_request}, + ["item_code", "warehouse", "idx"], as_dict=True) + if mreq_item.item_code != item.item_code or mreq_item.warehouse != item.t_warehouse: + msgprint(_("Row #") + (" %d: " % item.idx) + _("does not match") + + " " + _("Row #") + (" %d %s " % (mreq_item.idx, _("of"))) + + _("Material Request") + (" - %s" % item.material_request), + raise_exception=webnotes.MappingMismatchError) @webnotes.whitelist() def get_production_order_details(production_order):