From 84af60da7fce56b2476792fac31e9dcb416da7bc Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 13 Nov 2025 12:51:07 +0530 Subject: [PATCH] feat: company wise default warehouses --- .../doctype/job_card/job_card.py | 2 +- .../manufacturing_settings.js | 12 ------- .../manufacturing_settings.json | 34 +------------------ .../manufacturing_settings.py | 3 -- .../production_plan/production_plan.py | 2 +- .../doctype/work_order/work_order.js | 3 ++ .../doctype/work_order/work_order.py | 17 +++++----- erpnext/patches.txt | 1 + .../v16_0/set_company_wise_warehouses.py | 14 ++++++++ .../doctype/sales_order/test_sales_order.py | 2 +- erpnext/setup/doctype/company/company.json | 31 ++++++++++++++++- erpnext/setup/doctype/company/company.py | 3 ++ .../material_request/material_request.js | 1 + .../material_request/material_request.py | 4 +-- .../material_request/test_material_request.py | 6 ++-- 15 files changed, 70 insertions(+), 65 deletions(-) create mode 100644 erpnext/patches/v16_0/set_company_wise_warehouses.py diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 683ce626bc1..1bd0cc4a58f 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -1085,7 +1085,7 @@ class JobCard(Document): def set_wip_warehouse(self): if not self.wip_warehouse: - self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") + self.wip_warehouse = frappe.get_cached_value("Company", self.company, "default_wip_warehouse") def validate_operation_id(self): if ( diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js index f54478a1c10..17c9c085b12 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js @@ -18,18 +18,6 @@ frappe.tour["Manufacturing Settings"] = [ "The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing.

When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field." ), }, - { - fieldname: "default_wip_warehouse", - title: __("Work In Progress Warehouse"), - description: __( - "This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders." - ), - }, - { - fieldname: "default_fg_warehouse", - title: __("Finished Goods Warehouse"), - description: __("This Warehouse will be auto-updated in the Target Warehouse field of Work Order."), - }, { fieldname: "update_bom_costs_automatically", title: __("Update BOM Cost Automatically"), diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index 80e97f8fb46..a43804c0191 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -16,11 +16,6 @@ "update_bom_costs_automatically", "column_break_lhyt", "allow_editing_of_items_and_quantities_in_work_order", - "section_break_6", - "default_wip_warehouse", - "default_fg_warehouse", - "column_break_11", - "default_scrap_warehouse", "over_production_for_sales_and_work_order_section", "overproduction_percentage_for_sales_order", "column_break_16", @@ -86,11 +81,6 @@ "fieldtype": "Int", "label": "Time Between Operations (Mins)" }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "label": "Default Warehouses for Production" - }, { "fieldname": "overproduction_percentage_for_sales_order", "fieldtype": "Percent", @@ -122,34 +112,12 @@ "fieldtype": "Check", "label": "Update BOM Cost Automatically" }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "fieldname": "default_wip_warehouse", - "fieldtype": "Link", - "label": "Default Work In Progress Warehouse", - "options": "Warehouse" - }, - { - "fieldname": "default_fg_warehouse", - "fieldtype": "Link", - "label": "Default Finished Goods Warehouse", - "options": "Warehouse" - }, { "default": "0", "fieldname": "disable_capacity_planning", "fieldtype": "Check", "label": "Disable Capacity Planning" }, - { - "fieldname": "default_scrap_warehouse", - "fieldtype": "Link", - "label": "Default Scrap Warehouse", - "options": "Warehouse" - }, { "fieldname": "over_production_for_sales_and_work_order_section", "fieldtype": "Section Break", @@ -275,7 +243,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-11-07 14:52:56.241459", + "modified": "2025-11-13 12:30:29.006822", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing Settings", diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py index b3d7742929e..e60a9627a21 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py @@ -23,9 +23,6 @@ class ManufacturingSettings(Document): allow_production_on_holidays: DF.Check backflush_raw_materials_based_on: DF.Literal["BOM", "Material Transferred for Manufacture"] capacity_planning_for_days: DF.Int - default_fg_warehouse: DF.Link | None - default_scrap_warehouse: DF.Link | None - default_wip_warehouse: DF.Link | None disable_capacity_planning: DF.Check enforce_time_logs: DF.Check get_rm_cost_from_consumption_entry: DF.Check diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3de22cb72b0..9a607778ebc 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -756,7 +756,7 @@ class ProductionPlan(Document): wo_list, po_list = [], [] subcontracted_po = {} - default_warehouses = get_default_warehouse() + default_warehouses = get_default_warehouse(self.company) self.make_work_order_for_finished_goods(wo_list, default_warehouses) self.make_work_order_for_subassembly_items(wo_list, subcontracted_po, default_warehouses) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 6c9c707fa22..82b26e28912 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -932,6 +932,9 @@ erpnext.work_order = { if (!(frm.doc.wip_warehouse || frm.doc.fg_warehouse)) { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse", + args: { + company: frm.doc.company, + }, callback: function (r) { if (!r.exe) { frm.set_value("wip_warehouse", r.message.wip_warehouse); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 6bc0dde543d..b651b211e11 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -453,9 +453,9 @@ class WorkOrder(Document): def set_default_warehouse(self): if not self.wip_warehouse and not self.skip_transfer: - self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") + self.wip_warehouse = frappe.get_cached_value("Company", self.company, "default_wip_warehouse") if not self.fg_warehouse: - self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse") + self.fg_warehouse = frappe.get_cached_value("Company", self.company, "default_fg_warehouse") def check_wip_warehouse_skip(self): if self.skip_transfer and not self.from_wip_warehouse: @@ -2318,13 +2318,14 @@ def make_stock_entry( @frappe.whitelist() -def get_default_warehouse(): - doc = frappe.get_cached_doc("Manufacturing Settings") - +def get_default_warehouse(company): + wip, fg, scrap = frappe.get_cached_value( + "Company", company, ["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"] + ) return { - "wip_warehouse": doc.default_wip_warehouse, - "fg_warehouse": doc.default_fg_warehouse, - "scrap_warehouse": doc.default_scrap_warehouse, + "wip_warehouse": wip, + "fg_warehouse": fg, + "scrap_warehouse": scrap, } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a1edea61b40..ea8bea587da 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -446,3 +446,4 @@ erpnext.patches.v16_0.add_new_stock_entry_types erpnext.patches.v15_0.set_asset_status_if_not_already_set erpnext.patches.v15_0.toggle_legacy_controller_for_period_closing erpnext.patches.v16_0.update_serial_batch_entries +erpnext.patches.v16_0.set_company_wise_warehouses \ No newline at end of file diff --git a/erpnext/patches/v16_0/set_company_wise_warehouses.py b/erpnext/patches/v16_0/set_company_wise_warehouses.py new file mode 100644 index 00000000000..8d9e7320076 --- /dev/null +++ b/erpnext/patches/v16_0/set_company_wise_warehouses.py @@ -0,0 +1,14 @@ +import frappe + + +def execute(): + warehouses = frappe.get_single_value( + "Manufacturing Settings", + ["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"], + as_dict=True, + ) + + for name, warehouse in warehouses.items(): + if warehouse: + company = frappe.get_value("Warehouse", warehouse, "company") + frappe.db.set_value("Company", company, name, warehouse) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 2d01539ca04..a75e6eca08f 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1796,7 +1796,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase): mr.submit() # WO from MR - wo_name = raise_work_orders(mr.name)[0] + wo_name = raise_work_orders(mr.name, mr.company)[0] wo = frappe.get_doc("Work Order", wo_name) wo.wip_warehouse = "Work In Progress - _TC" wo.skip_transfer = True diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index b5cbc3907d7..32c89dc531d 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -124,6 +124,10 @@ "default_in_transit_warehouse", "manufacturing_section", "default_operating_cost_account", + "column_break_9prc", + "default_wip_warehouse", + "default_fg_warehouse", + "default_scrap_warehouse", "dashboard_tab" ], "fields": [ @@ -885,6 +889,31 @@ "fieldname": "enable_item_wise_inventory_account", "fieldtype": "Check", "label": "Enable Item-wise Inventory Account" + }, + { + "fieldname": "default_wip_warehouse", + "fieldtype": "Link", + "label": " Default Work In Progress Warehouse ", + "link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]", + "options": "Warehouse" + }, + { + "fieldname": "default_fg_warehouse", + "fieldtype": "Link", + "label": "Default Finished Goods Warehouse", + "link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]", + "options": "Warehouse" + }, + { + "fieldname": "default_scrap_warehouse", + "fieldtype": "Link", + "label": "Default Scrap Warehouse", + "link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]", + "options": "Warehouse" + }, + { + "fieldname": "column_break_9prc", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -892,7 +921,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2025-10-23 13:15:52.411984", + "modified": "2025-11-16 16:50:27.624096", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 9eebf21b1d0..6656af2ef11 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -59,6 +59,7 @@ class Company(NestedSet): default_deferred_revenue_account: DF.Link | None default_discount_account: DF.Link | None default_expense_account: DF.Link | None + default_fg_warehouse: DF.Link | None default_finance_book: DF.Link | None default_holiday_list: DF.Link | None default_in_transit_warehouse: DF.Link | None @@ -69,8 +70,10 @@ class Company(NestedSet): default_payable_account: DF.Link | None default_provisional_account: DF.Link | None default_receivable_account: DF.Link | None + default_scrap_warehouse: DF.Link | None default_selling_terms: DF.Link | None default_warehouse_for_sales_return: DF.Link | None + default_wip_warehouse: DF.Link | None depreciation_cost_center: DF.Link | None depreciation_expense_account: DF.Link | None disposal_account: DF.Link | None diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index f322fd0c492..00f80fbbeca 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -495,6 +495,7 @@ frappe.ui.form.on("Material Request", { method: "erpnext.stock.doctype.material_request.material_request.raise_work_orders", args: { material_request: frm.doc.name, + company: frm.doc.company, }, freeze: true, callback: function (r) { diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index b8817996bac..6f12fd05ea7 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -833,11 +833,11 @@ def make_stock_entry(source_name, target_doc=None): @frappe.whitelist() -def raise_work_orders(material_request): +def raise_work_orders(material_request, company): mr = frappe.get_doc("Material Request", material_request) errors = [] work_orders = [] - default_wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") + default_wip_warehouse = frappe.get_cached_value("Company", company, "default_wip_warehouse") for d in mr.items: if (d.stock_qty - d.ordered_qty) > 0: diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 0a6251f7794..4a888dea879 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -747,7 +747,7 @@ class TestMaterialRequest(IntegrationTestCase): (mr.items[0].item_code, mr.items[0].warehouse), )[0][0] - prod_order = raise_work_orders(mr.name) + prod_order = raise_work_orders(mr.name, mr.company) po = frappe.get_doc("Work Order", prod_order[0]) po.wip_warehouse = "_Test Warehouse 1 - _TC" po.submit() @@ -789,7 +789,7 @@ class TestMaterialRequest(IntegrationTestCase): self.assertEqual(requested_qty, existing_requested_qty + 120) - work_order = raise_work_orders(mr.name) + work_order = raise_work_orders(mr.name, mr.company) wo = frappe.get_doc("Work Order", work_order[0]) wo.qty = 50 wo.wip_warehouse = "_Test Warehouse 1 - _TC" @@ -924,7 +924,7 @@ class TestMaterialRequest(IntegrationTestCase): item_code="_Test FG Item", material_request_type="Manufacture", do_not_submit=False ) - work_order = raise_work_orders(mr.name) + work_order = raise_work_orders(mr.name, mr.company) wo = frappe.get_doc("Work Order", work_order[0]) wo.wip_warehouse = "_Test Warehouse 1 - _TC" wo.submit()