diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 121fc93fb92..cd4290134cb 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -151,7 +151,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "depends_on": "eval:doc.is_drop_ship==1", + "depends_on": "eval:doc.delivered_by_supplier==1", "fieldname": "customer", "fieldtype": "Link", "hidden": 0, @@ -164,7 +164,7 @@ "permlevel": 0, "precision": "", "print_hide": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -175,7 +175,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "depends_on": "eval:doc.is_drop_ship==1", + "depends_on": "eval:doc.delivered_by_supplier==1", "fieldname": "customer_name", "fieldtype": "Data", "hidden": 0, @@ -476,7 +476,7 @@ "permlevel": 0, "precision": "", "print_hide": 1, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -1557,30 +1557,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "eval:doc.is_drop_ship==1", - "fieldname": "customer_address", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Customer Address", - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -1623,30 +1599,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "eval:doc.is_drop_ship==1", - "fieldname": "customer_contact_person", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Customer Contact Person", - "no_copy": 0, - "options": "Contact", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -2238,8 +2190,8 @@ "is_submittable": 1, "issingle": 0, "istable": 0, - "modified": "2015-10-27 16:58:53.362949", - "modified_by": "saurabh@erpnext.com", + "modified": "2015-10-29 16:41:30.749753", + "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", "owner": "Administrator", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 26837f1a64e..4f373e0eb23 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -353,25 +353,13 @@ def delivered_by_supplier(purchase_order): def update_delivered_qty(purchase_order): sales_order_list = [] + so_name = '' for item in purchase_order.items: if item.prevdoc_doctype == "Sales Order": - frappe.db.sql(""" update `tabSales Order Item` - set delivered_qty = (ifnull(delivered_qty, 0) + %(qty)s) - where item_code='%(item_code)s' and parent = '%(name)s' - """%{"qty": item.qty, "item_code": item.item_code, "name": item.prevdoc_docname}) - - sales_order_list.append(item.prevdoc_docname) + so_name = item.prevdoc_docname - update_per_delivery(sales_order_list) - -def update_per_delivery(sales_order_list): - for so in sales_order_list: - frappe.db.sql("""update `tabSales Order` - set per_delivered = (select - sum(if(qty > ifnull(delivered_qty, 0), delivered_qty, qty))/ sum(qty)*100 - from `tabSales Order Item` where parent="%(name)s") where name = "%(name)s" """%{"name":so}) - - so = frappe.get_doc("Sales Order", so) - so.set_status(update=True) - so.notify_update() + so = frappe.get_doc("Sales Order", so_name) + so.update_delivery_status(purchase_order.name) + so.set_status(update=True) + so.notify_update() \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 0c8fba62b52..1988a6d8e19 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -71,19 +71,19 @@ class TestPurchaseOrder(unittest.TestCase): po = create_purchase_order(qty=3.4, do_not_save=True) self.assertRaises(UOMMustBeIntegerError, po.insert) - def test_ordered_qty_for_closing_po(self): - from erpnext.stock.doctype.item.test_item import make_item + def test_ordered_qty_for_closing_po(self): + bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, + fields=["ordered_qty"]) - item = make_item("_Test Close Item", {"is_stock_item": 1, "is_sales_item": 1, - "is_purchase_item": 1}) + existing_ordered_qty = bin[0].ordered_qty if bin else 0.0 - po = create_purchase_order(item_code=item.item_code, qty=1) + po = create_purchase_order(item_code= "_Test Item", qty=1) - self.assertEquals(get_ordered_qty(item_code=item.item_code, warehouse="_Test Warehouse - _TC"), 1) + self.assertEquals(get_ordered_qty(item_code= "_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty+1) po.update_status("Closed") - self.assertEquals(get_ordered_qty(item_code=item.item_code, warehouse="_Test Warehouse - _TC"), 0) + self.assertEquals(get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty) def create_purchase_order(**args): po = frappe.new_doc("Purchase Order") diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2de6777fef6..b68339c3518 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -174,9 +174,7 @@ class StatusUpdater(Document): if change_modified: args['set_modified'] = ', modified = now(), modified_by = "{0}"'\ .format(frappe.db.escape(frappe.session.user)) - - args["drop_ship_cond"] = '' - + self._update_children(args) if "percent_join_field" in args: diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 9ad6d563015..2022ffd80a7 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -22,8 +22,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( if(doc.status != 'Stopped' && doc.status != 'Closed') { $.each(cur_frm.doc.items, function(i, item){ - if((item.delivered_by_supplier == 1 || item.supplier) && (item.qty > item.ordered_qty)){ - is_delivered_by_supplier = true; + if(item.delivered_by_supplier == 1 || item.supplier){ + if(item.qty > item.ordered_qty) + is_delivered_by_supplier = true; } else{ if(item.qty > item.delivered_qty) @@ -37,7 +38,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( // doc.per_billed); // indent - if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && flt(doc.per_delivered, 2) < 100) + if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && flt(doc.per_delivered, 2) < 100 && !is_delivered_by_supplier) cur_frm.add_custom_button(__('Material Request'), this.make_material_request); if(flt(doc.per_billed)==0) { @@ -45,8 +46,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( } // stop - if((flt(doc.per_delivered, 2) < 100 && is_delivery_note) || doc.per_billed < 100 - || (flt(doc.per_ordered,2) < 100 && is_delivered_by_supplier)){ + if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) { cur_frm.add_custom_button(__('Stop'), this.stop_sales_order) } @@ -68,7 +68,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); } - if(flt(doc.per_ordered, 2) < 100 && is_delivered_by_supplier) + if(flt(doc.per_delivered, 2) < 100 && is_delivered_by_supplier) cur_frm.add_custom_button(__('Make Purchase Order'), cur_frm.cscript.make_purchase_order).addClass("btn-primary"); } else { diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index f452fa98414..7ea796cc997 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -260,8 +260,34 @@ class SalesOrder(SellingController): pass def before_update_after_submit(self): - self.validate_drop_ship() + self.validate_drop_ship() + self.validate_po() + + def validate_po(self): + exc_list = [] + + for item in self.items: + supplier = frappe.db.get_value("Sales Order Item", {"parent": self.name, "item_code": item.item_code}, + "supplier") + if item.ordered_qty > 0.0 and item.supplier != supplier: + exc_list.append("Row #{0}: Not allowed to change supplier as Purchase Order already exists".format(item.idx)) + + if exc_list: + frappe.throw('\n'.join(exc_list)) + + def update_delivery_status(self, po_name): + tot_qty, delivered_qty = 0.0, 0.0 + for item in self.items: + if item.delivered_by_supplier: + delivered_qty = frappe.db.get_value("Purchase Order Item", {"parent": po_name, "item_code": item.item_code}, "qty") + frappe.db.set_value("Sales Order Item", item.name, "delivered_qty", delivered_qty) + + delivered_qty += item.delivered_qty + tot_qty += item.qty + + frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100) + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) @@ -551,15 +577,9 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc= ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == for_supplier - }, - "Sales Taxes and Charges": { - "doctype": "Purchase Taxes and Charges", - "add_if_empty": True } }, target_doc, set_missing_values) - - return doclist @frappe.whitelist() @@ -595,4 +615,4 @@ def get_supplier(doctype, txt, searchfield, start, page_len, filters): def update_status(status, name): so = frappe.get_doc("Sales Order", name) so.stop_sales_order(status) - return \ No newline at end of file + \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 3ef1a25bc50..1d11a8bead8 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -317,7 +317,6 @@ class TestSalesOrder(unittest.TestCase): "warehouse": "_Test Warehouse - _TC", "qty": 2, "rate": 400, - "conversion_factor": 1.0, "delivered_by_supplier": 1, "supplier": '_Test Supplier' }, @@ -330,9 +329,19 @@ class TestSalesOrder(unittest.TestCase): } ] + #setuo existing qty from bin bin = frappe.get_all("Bin", filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, fields=["ordered_qty", "reserved_qty"]) - + + existing_ordered_qty = bin[0].ordered_qty if bin else 0.0 + existing_reserved_qty = bin[0].reserved_qty if bin else 0.0 + + bin = frappe.get_all("Bin", filters={"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, + fields=["reserved_qty"]) + + existing_reserved_qty_for_dn_item = bin[0].reserved_qty if bin else 0.0 + + #create so, po and partial dn so = make_sales_order(item_list=so_items, do_not_submit=True) so.submit() @@ -351,40 +360,56 @@ class TestSalesOrder(unittest.TestCase): ordered_qty, reserved_qty = frappe.db.get_value("Bin", {"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, ["ordered_qty", "reserved_qty"]) - existing_ordered_qty = bin[0].ordered_qty if bin else 0.0 - existing_reserved_qty = bin[0].reserved_qty if bin else 0.0 - self.assertEquals(abs(ordered_qty), existing_ordered_qty + so_items[0]['qty']) - self.assertEquals(abs(reserved_qty), existing_reserved_qty + so_items[0]['qty']) + self.assertEquals(abs(reserved_qty), existing_reserved_qty + so_items[0]['qty']) + + reserved_qty = frappe.db.get_value("Bin", + {"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty") + + self.assertEquals(abs(reserved_qty), existing_reserved_qty_for_dn_item + 1) #test po_item length self.assertEquals(len(po.items), 1) #test per_delivered status delivered_by_supplier(po.name) - per_delivered = frappe.db.sql("""select sum(if(qty > ifnull(delivered_qty, 0), delivered_qty, qty))/sum(qty)*100 as per_delivered - from `tabSales Order Item` where parent="{0}" """.format(so.name)) - - self.assertEquals(frappe.db.get_value("Sales Order", so.name, "per_delivered"), per_delivered[0][0]) + self.assertEquals(flt(frappe.db.get_value("Sales Order", so.name, "per_delivered"), 2), 75.00) + #test reserved qty after complete delivery dn = create_dn_against_so(so.name, delivered_qty=1) + reserved_qty = frappe.db.get_value("Bin", + {"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty") + self.assertEquals(abs(reserved_qty), existing_reserved_qty_for_dn_item) + + #test after closing so so.db_set('status', "Closed") so.update_reserved_qty() + ordered_qty, reserved_qty = frappe.db.get_value("Bin", + {"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, ["ordered_qty", "reserved_qty"]) + + self.assertEquals(abs(ordered_qty), existing_ordered_qty) + self.assertEquals(abs(reserved_qty), existing_reserved_qty) + + reserved_qty = frappe.db.get_value("Bin", + {"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty") + + self.assertEquals(abs(reserved_qty), existing_reserved_qty) + def test_reserved_qty_for_closing_so(self): - from erpnext.stock.doctype.item.test_item import make_item + bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, + fields=["reserved_qty"]) - item = make_item("_Test Close Item", {"is_stock_item": 1, "is_sales_item": 1, - "is_purchase_item": 1}) + existing_reserved_qty = bin[0].reserved_qty if bin else 0.0 - so = make_sales_order(item_code=item.item_code, qty=1) + so = make_sales_order(item_code="_Test Item", qty=1) - self.assertEquals(get_reserved_qty(item_code=item.item_code, warehouse="_Test Warehouse - _TC"), 1) + self.assertEquals(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty+1) so.stop_sales_order("Closed") - self.assertEquals(get_reserved_qty(item_code=item.item_code, warehouse="_Test Warehouse - _TC"), 0) + self.assertEquals(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty) def make_sales_order(**args):