fix(stock): ignore disabled price lists on transactions (backport #58926)

Disabled price lists are not set on transactions.

(cherry picked from commit fd492100b0)
This commit is contained in:
Mihir Kandoi
2026-10-15 14:13:51 +05:30
parent c527b6bd4e
commit fd11766ba9
8 changed files with 99 additions and 13 deletions
+15 -6
View File
@@ -26,6 +26,7 @@ import erpnext
from erpnext import get_company_currency
from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
from erpnext.stock.doctype.price_list.price_list import is_price_list_enabled
from erpnext.utilities.regional import temporary_flag
try:
@@ -371,12 +372,17 @@ def set_other_values(party_details, party, party_type):
def get_default_price_list(party):
"""Return default price list for party (Document object)"""
if party.get("default_price_list"):
return party.default_price_list
"""Return the first enabled default price list for party (Document object)"""
price_list = party.get("default_price_list")
if is_price_list_enabled(price_list):
return price_list
if party.doctype == "Customer":
return frappe.get_cached_value("Customer Group", party.customer_group, "default_price_list")
if party.doctype != "Customer":
return
price_list = frappe.get_cached_value("Customer Group", party.customer_group, "default_price_list")
if is_price_list_enabled(price_list):
return price_list
def get_permitted_price_lists(doctype=None):
@@ -409,7 +415,7 @@ def set_price_list(party_details, party, party_type, given_price_list, pos=None,
elif pos and party_type == "Customer":
customer_price_list = frappe.get_value("Customer", party.name, "default_price_list")
if customer_price_list:
if is_price_list_enabled(customer_price_list):
price_list = customer_price_list
else:
pos_price_list = frappe.get_value("POS Profile", pos, "selling_price_list")
@@ -421,6 +427,9 @@ def set_price_list(party_details, party, party_type, given_price_list, pos=None,
if price_list and permitted_price_lists and price_list not in permitted_price_lists:
price_list = get_usable_price_list(permitted_price_lists, party.doctype)
if price_list and not is_price_list_enabled(price_list):
price_list = None
if price_list:
party_details.price_list_currency = frappe.db.get_value(
"Price List", price_list, "currency", cache=True
+17
View File
@@ -115,6 +115,23 @@ class PartyTestCase(FrappeTestCase):
self.assertIsNone(party_details.selling_price_list)
def test_disabled_party_default_should_fall_back_to_given_price_list(self):
customer = self.create_customer(default_price_list=self.create_price_list(enabled=0))
given_price_list = self.create_price_list(enabled=1)
party_details = frappe._dict()
set_price_list(party_details, customer, "Customer", given_price_list)
self.assertEqual(party_details.selling_price_list, given_price_list)
def test_disabled_given_price_list_should_not_be_set(self):
customer = self.create_customer()
party_details = frappe._dict()
set_price_list(party_details, customer, "Customer", self.create_price_list(enabled=0))
self.assertIsNone(party_details.selling_price_list)
def create_user_with_price_list_permissions(self, price_lists, applicable_for=None):
user = frappe.get_doc(
{
+1 -1
View File
@@ -70,7 +70,7 @@ erpnext.buying = {
if(this.frm.fields_dict.buying_price_list) {
this.frm.set_query("buying_price_list", function() {
return{
filters: { 'buying': 1 }
filters: { 'buying': 1, 'enabled': 1 }
}
});
}
+1 -1
View File
@@ -59,7 +59,7 @@ erpnext.sales_common = {
if (this.frm.fields_dict.selling_price_list) {
this.frm.set_query("selling_price_list", function () {
return { filters: { selling: 1 } };
return { filters: { selling: 1, enabled: 1 } };
});
}
+15
View File
@@ -7,6 +7,7 @@ from frappe.defaults import get_user_default
from frappe.utils import cint
from erpnext.accounts.utils import get_fiscal_years
from erpnext.stock.doctype.price_list.price_list import is_price_list_enabled
def boot_session(bootinfo):
@@ -29,6 +30,8 @@ def boot_session(bootinfo):
frappe.get_single_value("Accounts Settings", "disable_include_dimensions")
)
remove_disabled_price_list_defaults(bootinfo)
bootinfo.sysdefaults.quotation_valid_till = cint(
frappe.db.get_single_value("CRM Settings", "default_valid_till")
)
@@ -72,6 +75,18 @@ def boot_session(bootinfo):
)
def remove_disabled_price_list_defaults(bootinfo):
user_defaults = (bootinfo.user or {}).get("defaults") or {}
for key in ("selling_price_list", "buying_price_list"):
price_list = bootinfo.sysdefaults.get(key) or user_defaults.get(key)
if not isinstance(price_list, str) or is_price_list_enabled(price_list):
continue
bootinfo.sysdefaults.pop(key, None)
user_defaults.pop(key, None)
def update_page_info(bootinfo):
bootinfo.page_info.update(
{
+29
View File
@@ -0,0 +1,29 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
class TestBoot(FrappeTestCase):
def test_boot_session_drops_disabled_price_list_default(self):
from erpnext.startup.boot import boot_session
price_list = frappe.get_doc(
{
"doctype": "Price List",
"price_list_name": frappe.generate_hash(length=10),
"currency": "INR",
"selling": 1,
"enabled": 0,
}
).insert(ignore_permissions=True)
bootinfo = frappe._dict(
sysdefaults=frappe._dict(selling_price_list=price_list.name),
page_info=frappe._dict(),
docs=[],
)
boot_session(bootinfo)
self.assertIsNone(bootinfo.sysdefaults.get("selling_price_list"))
@@ -20,6 +20,7 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.controllers.mapper import get_qty_already_mapped
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from erpnext.stock.doctype.item.item import get_item_defaults
from erpnext.stock.doctype.price_list.price_list import is_price_list_enabled
from erpnext.stock.get_item_details import get_price_list_rate_for
from erpnext.stock.stock_balance import get_indented_qty, update_bin_qty
@@ -188,13 +189,20 @@ class MaterialRequest(BuyingController):
self.reset_default_field_value("set_warehouse", "items", "warehouse")
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
if self.buying_price_list and not frappe.get_value("Price List", self.buying_price_list, "buying"):
self.set_buying_price_list()
def set_buying_price_list(self):
if not is_valid_buying_price_list(self.buying_price_list):
self.buying_price_list = None
if not self.buying_price_list:
buying_price_list = frappe.defaults.get_defaults().buying_price_list
if frappe.has_permission("Price List", "read", buying_price_list):
self.buying_price_list = buying_price_list
if self.buying_price_list:
return
default_price_list = frappe.defaults.get_defaults().buying_price_list
if is_valid_buying_price_list(default_price_list) and frappe.has_permission(
"Price List", "read", default_price_list
):
self.buying_price_list = default_price_list
def on_update(self):
if not self.is_new() and self.buying_price_list and self.has_value_changed("buying_price_list"):
@@ -432,6 +440,10 @@ class MaterialRequest(BuyingController):
doc.db_set("status", doc.status)
def is_valid_buying_price_list(price_list: str | None) -> bool:
return is_price_list_enabled(price_list) and bool(frappe.get_value("Price List", price_list, "buying"))
def update_completed_and_requested_qty(stock_entry, method):
if stock_entry.doctype == "Stock Entry":
material_request_map = {}
@@ -86,3 +86,7 @@ def get_price_list_details(price_list):
frappe.cache().hset("price_list_details", price_list, price_list_details)
return price_list_details or {}
def is_price_list_enabled(price_list: str | None) -> bool:
return bool(price_list) and bool(frappe.get_cached_value("Price List", price_list, "enabled"))