From dafaed3cbd6f7d52119e2600c675aa5fa64b04ba Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Wed, 4 May 2022 15:20:29 +0530 Subject: [PATCH] refactor!: Purchase Order --- .../doctype/purchase_order/purchase_order.js | 206 +------- .../doctype/purchase_order/purchase_order.py | 150 ------ .../purchase_order/test_purchase_order.py | 452 +----------------- .../doctype/purchase_order/test_records.json | 34 -- .../purchase_order_item.json | 2 - 5 files changed, 2 insertions(+), 842 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 2cad1fb081..d347026b63 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -7,17 +7,6 @@ frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on("Purchase Order", { setup: function(frm) { - - frm.set_query("reserve_warehouse", "supplied_items", function() { - return { - filters: { - "company": frm.doc.company, - "name": ['!=', frm.doc.supplier_warehouse], - "is_group": 0 - } - } - }); - frm.set_indicator_formatter('item_code', function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" }) @@ -59,39 +48,6 @@ frappe.ui.form.on("Purchase Order", { frm.set_value("tax_withholding_category", frm.supplier_tds); } }, - - refresh: function(frm) { - frm.trigger('get_materials_from_supplier'); - }, - - get_materials_from_supplier: function(frm) { - let po_details = []; - - if (frm.doc.supplied_items && (frm.doc.per_received == 100 || frm.doc.status === 'Closed')) { - frm.doc.supplied_items.forEach(d => { - if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) { - po_details.push(d.name) - } - }); - } - - if (po_details && po_details.length) { - frm.add_custom_button(__('Return of Components'), () => { - frm.call({ - method: 'erpnext.buying.doctype.purchase_order.purchase_order.get_materials_from_supplier', - freeze: true, - freeze_message: __('Creating Stock Entry'), - args: { purchase_order: frm.doc.name, po_details: po_details }, - callback: function(r) { - if (r && r.message) { - const doc = frappe.model.sync(r.message); - frappe.set_route("Form", doc[0].doctype, doc[0].name); - } - } - }); - }, __('Create')); - } - } }); frappe.ui.form.on("Purchase Order Item", { @@ -112,13 +68,11 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e this.frm.custom_make_buttons = { 'Purchase Receipt': 'Purchase Receipt', 'Purchase Invoice': 'Purchase Invoice', - 'Stock Entry': 'Material to Supplier', 'Payment Entry': 'Payment', 'Subcontracting Order': 'Subcontracting Order' } super.setup(); - } refresh(doc, cdt, cdn) { @@ -185,10 +139,6 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e if (doc.status != "On Hold") { if(flt(doc.per_received) < 100 && allow_receipt) { cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create')); - if(doc.is_subcontracted && me.has_unsupplied_items()) { - cur_frm.add_custom_button(__('Material to Supplier'), - function() { me.make_stock_entry(); }, __("Transfer")); - } if (doc.is_subcontracted) { cur_frm.add_custom_button(__('Subcontracting Order'), this.make_subcontracting_order, __('Create')); } @@ -258,142 +208,6 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e set_schedule_date(this.frm); } - has_unsupplied_items() { - return this.frm.doc['supplied_items'].some(item => item.required_qty > item.supplied_qty); - } - - make_stock_entry() { - var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; }); - var me = this; - - if(items.length >= 1){ - me.raw_material_data = []; - me.show_dialog = 1; - let title = __('Transfer Material to Supplier'); - let fields = [ - {fieldtype:'Section Break', label: __('Raw Materials')}, - {fieldname: 'sub_con_rm_items', fieldtype: 'Table', label: __('Items'), - fields: [ - { - fieldtype:'Data', - fieldname:'item_code', - label: __('Item'), - read_only:1, - in_list_view:1 - }, - { - fieldtype:'Data', - fieldname:'rm_item_code', - label: __('Raw Material'), - read_only:1, - in_list_view:1 - }, - { - fieldtype:'Float', - read_only:1, - fieldname:'qty', - label: __('Quantity'), - read_only:1, - in_list_view:1 - }, - { - fieldtype:'Data', - read_only:1, - fieldname:'warehouse', - label: __('Reserve Warehouse'), - in_list_view:1 - }, - { - fieldtype:'Float', - read_only:1, - fieldname:'rate', - label: __('Rate'), - hidden:1 - }, - { - fieldtype:'Float', - read_only:1, - fieldname:'amount', - label: __('Amount'), - hidden:1 - }, - { - fieldtype:'Link', - read_only:1, - fieldname:'uom', - label: __('UOM'), - hidden:1 - } - ], - data: me.raw_material_data, - get_data: function() { - return me.raw_material_data; - } - } - ] - - me.dialog = new frappe.ui.Dialog({ - title: title, fields: fields - }); - - if (me.frm.doc['supplied_items']) { - me.frm.doc['supplied_items'].forEach((item, index) => { - if (item.rm_item_code && item.main_item_code && item.required_qty - item.supplied_qty != 0) { - me.raw_material_data.push ({ - 'name':item.name, - 'item_code': item.main_item_code, - 'rm_item_code': item.rm_item_code, - 'item_name': item.rm_item_code, - 'qty': item.required_qty - item.supplied_qty, - 'warehouse':item.reserve_warehouse, - 'rate':item.rate, - 'amount':item.amount, - 'stock_uom':item.stock_uom - }); - me.dialog.fields_dict.sub_con_rm_items.grid.refresh(); - } - }) - } - - me.dialog.get_field('sub_con_rm_items').check_all_rows() - - me.dialog.show() - this.dialog.set_primary_action(__('Transfer'), function() { - me.values = me.dialog.get_values(); - if(me.values) { - me.values.sub_con_rm_items.map((row,i) => { - if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) { - let row_id = i+1; - frappe.throw(__("Item Code, warehouse and quantity are required on row {0}", [row_id])); - } - }) - me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children()) - me.dialog.hide() - } - }); - } - - me.dialog.get_close_btn().on('click', () => { - me.dialog.hide(); - }); - - } - - _make_rm_stock_entry(rm_items) { - frappe.call({ - method:"erpnext.buying.doctype.purchase_order.purchase_order.make_rm_stock_entry", - args: { - purchase_order: cur_frm.doc.name, - rm_items: rm_items - } - , - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }); - } - make_inter_company_order(frm) { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_inter_company_sales_order", @@ -632,28 +446,10 @@ cur_frm.fields_dict['items'].grid.get_field('project').get_query = function(doc, } } -cur_frm.fields_dict['items'].grid.get_field('bom').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn] - return { - filters: [ - ['BOM', 'item', '=', d.item_code], - ['BOM', 'is_active', '=', '1'], - ['BOM', 'docstatus', '=', '1'], - ['BOM', 'company', '=', doc.company] - ] - } -} - function set_schedule_date(frm) { if(frm.doc.schedule_date){ erpnext.utils.copy_value_in_all_rows(frm.doc, frm.doc.doctype, frm.doc.name, "items", "schedule_date"); } } -frappe.provide("erpnext.buying"); - -frappe.ui.form.on("Purchase Order", "is_subcontracted", function(frm) { - if (frm.doc.is_subcontracted) { - erpnext.buying.get_default_bom(frm); - } -}); +frappe.provide("erpnext.buying"); \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 1945079171..234bec17ef 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -24,7 +24,6 @@ from erpnext.controllers.buying_controller import BuyingController from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.stock.doctype.item.item import get_item_defaults, get_last_purchase_details from erpnext.stock.stock_balance import get_ordered_qty, update_bin_qty -from erpnext.stock.utils import get_bin form_grid_templates = {"items": "templates/form_grid/item_grid.html"} @@ -70,7 +69,6 @@ class PurchaseOrder(BuyingController): self.validate_for_subcontracting() self.validate_minimum_order_qty() self.validate_fg_item_for_subcontracting() - self.create_raw_materials_supplied("supplied_items") self.set_received_qty_for_drop_ship_items() validate_inter_company_party( self.doctype, self.supplier, self.company, self.inter_company_order_reference @@ -307,9 +305,6 @@ class PurchaseOrder(BuyingController): self.set_status(update=True, status=status) self.update_requested_qty() self.update_ordered_qty() - if self.is_subcontracted: - self.update_reserved_qty_for_subcontract() - self.notify_update() clear_doctype_notifications(self) @@ -324,9 +319,6 @@ class PurchaseOrder(BuyingController): self.update_ordered_qty() self.validate_budget() - if self.is_subcontracted: - self.update_reserved_qty_for_subcontract() - frappe.get_doc("Authorization Control").validate_approving_authority( self.doctype, self.company, self.base_grand_total ) @@ -344,9 +336,6 @@ class PurchaseOrder(BuyingController): if self.has_drop_ship_item(): self.update_delivered_qty_in_sales_order() - if self.is_subcontracted: - self.update_reserved_qty_for_subcontract() - self.check_on_hold_or_closed_status() frappe.db.set(self, "status", "Cancelled") @@ -416,12 +405,6 @@ class PurchaseOrder(BuyingController): if item.delivered_by_supplier == 1: item.received_qty = item.qty - def update_reserved_qty_for_subcontract(self): - for d in self.supplied_items: - 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: @@ -599,78 +582,6 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions return doc -@frappe.whitelist() -def make_rm_stock_entry(purchase_order, rm_items): - rm_items_list = rm_items - - if isinstance(rm_items, str): - rm_items_list = json.loads(rm_items) - elif not rm_items: - frappe.throw(_("No Items available for transfer")) - - if rm_items_list: - fg_items = list(set(d["item_code"] for d in rm_items_list)) - else: - frappe.throw(_("No Items selected for transfer")) - - if purchase_order: - purchase_order = frappe.get_doc("Purchase Order", purchase_order) - - if fg_items: - items = tuple(set(d["rm_item_code"] for d in rm_items_list)) - item_wh = get_item_details(items) - - stock_entry = frappe.new_doc("Stock Entry") - stock_entry.purpose = "Send to Subcontractor" - stock_entry.purchase_order = purchase_order.name - stock_entry.supplier = purchase_order.supplier - stock_entry.supplier_name = purchase_order.supplier_name - stock_entry.supplier_address = purchase_order.supplier_address - stock_entry.address_display = purchase_order.address_display - stock_entry.company = purchase_order.company - stock_entry.to_warehouse = purchase_order.supplier_warehouse - stock_entry.set_stock_entry_type() - - for item_code in fg_items: - for rm_item_data in rm_items_list: - if rm_item_data["item_code"] == item_code: - rm_item_code = rm_item_data["rm_item_code"] - items_dict = { - rm_item_code: { - "po_detail": rm_item_data.get("name"), - "item_name": rm_item_data["item_name"], - "description": item_wh.get(rm_item_code, {}).get("description", ""), - "qty": rm_item_data["qty"], - "from_warehouse": rm_item_data["warehouse"], - "stock_uom": rm_item_data["stock_uom"], - "serial_no": rm_item_data.get("serial_no"), - "batch_no": rm_item_data.get("batch_no"), - "main_item_code": rm_item_data["item_code"], - "allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"), - } - } - stock_entry.add_to_stock_entry_detail(items_dict) - return stock_entry.as_dict() - else: - frappe.throw(_("No Items selected for transfer")) - return purchase_order.name - - -def get_item_details(items): - item_details = {} - for d in frappe.db.sql( - """select item_code, description, allow_alternative_item from `tabItem` - where name in ({0})""".format( - ", ".join(["%s"] * len(items)) - ), - items, - as_dict=1, - ): - item_details[d.item_code] = d - - return item_details - - def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context @@ -700,67 +611,6 @@ def make_inter_company_sales_order(source_name, target_doc=None): return make_inter_company_transaction("Purchase Order", source_name, target_doc) -@frappe.whitelist() -def get_materials_from_supplier(purchase_order, po_details): - if isinstance(po_details, str): - po_details = json.loads(po_details) - - doc = frappe.get_cached_doc("Purchase Order", purchase_order) - doc.initialized_fields() - doc.purchase_orders = [doc.name] - doc.get_available_materials() - - if not doc.available_materials: - frappe.throw( - _("Materials are already received against the purchase order {0}").format(purchase_order) - ) - - return make_return_stock_entry_for_subcontract(doc.available_materials, doc, po_details) - - -def make_return_stock_entry_for_subcontract(available_materials, po_doc, po_details): - ste_doc = frappe.new_doc("Stock Entry") - ste_doc.purpose = "Material Transfer" - ste_doc.purchase_order = po_doc.name - ste_doc.company = po_doc.company - ste_doc.is_return = 1 - - for key, value in available_materials.items(): - if not value.qty: - continue - - if value.batch_no: - for batch_no, qty in value.batch_no.items(): - if qty > 0: - add_items_in_ste(ste_doc, value, value.qty, po_details, batch_no) - else: - add_items_in_ste(ste_doc, value, value.qty, po_details) - - ste_doc.set_stock_entry_type() - ste_doc.calculate_rate_and_amount() - - return ste_doc - - -def add_items_in_ste(ste_doc, row, qty, po_details, batch_no=None): - item = ste_doc.append("items", row.item_details) - - po_detail = list(set(row.po_details).intersection(po_details)) - item.update( - { - "qty": qty, - "batch_no": batch_no, - "basic_rate": row.item_details["rate"], - "po_detail": po_detail[0] if po_detail else "", - "s_warehouse": row.item_details["t_warehouse"], - "t_warehouse": row.item_details["s_warehouse"], - "item_code": row.item_details["rm_item_code"], - "subcontracted_item": row.item_details["main_item_code"], - "serial_no": "\n".join(row.serial_no) if row.serial_no else "", - } - ) - - @frappe.whitelist() def make_subcontracting_order(source_name, target_doc=None): return get_mapped_subcontracting_order(source_name, target_doc) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 1a7f2dd5d9..e1da54e410 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -13,9 +13,6 @@ from erpnext.buying.doctype.purchase_order.purchase_order import ( make_purchase_invoice as make_pi_from_po, ) from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt -from erpnext.buying.doctype.purchase_order.purchase_order import ( - make_rm_stock_entry as make_subcontract_transfer_entry, -) from erpnext.controllers.accounts_controller import update_child_qty_rate from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order from erpnext.stock.doctype.item.test_item import make_item @@ -24,7 +21,6 @@ from erpnext.stock.doctype.material_request.test_material_request import make_ma from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( make_purchase_invoice as make_pi_from_pr, ) -from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry class TestPurchaseOrder(FrappeTestCase): @@ -389,31 +385,6 @@ class TestPurchaseOrder(FrappeTestCase): new_item_with_tax.delete() frappe.get_doc("Item Tax Template", "Test Update Items Template - _TC").delete() - def test_update_child_uom_conv_factor_change(self): - po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1) - total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")]) - - trans_item = json.dumps( - [ - { - "item_code": po.get("items")[0].item_code, - "rate": po.get("items")[0].rate, - "qty": po.get("items")[0].qty, - "uom": "_Test UOM 1", - "conversion_factor": 2, - "docname": po.get("items")[0].name, - } - ] - ) - update_child_qty_rate("Purchase Order", trans_item, po.name) - po.reload() - - total_reqd_qty_after_change = sum( - d.get("required_qty") for d in po.as_dict().get("supplied_items") - ) - - self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty) - def test_update_qty(self): po = create_purchase_order() @@ -572,10 +543,6 @@ class TestPurchaseOrder(FrappeTestCase): ) automatically_fetch_payment_terms(enable=0) - def test_subcontracting(self): - po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1) - self.assertEqual(len(po.get("supplied_items")), 2) - def test_warehouse_company_validation(self): from erpnext.stock.utils import InvalidWarehouseCompany @@ -740,379 +707,6 @@ class TestPurchaseOrder(FrappeTestCase): pi.insert() self.assertTrue(pi.get("payment_schedule")) - def test_reserved_qty_subcontract_po(self): - # Make stock available for raw materials - make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100) - make_stock_entry( - target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=30, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse 1 - _TC", - item_code="_Test Item Home Desktop 100", - qty=30, - basic_rate=100, - ) - - bin1 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"], - as_dict=1, - ) - - # Submit PO - po = create_purchase_order(item_code="_Test FG Item", is_subcontracted=1) - - bin2 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"], - as_dict=1, - ) - - self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) - self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10) - self.assertNotEqual(bin1.modified, bin2.modified) - - # Create stock transfer - rm_item = [ - { - "item_code": "_Test FG Item", - "rm_item_code": "_Test Item", - "item_name": "_Test Item", - "qty": 6, - "warehouse": "_Test Warehouse - _TC", - "rate": 100, - "amount": 600, - "stock_uom": "Nos", - } - ] - rm_item_string = json.dumps(rm_item) - se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) - se.to_warehouse = "_Test Warehouse 1 - _TC" - se.save() - se.submit() - - bin3 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) - - # close PO - po.update_status("Closed") - bin4 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) - - # Re-open PO - po.update_status("Submitted") - bin5 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) - - make_stock_entry( - target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=40, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse 1 - _TC", - item_code="_Test Item Home Desktop 100", - qty=40, - basic_rate=100, - ) - - # make Purchase Receipt against PO - pr = make_purchase_receipt(po.name) - pr.supplier_warehouse = "_Test Warehouse 1 - _TC" - pr.save() - pr.submit() - - bin6 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) - - # Cancel PR - pr.cancel() - bin7 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) - - # Make Purchase Invoice - pi = make_pi_from_po(po.name) - pi.update_stock = 1 - pi.supplier_warehouse = "_Test Warehouse 1 - _TC" - pi.insert() - pi.submit() - bin8 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) - - # Cancel PR - pi.cancel() - bin9 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) - - # Cancel Stock Entry - se.cancel() - bin10 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) - - # Cancel PO - po.reload() - po.cancel() - bin11 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) - - def test_exploded_items_in_subcontracted(self): - item_code = "_Test Subcontracted FG Item 11" - make_subcontracted_item(item_code=item_code) - - po = create_purchase_order( - item_code=item_code, - qty=1, - is_subcontracted=1, - supplier_warehouse="_Test Warehouse 1 - _TC", - include_exploded_items=1, - ) - - name = frappe.db.get_value("BOM", {"item": item_code}, "name") - bom = frappe.get_doc("BOM", name) - - exploded_items = sorted( - [d.item_code for d in bom.exploded_items if not d.get("sourced_by_supplier")] - ) - supplied_items = sorted([d.rm_item_code for d in po.supplied_items]) - self.assertEqual(exploded_items, supplied_items) - - po1 = create_purchase_order( - item_code=item_code, - qty=1, - is_subcontracted=1, - supplier_warehouse="_Test Warehouse 1 - _TC", - include_exploded_items=0, - ) - - supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items]) - bom_items = sorted([d.item_code for d in bom.items if not d.get("sourced_by_supplier")]) - - self.assertEqual(supplied_items1, bom_items) - - def test_backflush_based_on_stock_entry(self): - item_code = "_Test Subcontracted FG Item 1" - make_subcontracted_item(item_code=item_code) - make_item("Sub Contracted Raw Material 1", {"is_stock_item": 1, "is_sub_contracted_item": 1}) - - update_backflush_based_on("Material Transferred for Subcontract") - - order_qty = 5 - po = create_purchase_order( - item_code=item_code, - qty=order_qty, - is_subcontracted=1, - supplier_warehouse="_Test Warehouse 1 - _TC", - ) - - make_stock_entry( - target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse - _TC", item_code="Test Extra Item 1", qty=100, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse - _TC", item_code="Test Extra Item 2", qty=10, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse - _TC", - item_code="Sub Contracted Raw Material 1", - qty=10, - basic_rate=100, - ) - - rm_items = [ - { - "item_code": item_code, - "rm_item_code": "Sub Contracted Raw Material 1", - "item_name": "_Test Item", - "qty": 10, - "warehouse": "_Test Warehouse - _TC", - "stock_uom": "Nos", - }, - { - "item_code": item_code, - "rm_item_code": "_Test Item Home Desktop 100", - "item_name": "_Test Item Home Desktop 100", - "qty": 20, - "warehouse": "_Test Warehouse - _TC", - "stock_uom": "Nos", - }, - { - "item_code": item_code, - "rm_item_code": "Test Extra Item 1", - "item_name": "Test Extra Item 1", - "qty": 10, - "warehouse": "_Test Warehouse - _TC", - "stock_uom": "Nos", - }, - { - "item_code": item_code, - "rm_item_code": "Test Extra Item 2", - "stock_uom": "Nos", - "qty": 10, - "warehouse": "_Test Warehouse - _TC", - "item_name": "Test Extra Item 2", - }, - ] - - rm_item_string = json.dumps(rm_items) - se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) - se.submit() - - pr = make_purchase_receipt(po.name) - - received_qty = 2 - # partial receipt - pr.get("items")[0].qty = received_qty - pr.save() - pr.submit() - - transferred_items = sorted( - [d.item_code for d in se.get("items") if se.purchase_order == po.name] - ) - issued_items = sorted([d.rm_item_code for d in pr.get("supplied_items")]) - - self.assertEqual(transferred_items, issued_items) - self.assertEqual(pr.get("items")[0].rm_supp_cost, 2000) - - transferred_rm_map = frappe._dict() - for item in rm_items: - transferred_rm_map[item.get("rm_item_code")] = item - - update_backflush_based_on("BOM") - - def test_supplied_qty_against_subcontracted_po(self): - item_code = "_Test Subcontracted FG Item 5" - make_item("Sub Contracted Raw Material 4", {"is_stock_item": 1, "is_sub_contracted_item": 1}) - - make_subcontracted_item(item_code=item_code, raw_materials=["Sub Contracted Raw Material 4"]) - - update_backflush_based_on("Material Transferred for Subcontract") - - order_qty = 250 - po = create_purchase_order( - item_code=item_code, - qty=order_qty, - is_subcontracted=1, - supplier_warehouse="_Test Warehouse 1 - _TC", - do_not_save=True, - ) - - # Add same subcontracted items multiple times - po.append( - "items", - { - "item_code": item_code, - "qty": order_qty, - "schedule_date": add_days(nowdate(), 1), - "warehouse": "_Test Warehouse - _TC", - }, - ) - - po.set_missing_values() - po.submit() - - # Material receipt entry for the raw materials which will be send to supplier - make_stock_entry( - target="_Test Warehouse - _TC", - item_code="Sub Contracted Raw Material 4", - qty=500, - basic_rate=100, - ) - - rm_items = [ - { - "item_code": item_code, - "rm_item_code": "Sub Contracted Raw Material 4", - "item_name": "_Test Item", - "qty": 250, - "warehouse": "_Test Warehouse - _TC", - "stock_uom": "Nos", - "name": po.supplied_items[0].name, - }, - { - "item_code": item_code, - "rm_item_code": "Sub Contracted Raw Material 4", - "item_name": "_Test Item", - "qty": 250, - "warehouse": "_Test Warehouse - _TC", - "stock_uom": "Nos", - }, - ] - - # Raw Materials transfer entry from stores to supplier's warehouse - rm_item_string = json.dumps(rm_items) - se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) - se.submit() - - # Test po_detail field has value or not - for item_row in se.items: - self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name) - - po_doc = frappe.get_doc("Purchase Order", po.name) - for row in po_doc.supplied_items: - # Valid that whether transferred quantity is matching with supplied qty or not in the purchase order - self.assertEqual(row.supplied_qty, 250.0) - - update_backflush_based_on("BOM") - def test_advance_payment_entry_unlink_against_purchase_order(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry @@ -1211,50 +805,6 @@ def make_pr_against_po(po, received_qty=0): return pr -def make_subcontracted_item(**args): - from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom - - args = frappe._dict(args) - - if not frappe.db.exists("Item", args.item_code): - make_item( - args.item_code, - { - "is_stock_item": 1, - "is_sub_contracted_item": 1, - "has_batch_no": args.get("has_batch_no") or 0, - }, - ) - - if not args.raw_materials: - if not frappe.db.exists("Item", "Test Extra Item 1"): - make_item( - "Test Extra Item 1", - { - "is_stock_item": 1, - }, - ) - - if not frappe.db.exists("Item", "Test Extra Item 2"): - make_item( - "Test Extra Item 2", - { - "is_stock_item": 1, - }, - ) - - args.raw_materials = ["_Test FG Item", "Test Extra Item 1"] - - if not frappe.db.get_value("BOM", {"item": args.item_code}, "name"): - make_bom(item=args.item_code, raw_materials=args.get("raw_materials")) - - -def update_backflush_based_on(based_on): - doc = frappe.get_doc("Buying Settings") - doc.backflush_raw_materials_of_subcontract_based_on = based_on - doc.save() - - def get_same_items(): return [ { @@ -1341,4 +891,4 @@ def get_requested_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC") test_dependencies = ["BOM", "Item Price"] -test_records = frappe.get_test_records("Purchase Order") +test_records = frappe.get_test_records("Purchase Order") \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/test_records.json b/erpnext/buying/doctype/purchase_order/test_records.json index 896050ce43..4df994a68c 100644 --- a/erpnext/buying/doctype/purchase_order/test_records.json +++ b/erpnext/buying/doctype/purchase_order/test_records.json @@ -1,38 +1,4 @@ [ - { - "advance_paid": 0.0, - "buying_price_list": "_Test Price List", - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "doctype": "Purchase Order", - "base_grand_total": 5000.0, - "grand_total": 5000.0, - "is_subcontracted": 1, - "naming_series": "_T-Purchase Order-", - "base_net_total": 5000.0, - "items": [ - { - "base_amount": 5000.0, - "conversion_factor": 1.0, - "description": "_Test FG Item", - "doctype": "Purchase Order Item", - "item_code": "_Test FG Item", - "item_name": "_Test FG Item", - "parentfield": "items", - "qty": 10.0, - "rate": 500.0, - "schedule_date": "2013-03-01", - "stock_uom": "_Test UOM", - "uom": "_Test UOM", - "warehouse": "_Test Warehouse - _TC" - } - ], - "supplier": "_Test Supplier", - "supplier_name": "_Test Supplier", - "transaction_date": "2013-02-12", - "schedule_date": "2013-02-13" - }, { "advance_paid": 0.0, "buying_price_list": "_Test Price List", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 7f797cfd2f..12eef79dff 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -574,7 +574,6 @@ "read_only": 1 }, { - "depends_on": "eval:parent.is_subcontracted", "fieldname": "bom", "fieldtype": "Link", "label": "BOM", @@ -584,7 +583,6 @@ }, { "default": "0", - "depends_on": "eval:parent.is_subcontracted", "fieldname": "include_exploded_items", "fieldtype": "Check", "hidden": 1,