mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 00:14:50 +00:00
Merge pull request #47783 from frappe/perf/stock_reco
perf: speed up stock reconciliation
This commit is contained in:
@@ -154,7 +154,7 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
"Company", args.get("company"), "exception_budget_approver_role"
|
"Company", args.get("company"), "exception_budget_approver_role"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not frappe.get_cached_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}): # nosec
|
if not frappe.db.get_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}):
|
||||||
return
|
return
|
||||||
|
|
||||||
if not args.account:
|
if not args.account:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import frappe
|
|||||||
from frappe import _, bold, scrub
|
from frappe import _, bold, scrub
|
||||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils.caching import request_cache
|
||||||
|
|
||||||
|
|
||||||
class DoNotChangeError(frappe.ValidationError):
|
class DoNotChangeError(frappe.ValidationError):
|
||||||
@@ -388,27 +389,20 @@ def get_document_wise_inventory_dimensions(doctype) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
@request_cache
|
||||||
def get_inventory_dimensions():
|
def get_inventory_dimensions():
|
||||||
if not hasattr(frappe.local, "inventory_dimensions"):
|
return frappe.get_all(
|
||||||
frappe.local.inventory_dimensions = {}
|
"Inventory Dimension",
|
||||||
|
fields=[
|
||||||
if not frappe.local.inventory_dimensions:
|
"distinct target_fieldname as fieldname",
|
||||||
dimensions = frappe.get_all(
|
"source_fieldname",
|
||||||
"Inventory Dimension",
|
"reference_document as doctype",
|
||||||
fields=[
|
"validate_negative_stock",
|
||||||
"distinct target_fieldname as fieldname",
|
"name as dimension_name",
|
||||||
"source_fieldname",
|
],
|
||||||
"reference_document as doctype",
|
filters={"disabled": 0},
|
||||||
"validate_negative_stock",
|
order_by="creation",
|
||||||
"name as dimension_name",
|
)
|
||||||
],
|
|
||||||
filters={"disabled": 0},
|
|
||||||
order_by="creation",
|
|
||||||
)
|
|
||||||
|
|
||||||
frappe.local.inventory_dimensions = dimensions
|
|
||||||
|
|
||||||
return frappe.local.inventory_dimensions
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
|
|||||||
CanNotBeDefaultDimension,
|
CanNotBeDefaultDimension,
|
||||||
DoNotChangeError,
|
DoNotChangeError,
|
||||||
delete_dimension,
|
delete_dimension,
|
||||||
|
get_inventory_dimensions,
|
||||||
)
|
)
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
@@ -447,7 +448,7 @@ class TestInventoryDimension(IntegrationTestCase):
|
|||||||
self.assertEqual(d.store, "Inter Transfer Store 2")
|
self.assertEqual(d.store, "Inter Transfer Store 2")
|
||||||
|
|
||||||
def test_validate_negative_stock_for_inventory_dimension(self):
|
def test_validate_negative_stock_for_inventory_dimension(self):
|
||||||
frappe.local.inventory_dimensions = {}
|
frappe.clear_cache(doctype="Inventory Dimension")
|
||||||
item_code = "Test Negative Inventory Dimension Item"
|
item_code = "Test Negative Inventory Dimension Item"
|
||||||
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
|
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
|
||||||
create_item(item_code)
|
create_item(item_code)
|
||||||
@@ -496,7 +497,7 @@ class TestInventoryDimension(IntegrationTestCase):
|
|||||||
# disable validate_negative_stock for inventory dimension
|
# disable validate_negative_stock for inventory dimension
|
||||||
inv_dimension.reload()
|
inv_dimension.reload()
|
||||||
inv_dimension.db_set("validate_negative_stock", 0)
|
inv_dimension.db_set("validate_negative_stock", 0)
|
||||||
frappe.local.inventory_dimensions = {}
|
frappe.clear_cache(doctype="Inventory Dimension")
|
||||||
|
|
||||||
# Try issuing 100 qty, more than available stock against inventory dimension
|
# Try issuing 100 qty, more than available stock against inventory dimension
|
||||||
doc = make_stock_entry(item_code=item_code, source=warehouse, qty=100, do_not_submit=True)
|
doc = make_stock_entry(item_code=item_code, source=warehouse, qty=100, do_not_submit=True)
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ class StockReconciliation(StockController):
|
|||||||
def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None:
|
def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None:
|
||||||
"""Set Serial and Batch Bundle for each item"""
|
"""Set Serial and Batch Bundle for each item"""
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if not frappe.db.exists("Item", item.item_code):
|
if not item.item_code:
|
||||||
frappe.throw(_("Item {0} does not exist").format(item.item_code))
|
continue
|
||||||
|
|
||||||
item_details = frappe.get_cached_value(
|
item_details = frappe.get_cached_value(
|
||||||
"Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
|
"Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
|
||||||
@@ -595,10 +595,6 @@ class StockReconciliation(StockController):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# validate warehouse
|
|
||||||
if not frappe.db.get_value("Warehouse", row.warehouse):
|
|
||||||
self.validation_messages.append(_get_msg(row_num, _("Warehouse not found in the system")))
|
|
||||||
|
|
||||||
# if both not specified
|
# if both not specified
|
||||||
if row.qty in ["", None] and row.valuation_rate in ["", None]:
|
if row.qty in ["", None] and row.valuation_rate in ["", None]:
|
||||||
self.validation_messages.append(
|
self.validation_messages.append(
|
||||||
@@ -654,7 +650,7 @@ class StockReconciliation(StockController):
|
|||||||
# using try except to catch all validation msgs and display together
|
# using try except to catch all validation msgs and display together
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = frappe.get_doc("Item", item_code)
|
item = frappe.get_cached_doc("Item", item_code)
|
||||||
|
|
||||||
# end of life and stock item
|
# end of life and stock item
|
||||||
validate_end_of_life(item_code, item.end_of_life, item.disabled)
|
validate_end_of_life(item_code, item.end_of_life, item.disabled)
|
||||||
@@ -961,7 +957,7 @@ class StockReconciliation(StockController):
|
|||||||
changed_any_values = False
|
changed_any_values = False
|
||||||
|
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
is_customer_item = frappe.db.get_value("Item", d.item_code, "is_customer_provided_item")
|
is_customer_item = frappe.get_cached_value("Item", d.item_code, "is_customer_provided_item")
|
||||||
if is_customer_item and d.valuation_rate:
|
if is_customer_item and d.valuation_rate:
|
||||||
d.valuation_rate = 0.0
|
d.valuation_rate = 0.0
|
||||||
changed_any_values = True
|
changed_any_values = True
|
||||||
@@ -1011,8 +1007,6 @@ class StockReconciliation(StockController):
|
|||||||
self._cancel()
|
self._cancel()
|
||||||
|
|
||||||
def recalculate_current_qty(self, voucher_detail_no, sle_creation, add_new_sle=False):
|
def recalculate_current_qty(self, voucher_detail_no, sle_creation, add_new_sle=False):
|
||||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
|
||||||
|
|
||||||
for row in self.items:
|
for row in self.items:
|
||||||
if voucher_detail_no != row.name:
|
if voucher_detail_no != row.name:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ def get_bin(item_code, warehouse):
|
|||||||
|
|
||||||
|
|
||||||
def get_or_make_bin(item_code: str, warehouse: str) -> str:
|
def get_or_make_bin(item_code: str, warehouse: str) -> str:
|
||||||
bin_record = frappe.get_cached_value("Bin", {"item_code": item_code, "warehouse": warehouse})
|
bin_record = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse})
|
||||||
|
|
||||||
if not bin_record:
|
if not bin_record:
|
||||||
bin_obj = _create_bin(item_code, warehouse)
|
bin_obj = _create_bin(item_code, warehouse)
|
||||||
|
|||||||
Reference in New Issue
Block a user