Merge branch 'master' of github.com:webnotes/erpnext
diff --git a/stock/doctype/material_request/material_request.py b/stock/doctype/material_request/material_request.py
index 708700d..7742676 100644
--- a/stock/doctype/material_request/material_request.py
+++ b/stock/doctype/material_request/material_request.py
@@ -205,7 +205,7 @@
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 @@
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 3c2f421..ae40858 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 @@
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 @@
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 78d06ce..05d7460 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -52,6 +52,8 @@
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 @@
'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):