Merge branch 'master' of github.com:webnotes/erpnext into edge

This commit is contained in:
Anand Doshi 2013-02-21 16:59:41 +05:30
commit c7cfe61229
7 changed files with 99 additions and 46 deletions

View File

@ -117,8 +117,7 @@ data_map = {
"(ifnull(qty, 0) - ifnull(ordered_qty, 0)) as qty"], "(ifnull(qty, 0) - ifnull(ordered_qty, 0)) as qty"],
"from": "`tabMaterial Request Item` item, `tabMaterial Request` main", "from": "`tabMaterial Request Item` item, `tabMaterial Request` main",
"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'", "conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
"material_request_type = 'Purchase'", "ifnull(warehouse, '')!=''", "ifnull(warehouse, '')!=''", "ifnull(qty, 0) > ifnull(ordered_qty, 0)"],
"ifnull(qty, 0) > ifnull(ordered_qty, 0)"],
"links": { "links": {
"item_code": ["Item", "name"], "item_code": ["Item", "name"],
"warehouse": ["Warehouse", "name"] "warehouse": ["Warehouse", "name"]

View File

@ -87,7 +87,7 @@ class DocType:
if (flt(args.get("actual_qty")) < 0 or flt(args.get("reserved_qty")) > 0) \ if (flt(args.get("actual_qty")) < 0 or flt(args.get("reserved_qty")) > 0) \
and args.get("is_cancelled") == 'No' and args.get("is_amended")=='No': and args.get("is_cancelled") == 'No' and args.get("is_amended")=='No':
self.reorder_item(args.get("voucher_type"), args.get("voucher_no")) self.reorder_item(args.get("voucher_type"), args.get("voucher_no"))
def get_first_sle(self): def get_first_sle(self):
sle = sql(""" sle = sql("""
select * from `tabStock Ledger Entry` select * from `tabStock Ledger Entry`
@ -122,7 +122,8 @@ class DocType:
self.create_material_request(doc_type, doc_name, reorder_level, reorder_qty, self.create_material_request(doc_type, doc_name, reorder_level, reorder_qty,
material_request_type) material_request_type)
def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty, material_request_type): def create_material_request(self, doc_type, doc_name, reorder_level, reorder_qty,
material_request_type):
""" Create indent on reaching reorder level """ """ Create indent on reaching reorder level """
defaults = webnotes.defaults.get_defaults() defaults = webnotes.defaults.get_defaults()
item = webnotes.doc("Item", self.doc.item_code) item = webnotes.doc("Item", self.doc.item_code)
@ -175,4 +176,5 @@ class DocType:
formatdate(), doc_type, doc_name, bean.doc.doctype, formatdate(), doc_type, doc_name, bean.doc.doctype,
get_url_to_form(bean.doc.doctype, bean.doc.name)) get_url_to_form(bean.doc.doctype, bean.doc.name))
sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg) sendmail(email_list, subject='Auto Material Request Generation Notification', msg = msg)

View File

@ -7,7 +7,7 @@ import webnotes
from webnotes.utils import cstr, flt from webnotes.utils import cstr, flt
from webnotes.model.bean import getlist from webnotes.model.bean import getlist
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
from webnotes import msgprint from webnotes import msgprint, _
from controllers.buying_controller import BuyingController from controllers.buying_controller import BuyingController
class DocType(BuyingController): class DocType(BuyingController):
@ -119,23 +119,22 @@ class DocType(BuyingController):
def update_bin(self, is_submit, is_stopped): def update_bin(self, is_submit, is_stopped):
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'""" """ Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""
if self.doc.material_request_type == "Purchase": for d in getlist(self.doclist, 'indent_details'):
for d in getlist(self.doclist, 'indent_details'): if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes":
if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes": if not d.warehouse:
if not d.warehouse: msgprint("Please Enter Warehouse for Item %s as it is stock item"
msgprint("Please Enter Warehouse for Item %s as it is stock item" % cstr(d.item_code), raise_exception=1)
% cstr(d.item_code), raise_exception=1)
qty =flt(d.qty)
qty =flt(d.qty) if is_stopped:
if is_stopped: qty = (d.qty > d.ordered_qty) and flt(flt(d.qty) - flt(d.ordered_qty)) or 0
qty = (d.qty > d.ordered_qty) and flt(flt(d.qty) - flt(d.ordered_qty)) or 0
args = {
args = { "item_code": d.item_code,
"item_code": d.item_code, "indented_qty": (is_submit and 1 or -1) * flt(qty),
"indented_qty": (is_submit and 1 or -1) * flt(qty), "posting_date": self.doc.transaction_date
"posting_date": self.doc.transaction_date }
} get_obj('Warehouse', d.warehouse).update_bin(args)
get_obj('Warehouse', d.warehouse).update_bin(args)
def on_submit(self): def on_submit(self):
purchase_controller = webnotes.get_obj("Purchase Common") purchase_controller = webnotes.get_obj("Purchase Common")
@ -196,7 +195,7 @@ class DocType(BuyingController):
and material_request_item = %s and docstatus = 1""", and material_request_item = %s and docstatus = 1""",
(self.doc.name, d.name))[0][0]) (self.doc.name, d.name))[0][0])
webnotes.conn.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty) webnotes.conn.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty)
# note: if qty is 0, its row is still counted in len(item_doclist) # note: if qty is 0, its row is still counted in len(item_doclist)
# hence adding 1 to per_ordered # hence adding 1 to per_ordered
if (d.ordered_qty > d.qty) or not d.qty: if (d.ordered_qty > d.qty) or not d.qty:
@ -204,11 +203,9 @@ class DocType(BuyingController):
elif d.qty > 0: elif d.qty > 0:
per_ordered += flt(d.ordered_qty / flt(d.qty)) per_ordered += flt(d.ordered_qty / flt(d.qty))
if per_ordered: self.doc.per_ordered = flt((per_ordered / flt(len(item_doclist))) * 100.0, 2)
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)
webnotes.conn.set_value(self.doc.doctype, self.doc.name, "per_ordered", self.doc.per_ordered)
def update_completed_qty(controller, caller_method): def update_completed_qty(controller, caller_method):
if controller.doc.doctype == "Stock Entry": if controller.doc.doctype == "Stock Entry":
material_request_map = {} material_request_map = {}
@ -218,7 +215,18 @@ def update_completed_qty(controller, caller_method):
if d.material_request not in material_request_map: if d.material_request not in material_request_map:
material_request_map[d.material_request] = [] material_request_map[d.material_request] = []
material_request_map[d.material_request].append(d.material_request_item) 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(): for mr_name, mr_items in material_request_map.items():
webnotes.get_obj("Material Request", mr_name, with_children=1).update_completed_qty(mr_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)
mr_obj.update_completed_qty(mr_items)

View File

@ -10,7 +10,15 @@ class TestMaterialRequest(unittest.TestCase):
for fieldname, val in expected.items(): for fieldname, val in expected.items():
self.assertEquals(val, doclist[i].fields.get(fieldname)) 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)
def test_completed_qty_for_purchase(self): def test_completed_qty_for_purchase(self):
webnotes.conn.sql("""delete from `tabBin`""")
# submit material request of type Purchase # submit material request of type Purchase
mr = webnotes.bean(copy=test_records[0]) mr = webnotes.bean(copy=test_records[0])
mr.insert() mr.insert()
@ -19,11 +27,12 @@ class TestMaterialRequest(unittest.TestCase):
# check if per complete is None # check if per complete is None
self._test_expected(mr.doclist, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": 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 purchase order # map a purchase order
po = webnotes.map_doclist([["Material Request", "Purchase Order"], po_doclist = webnotes.map_doclist([["Material Request", "Purchase Order"],
["Material Request Item", "Purchase Order Item"]], mr.doc.name) ["Material Request Item", "Purchase Order Item"]], mr.doc.name)
po = webnotes.bean(po) po_doclist[0].fields.update({
po.doc.fields.update({
"supplier": "_Test Supplier", "supplier": "_Test Supplier",
"supplier_name": "_Test Supplier", "supplier_name": "_Test Supplier",
"transaction_date": mr.doc.transaction_date, "transaction_date": mr.doc.transaction_date,
@ -32,21 +41,35 @@ class TestMaterialRequest(unittest.TestCase):
"conversion_rate": 1.0, "conversion_rate": 1.0,
"grand_total_import": 0.0 "grand_total_import": 0.0
}) })
po.doclist[1].qty = 27.0 po_doclist[1].qty = 27.0
po.doclist[2].qty = 1.5 po_doclist[2].qty = 1.5
# check for stopped status of Material Request
po = webnotes.bean(copy=po_doclist)
po.insert()
mr.obj.update_status('Stopped')
self.assertRaises(webnotes.ValidationError, po.submit)
self.assertRaises(webnotes.ValidationError, po.cancel)
mr.obj.update_status('Submitted')
po = webnotes.bean(copy=po_doclist)
po.insert() po.insert()
po.submit() po.submit()
# check if per complete is as expected # check if per complete is as expected
mr.load_from_db() mr.load_from_db()
self._test_expected(mr.doclist, [{"per_ordered": 50}, {"ordered_qty": 27.0}, {"ordered_qty": 1.5}]) self._test_expected(mr.doclist, [{"per_ordered": 50}, {"ordered_qty": 27.0}, {"ordered_qty": 1.5}])
self._test_requested_qty(27.0, 1.5)
po.cancel() po.cancel()
# check if per complete is as expected # check if per complete is as expected
mr.load_from_db() mr.load_from_db()
self._test_expected(mr.doclist, [{"per_ordered": 0}, {"ordered_qty": 0}, {"ordered_qty": 0}]) 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_transfer(self): def test_completed_qty_for_transfer(self):
webnotes.conn.sql("""delete from `tabBin`""")
# submit material request of type Purchase # submit material request of type Purchase
mr = webnotes.bean(copy=test_records[0]) mr = webnotes.bean(copy=test_records[0])
mr.doc.material_request_type = "Transfer" mr.doc.material_request_type = "Transfer"
@ -56,33 +79,51 @@ class TestMaterialRequest(unittest.TestCase):
# check if per complete is None # check if per complete is None
self._test_expected(mr.doclist, [{"per_ordered": None}, {"ordered_qty": None}, {"ordered_qty": 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 # map a stock entry
se = webnotes.map_doclist([["Material Request", "Stock Entry"], se_doclist = webnotes.map_doclist([["Material Request", "Stock Entry"],
["Material Request Item", "Stock Entry Detail"]], mr.doc.name) ["Material Request Item", "Stock Entry Detail"]], mr.doc.name)
se = webnotes.bean(se) se_doclist[0].fields.update({
se.doc.fields.update({
"posting_date": "2013-03-01", "posting_date": "2013-03-01",
"posting_time": "00:00" "posting_time": "00:00"
}) })
se.doclist[1].fields.update({ se_doclist[1].fields.update({
"qty": 27.0, "qty": 27.0,
"transfer_qty": 27.0, "transfer_qty": 27.0,
"s_warehouse": "_Test Warehouse 1", "s_warehouse": "_Test Warehouse 1",
"incoming_rate": 1.0 "incoming_rate": 1.0
}) })
se.doclist[2].fields.update({ se_doclist[2].fields.update({
"qty": 1.5, "qty": 1.5,
"transfer_qty": 1.5, "transfer_qty": 1.5,
"s_warehouse": "_Test Warehouse 1", "s_warehouse": "_Test Warehouse 1",
"incoming_rate": 1.0 "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.insert()
se.submit() se.submit()
# check if per complete is as expected # check if per complete is as expected
mr.load_from_db() mr.load_from_db()
self._test_expected(mr.doclist, [{"per_ordered": 50}, {"ordered_qty": 27.0}, {"ordered_qty": 1.5}]) self._test_expected(mr.doclist, [{"per_ordered": 50}, {"ordered_qty": 27.0}, {"ordered_qty": 1.5}])
self._test_requested_qty(27.0, 1.5)
# 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)
test_records = [ test_records = [
[ [
{ {

View File

@ -63,7 +63,7 @@ class DocType(TransactionBase):
self.update_stock_ledger(1) self.update_stock_ledger(1)
# update Production Order # update Production Order
self.update_production_order(0) self.update_production_order(0)
def validate_purpose(self): def validate_purpose(self):
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
"Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"] "Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"]

View File

@ -207,3 +207,4 @@ class DocType:
exists for this warehouse.""", raise_exception=1) exists for this warehouse.""", raise_exception=1)
else: else:
sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name) sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name)

View File

@ -6,6 +6,8 @@
{% include 'html/product_missing_image.html' %} {% include 'html/product_missing_image.html' %}
{% endif %} {% endif %}
</div> </div>
<h4><a href="{{ page_name }}">{{ item_name }}</a></h4> <div style="height: 80px; overflow: hidden; font-size: 80%;">
<p class="help">Item Code: {{ name }}</p> <h4 style="margin-bottom: 2px;"><a href="{{ page_name }}">{{ item_name }}</a></h4>
<p class="help">Item Code: {{ name }}</p>
</div>
</div> </div>