diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index a3d41ab29a..5e9c069b1d 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -311,6 +311,7 @@ class SellingController(StockController): "sales_invoice_item": d.get("sales_invoice_item"), "dn_detail": d.get("dn_detail"), "incoming_rate": p.get("incoming_rate"), + "item_row": p, } ) ) @@ -334,6 +335,7 @@ class SellingController(StockController): "sales_invoice_item": d.get("sales_invoice_item"), "dn_detail": d.get("dn_detail"), "incoming_rate": d.get("incoming_rate"), + "item_row": d, } ) ) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 36bed36484..d4f9aba41d 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -390,6 +390,10 @@ class StockController(AccountsController): return sl_dict def update_inventory_dimensions(self, row, sl_dict) -> None: + # To handle delivery note and sales invoice + if row.get("item_row"): + row = row.get("item_row") + dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self) for dimension in dimensions: if not dimension: @@ -407,9 +411,17 @@ class StockController(AccountsController): "DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" ) + if not fieldname: + fieldname = frappe.get_cached_value( + "Custom Field", {"dt": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" + ) + if fieldname and self.get(fieldname): sl_dict[dimension.target_fieldname] = self.get(fieldname) + if sl_dict[dimension.target_fieldname] and self.docstatus == 1: + row.db_set(dimension.source_fieldname, sl_dict[dimension.target_fieldname]) + def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False): from erpnext.stock.stock_ledger import make_sl_entries diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js index 07cb73b1d5..79e7895f6d 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js @@ -30,6 +30,7 @@ frappe.ui.form.on('Inventory Dimension', { onload(frm) { frm.trigger('render_traget_field'); + frm.trigger("set_parent_fields"); }, refresh(frm) { @@ -52,6 +53,30 @@ frappe.ui.form.on('Inventory Dimension', { } }, + document_type(frm) { + frm.trigger("set_parent_fields"); + }, + + set_parent_fields(frm) { + if (frm.doc.apply_to_all_doctypes) { + frm.set_df_property("fetch_from_parent", "options", frm.doc.reference_document); + } else if (frm.doc.document_type && frm.doc.istable) { + frappe.call({ + method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_parent_fields', + args: { + child_doctype: frm.doc.document_type, + dimension_name: frm.doc.reference_document + }, + callback: (r) => { + if (r.message && r.message.length) { + frm.set_df_property("fetch_from_parent", "options", + [""].concat(r.message)); + } + } + }); + } + }, + delete_dimension(frm) { let msg = (` Custom fields related to this dimension will be deleted on deletion of dimension. diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json index 03e7fda841..09f4f63031 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json @@ -144,16 +144,15 @@ "fieldtype": "Column Break" }, { - "depends_on": "istable", "description": "Set fieldname or DocType name like Supplier, Customer etc.", "fieldname": "fetch_from_parent", - "fieldtype": "Data", + "fieldtype": "Select", "label": "Fetch Value From Parent Form" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-17 11:43:24.722441", + "modified": "2022-09-02 13:29:04.098469", "modified_by": "Administrator", "module": "Stock", "name": "Inventory Dimension", diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 4ff8f33b40..9e8c10b394 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -236,3 +236,30 @@ def get_inventory_dimensions(): def delete_dimension(dimension): doc = frappe.get_doc("Inventory Dimension", dimension) doc.delete() + + +@frappe.whitelist() +def get_parent_fields(child_doctype, dimension_name): + parent_doctypes = frappe.get_all( + "DocField", fields=["parent"], filters={"options": child_doctype} + ) + + fields = [] + + fields.extend( + frappe.get_all( + "DocField", + fields=["fieldname as value", "label"], + filters={"options": dimension_name, "parent": ("in", [d.parent for d in parent_doctypes])}, + ) + ) + + fields.extend( + frappe.get_all( + "Custom Field", + fields=["fieldname as value", "label"], + filters={"options": dimension_name, "dt": ("in", [d.parent for d in parent_doctypes])}, + ) + ) + + return fields diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index cc90b74ee8..19ddc449f0 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -2,14 +2,17 @@ # See license.txt import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.tests.utils import FrappeTestCase +from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.inventory_dimension.inventory_dimension import ( CanNotBeChildDoc, CanNotBeDefaultDimension, DoNotChangeError, delete_dimension, ) +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse @@ -136,6 +139,58 @@ class TestInventoryDimension(FrappeTestCase): self.assertTrue(inv_dim1.has_stock_ledger()) self.assertRaises(DoNotChangeError, inv_dim1.save) + def test_inventory_dimension_for_purchase_receipt_and_delivery_note(self): + create_inventory_dimension( + reference_document="Rack", + type_of_transaction="Both", + dimension_name="Rack", + apply_to_all_doctypes=1, + fetch_from_parent="Rack", + ) + + create_custom_field( + "Purchase Receipt", dict(fieldname="rack", label="Rack", fieldtype="Link", options="Rack") + ) + + create_custom_field( + "Delivery Note", dict(fieldname="rack", label="Rack", fieldtype="Link", options="Rack") + ) + + frappe.reload_doc("stock", "doctype", "purchase_receipt_item") + frappe.reload_doc("stock", "doctype", "delivery_note_item") + + pr_doc = make_purchase_receipt(qty=2, do_not_submit=True) + pr_doc.rack = "Rack 1" + pr_doc.save() + pr_doc.submit() + + pr_doc.load_from_db() + + self.assertEqual(pr_doc.items[0].rack, "Rack 1") + sle_rack = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": pr_doc.items[0].name, "voucher_type": pr_doc.doctype}, + "rack", + ) + + self.assertEqual(sle_rack, "Rack 1") + + dn_doc = create_delivery_note(qty=2, do_not_submit=True) + dn_doc.rack = "Rack 1" + dn_doc.save() + dn_doc.submit() + + dn_doc.load_from_db() + + self.assertEqual(dn_doc.items[0].rack, "Rack 1") + sle_rack = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": dn_doc.items[0].name, "voucher_type": dn_doc.doctype}, + "rack", + ) + + self.assertEqual(sle_rack, "Rack 1") + def prepare_test_data(): if not frappe.db.exists("DocType", "Shelf"): @@ -160,6 +215,28 @@ def prepare_test_data(): create_warehouse("Shelf Warehouse") + if not frappe.db.exists("DocType", "Rack"): + frappe.get_doc( + { + "doctype": "DocType", + "name": "Rack", + "module": "Stock", + "custom": 1, + "naming_rule": "By fieldname", + "autoname": "field:rack_name", + "fields": [{"label": "Rack Name", "fieldname": "rack_name", "fieldtype": "Data"}], + "permissions": [ + {"role": "System Manager", "permlevel": 0, "read": 1, "write": 1, "create": 1, "delete": 1} + ], + } + ).insert(ignore_permissions=True) + + for rack in ["Rack 1"]: + if not frappe.db.exists("Rack", rack): + frappe.get_doc({"doctype": "Rack", "rack_name": rack}).insert(ignore_permissions=True) + + create_warehouse("Rack Warehouse") + def create_inventory_dimension(**args): args = frappe._dict(args)