Merge pull request #33382 from rohitwaghchaure/fixed-pick-list-issue

fix: unsupported operand type(s) for +=: 'int' and 'NoneType'
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index fd19d25..7d72c76 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -420,6 +420,7 @@
 		"erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall.create_process_loan_security_shortfall",
 		"erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual.process_loan_interest_accrual_for_term_loans",
 		"erpnext.crm.utils.open_leads_opportunities_based_on_todays_event",
+		"erpnext.stock.doctype.stock_entry.stock_entry.audit_incorrect_valuation_entries",
 	],
 	"monthly_long": [
 		"erpnext.accounts.deferred_revenue.process_deferred_accounting",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d401f81..a047a9b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -4,12 +4,24 @@
 
 import json
 from collections import defaultdict
+from typing import Dict
 
 import frappe
 from frappe import _
 from frappe.model.mapper import get_mapped_doc
 from frappe.query_builder.functions import Sum
-from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate
+from frappe.utils import (
+	add_days,
+	cint,
+	comma_or,
+	cstr,
+	flt,
+	format_time,
+	formatdate,
+	getdate,
+	nowdate,
+	today,
+)
 
 import erpnext
 from erpnext.accounts.general_ledger import process_gl_map
@@ -2700,3 +2712,62 @@
 		)
 		.orderby(stock_entry.creation, stock_entry_detail.item_code, stock_entry_detail.idx)
 	).run(as_dict=1)
+
+
+def audit_incorrect_valuation_entries():
+	# Audit of stock transfer entries having incorrect valuation
+	from erpnext.controllers.stock_controller import create_repost_item_valuation_entry
+
+	stock_entries = get_incorrect_stock_entries()
+
+	for stock_entry, values in stock_entries.items():
+		reposting_data = frappe._dict(
+			{
+				"posting_date": values.posting_date,
+				"posting_time": values.posting_time,
+				"voucher_type": "Stock Entry",
+				"voucher_no": stock_entry,
+				"company": values.company,
+			}
+		)
+
+		create_repost_item_valuation_entry(reposting_data)
+
+
+def get_incorrect_stock_entries() -> Dict:
+	stock_entry = frappe.qb.DocType("Stock Entry")
+	stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry")
+	transfer_purposes = [
+		"Material Transfer",
+		"Material Transfer for Manufacture",
+		"Send to Subcontractor",
+	]
+
+	query = (
+		frappe.qb.from_(stock_entry)
+		.inner_join(stock_ledger_entry)
+		.on(stock_entry.name == stock_ledger_entry.voucher_no)
+		.select(
+			stock_entry.name,
+			stock_entry.company,
+			stock_entry.posting_date,
+			stock_entry.posting_time,
+			Sum(stock_ledger_entry.stock_value_difference).as_("stock_value"),
+		)
+		.where(
+			(stock_entry.docstatus == 1)
+			& (stock_entry.purpose.isin(transfer_purposes))
+			& (stock_ledger_entry.modified > add_days(today(), -2))
+		)
+		.groupby(stock_ledger_entry.voucher_detail_no)
+		.having(Sum(stock_ledger_entry.stock_value_difference) != 0)
+	)
+
+	data = query.run(as_dict=True)
+	stock_entries = {}
+
+	for row in data:
+		if abs(row.stock_value) > 0.1 and row.name not in stock_entries:
+			stock_entries.setdefault(row.name, row)
+
+	return stock_entries
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index b574b71..680d209 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe.permissions import add_user_permission, remove_user_permission
 from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, flt, nowdate, nowtime, today
+from frappe.utils import add_days, flt, now, nowdate, nowtime, today
 
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import (
@@ -17,6 +17,8 @@
 from erpnext.stock.doctype.serial_no.serial_no import *  # noqa
 from erpnext.stock.doctype.stock_entry.stock_entry import (
 	FinishedGoodError,
+	audit_incorrect_valuation_entries,
+	get_incorrect_stock_entries,
 	move_sample_to_retention_warehouse,
 )
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -1614,6 +1616,44 @@
 
 		self.assertRaises(BatchExpiredError, se.save)
 
+	def test_audit_incorrect_stock_entries(self):
+		item_code = "Test Incorrect Valuation Rate Item - 001"
+		create_item(item_code=item_code, is_stock_item=1)
+
+		make_stock_entry(
+			item_code=item_code,
+			purpose="Material Receipt",
+			posting_date=add_days(nowdate(), -10),
+			qty=2,
+			rate=500,
+			to_warehouse="_Test Warehouse - _TC",
+		)
+
+		transfer_entry = make_stock_entry(
+			item_code=item_code,
+			purpose="Material Transfer",
+			qty=2,
+			rate=500,
+			from_warehouse="_Test Warehouse - _TC",
+			to_warehouse="_Test Warehouse 1 - _TC",
+		)
+
+		sle_name = frappe.db.get_value(
+			"Stock Ledger Entry", {"voucher_no": transfer_entry.name, "actual_qty": (">", 0)}, "name"
+		)
+
+		frappe.db.set_value(
+			"Stock Ledger Entry", sle_name, {"modified": add_days(now(), -1), "stock_value_difference": 10}
+		)
+
+		stock_entries = get_incorrect_stock_entries()
+		self.assertTrue(transfer_entry.name in stock_entries)
+
+		audit_incorrect_valuation_entries()
+
+		stock_entries = get_incorrect_stock_entries()
+		self.assertFalse(transfer_entry.name in stock_entries)
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)