diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index a43c30d1bd..5af30788b7 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -292,6 +292,13 @@ class PurchaseOrder(BuyingController): if d.rm_item_code: stock_bin = get_bin(d.rm_item_code, d.reserve_warehouse) stock_bin.update_reserved_qty_for_sub_contracting() + + def update_receiving_percentage(self): + total_qty, received_qty = 0.0, 0.0 + for item in self.items: + received_qty += item.received_qty + total_qty += item.qty + self.db_set("per_received", flt(received_qty/total_qty) * 100, update_modified=False) def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= 1.0): """get last purchase rate for an item""" diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 32f8ee88a8..c67d4e5d76 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -9,6 +9,8 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_ent from frappe.utils import flt, add_days, nowdate from erpnext.stock.doctype.item.test_item import make_item from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry) +from erpnext.stock.doctype.material_request.test_material_request import make_material_request +from erpnext.stock.doctype.material_request.material_request import make_purchase_order from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.controllers.accounts_controller import update_child_qty_rate import json @@ -78,18 +80,34 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.get("items")[0].received_qty, 0) def test_update_child_qty_rate(self): - po = create_purchase_order(item_code= "_Test Item", qty=4) + mr = make_material_request(qty=10) + po = make_purchase_order(mr.name) + po.supplier = "_Test Supplier" + po.items[0].qty = 4 + po.save() + po.submit() create_pr_against_po(po.name) make_purchase_invoice(po.name) - trans_item = {'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7} + existing_ordered_qty = get_ordered_qty() + existing_requested_qty = get_requested_qty() + + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}]) update_child_qty_rate('Purchase Order', trans_item, po.name) + mr.reload() + self.assertEqual(mr.items[0].ordered_qty, 7) + self.assertEqual(mr.per_ordered, 70) + self.assertEqual(get_requested_qty(), existing_requested_qty - 3) + + po.reload() self.assertEqual(po.get("items")[0].rate, 200) self.assertEqual(po.get("items")[0].qty, 7) self.assertEqual(po.get("items")[0].amount, 1400) + self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) + def test_make_purchase_invoice(self): po = create_purchase_order(do_not_submit=True) @@ -536,6 +554,10 @@ def get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"): return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "ordered_qty")) +def get_requested_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"): + return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, + "indented_qty")) + test_dependencies = ["BOM", "Item Price"] test_records = frappe.get_test_records('Purchase Order') diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9751e83f1a..fe8e34e3da 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -783,7 +783,6 @@ class AccountsController(TransactionBase): else: return frappe.db.get_single_value("Global Defaults", "disable_rounded_total") - @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) @@ -1045,13 +1044,13 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name): data = json.loads(trans_items) for d in data: child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - child_item.qty = float(d.get("qty")) + child_item.qty = flt(d.get("qty")) - if child_item.billed_amt > (float(d.get("rate")) * float(d.get("qty"))): + if child_item.billed_amt > (flt(d.get("rate")) * flt(d.get("qty"))): frappe.throw(_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.") .format(child_item.idx, child_item.item_code)) else: - child_item.rate = float(d.get("rate")) + child_item.rate = flt(d.get("rate")) child_item.flags.ignore_validate_update_after_submit = True child_item.save() @@ -1059,28 +1058,36 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name): p_doctype.flags.ignore_validate_update_after_submit = True p_doctype.set_qty_as_per_stock_uom() - if parent_doctype == 'Purchase Order': - p_doctype.validate_minimum_order_qty() - update_last_purchase_rate(p_doctype, is_submit = 1) - if p_doctype.is_against_so(): - p_doctype.update_status_updater() - p_doctype.update_prevdoc_status() - p_doctype.update_requested_qty() - p_doctype.update_ordered_qty() - p_doctype.validate_budget() - p_doctype.update_ordered_and_reserved_qty() - if p_doctype.is_subcontracted == "Yes": - p_doctype.update_reserved_qty_for_subcontract() - else: - p_doctype.check_credit_limit() - p_doctype.update_reserved_qty() - p_doctype.update_project() - p_doctype.update_prevdoc_status('submit') - p_doctype.update_blanket_order() - p_doctype.set_status() + p_doctype.calculate_taxes_and_totals() frappe.get_doc('Authorization Control').validate_approving_authority(p_doctype.doctype, p_doctype.company, p_doctype.base_grand_total) - p_doctype.calculate_taxes_and_totals() p_doctype.set_payment_schedule() + if parent_doctype == 'Purchase Order': + p_doctype.validate_minimum_order_qty() + p_doctype.validate_budget() + if p_doctype.is_against_so(): + p_doctype.update_status_updater() + else: + p_doctype.check_credit_limit() + p_doctype.save() + + if parent_doctype == 'Purchase Order': + update_last_purchase_rate(p_doctype, is_submit = 1) + p_doctype.update_prevdoc_status() + p_doctype.update_requested_qty() + p_doctype.update_ordered_qty() + p_doctype.update_ordered_and_reserved_qty() + p_doctype.update_receiving_percentage() + if p_doctype.is_subcontracted == "Yes": + p_doctype.update_reserved_qty_for_subcontract() + else: + p_doctype.update_reserved_qty() + p_doctype.update_project() + p_doctype.update_prevdoc_status('submit') + p_doctype.update_delivery_status() + + p_doctype.update_blanket_order() + p_doctype.update_billing_percentage() + p_doctype.set_status() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 410510b90c..a3993b8265 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -256,17 +256,21 @@ class TestSalesOrder(unittest.TestCase): def test_update_child_qty_rate(self): so = make_sales_order(item_code= "_Test Item", qty=4) - - make_delivery_note(so.name) - + create_dn_against_so(so.name, 4) make_sales_invoice(so.name) - trans_item = {'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7} + existing_reserved_qty = get_reserved_qty() + + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}]) update_child_qty_rate('Sales Order', trans_item, so.name) - + + so.reload() self.assertEqual(so.get("items")[0].rate, 200) self.assertEqual(so.get("items")[0].qty, 7) self.assertEqual(so.get("items")[0].amount, 1400) + self.assertEqual(so.status, 'To Deliver and Bill') + + self.assertEqual(get_reserved_qty(), existing_reserved_qty + 3) def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 6b0b7bb486..c9bb169931 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -601,5 +601,21 @@ class TestMaterialRequest(unittest.TestCase): mr = frappe.get_doc("Material Request", mr.name) self.assertEqual(mr.per_ordered, 100) +def make_material_request(**args): + args = frappe._dict(args) + mr = frappe.new_doc("Material Request") + mr.material_request_type = args.material_request_type or "Purchase" + mr.company = args.company or "_Test Company" + mr.append("items", { + "item_code": args.item_code or "_Test Item", + "qty": args.qty or 10, + "schedule_date": args.schedule_date or today(), + "warehouse": args.warehouse or "_Test Warehouse - _TC" + }) + mr.insert() + if not args.do_not_submit: + mr.submit() + return mr + test_dependencies = ["Currency Exchange", "BOM"] test_records = frappe.get_test_records('Material Request')