refactor(stock): convert Item Price bulk update to qb

Replace the raw UPDATE ... modified=NOW() in update_item_price with
frappe.qb.update (now() for modified). Same result on MariaDB; valid under
Postgres.

Tests: currency/buying/selling/modified propagation and price-list scoping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mihir Kandoi
2026-06-19 22:11:18 +05:30
parent b0d9208561
commit d955122c88
2 changed files with 90 additions and 7 deletions

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _, throw
from frappe.model.document import Document
from frappe.utils import cint
from frappe.utils import cint, now
class PriceList(Document):
@@ -47,11 +47,15 @@ class PriceList(Document):
frappe.set_value("Buying Settings", "Buying Settings", "buying_price_list", self.name)
def update_item_price(self):
frappe.db.sql(
"""update `tabItem Price` set currency=%s,
buying=%s, selling=%s, modified=NOW() where price_list=%s""",
(self.currency, cint(self.buying), cint(self.selling), self.name),
)
item_price = frappe.qb.DocType("Item Price")
(
frappe.qb.update(item_price)
.set(item_price.currency, self.currency)
.set(item_price.buying, cint(self.buying))
.set(item_price.selling, cint(self.selling))
.set(item_price.modified, now())
.where(item_price.price_list == self.name)
).run()
def on_trash(self):
self.delete_price_list_details_key()

View File

@@ -1,4 +1,83 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.utils import random_string
from erpnext.tests.utils import ERPNextTestSuite
class TestPriceList(ERPNextTestSuite):
def make_price_list(self, currency="INR", buying=1, selling=1):
price_list = frappe.get_doc(
{
"doctype": "Price List",
"price_list_name": "_Test PL " + random_string(10),
"enabled": 1,
"currency": currency,
"buying": buying,
"selling": selling,
}
).insert()
return price_list
def make_item_price(self, price_list, item_code="_Test Item", rate=100):
return frappe.get_doc(
{
"doctype": "Item Price",
"item_code": item_code,
"price_list": price_list,
"price_list_rate": rate,
}
).insert()
def test_update_item_price_propagates_currency_and_flags(self):
# Price List starts in INR, applicable for both buying and selling.
price_list = self.make_price_list(currency="INR", buying=1, selling=1)
ip1 = self.make_item_price(price_list.name, item_code="_Test Item", rate=100)
ip2 = self.make_item_price(price_list.name, item_code="_Test Item 2", rate=250)
# Sanity: Item Price rows inherited the Price List's initial state.
for ip in (ip1, ip2):
row = frappe.db.get_value("Item Price", ip.name, ["currency", "buying", "selling"], as_dict=True)
self.assertEqual(row.currency, "INR")
self.assertEqual(row.buying, 1)
self.assertEqual(row.selling, 1)
# Change the Price List's currency and flip the buying flag off.
# on_update -> update_item_price() should bulk-UPDATE every Item Price
# linked to this Price List.
price_list.currency = "USD"
price_list.buying = 0
price_list.selling = 1
price_list.save()
for ip in (ip1, ip2):
row = frappe.db.get_value("Item Price", ip.name, ["currency", "buying", "selling"], as_dict=True)
self.assertEqual(row.currency, "USD")
self.assertEqual(row.buying, 0)
self.assertEqual(row.selling, 1)
def test_update_item_price_scoped_to_own_price_list(self):
# Two independent Price Lists; updating one must not touch the other's
# Item Price rows (the WHERE price_list == self.name clause).
pl_a = self.make_price_list(currency="INR", buying=1, selling=1)
pl_b = self.make_price_list(currency="INR", buying=1, selling=1)
ip_a = self.make_item_price(pl_a.name, item_code="_Test Item", rate=100)
ip_b = self.make_item_price(pl_b.name, item_code="_Test Item", rate=100)
pl_a.currency = "USD"
pl_a.buying = 0
pl_a.save()
row_a = frappe.db.get_value("Item Price", ip_a.name, ["currency", "buying"], as_dict=True)
self.assertEqual(row_a.currency, "USD")
self.assertEqual(row_a.buying, 0)
# pl_b was untouched, so its Item Price must keep the original values.
row_b = frappe.db.get_value("Item Price", ip_b.name, ["currency", "buying"], as_dict=True)
self.assertEqual(row_b.currency, "INR")
self.assertEqual(row_b.buying, 1)