diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index f24b28917237..47cb3fd812f0 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -538,7 +538,7 @@ def make_payment_request(**args):
)
party_type = args.get("party_type") or "Customer"
- party_account_currency = ref_doc.party_account_currency
+ party_account_currency = ref_doc.get("party_account_currency")
if not party_account_currency:
party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company)
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index 6109185f0bd4..59ffd656689b 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -40,7 +40,7 @@ def on_cancel(self):
)
if gle_count > 5000:
frappe.enqueue(
- make_reverse_gl_entries,
+ process_cancellation,
voucher_type="Period Closing Voucher",
voucher_no=self.name,
queue="long",
@@ -51,9 +51,7 @@ def on_cancel(self):
alert=True,
)
else:
- make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
-
- self.delete_closing_entries()
+ process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name)
def validate_future_closing_vouchers(self):
if frappe.db.exists(
@@ -66,12 +64,6 @@ def validate_future_closing_vouchers(self):
)
)
- def delete_closing_entries(self):
- closing_balance = frappe.qb.DocType("Account Closing Balance")
- frappe.qb.from_(closing_balance).delete().where(
- closing_balance.period_closing_voucher == self.name
- ).run()
-
def validate_account_head(self):
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
@@ -136,14 +128,7 @@ def make_gl_entries(self, get_opening_entries=False):
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
if len(gl_entries + closing_entries) > 3000:
frappe.enqueue(
- process_gl_entries,
- gl_entries=gl_entries,
- voucher_name=self.name,
- timeout=3000,
- )
-
- frappe.enqueue(
- process_closing_entries,
+ process_gl_and_closing_entries,
gl_entries=gl_entries,
closing_entries=closing_entries,
voucher_name=self.name,
@@ -157,8 +142,9 @@ def make_gl_entries(self, get_opening_entries=False):
alert=True,
)
else:
- process_gl_entries(gl_entries, self.name)
- process_closing_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
+ process_gl_and_closing_entries(
+ gl_entries, closing_entries, self.name, self.company, self.posting_date
+ )
def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = []
@@ -323,9 +309,10 @@ def get_balances_based_on_dimensions(
if get_opening_entries:
query = query.where(
- gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
- | gl_entry.is_opening
- == "Yes"
+ ( # noqa: UP034
+ (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date))
+ | (gl_entry.is_opening == "Yes")
+ )
)
else:
query = query.where(
@@ -343,12 +330,16 @@ def get_balances_based_on_dimensions(
return query.run(as_dict=1)
-def process_gl_entries(gl_entries, voucher_name):
+def process_gl_and_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
+ from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
+ make_closing_entries,
+ )
from erpnext.accounts.general_ledger import make_gl_entries
try:
if gl_entries:
make_gl_entries(gl_entries, merge_entries=False)
+ make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
@@ -356,25 +347,21 @@ def process_gl_entries(gl_entries, voucher_name):
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed")
-def process_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
- from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
- make_closing_entries,
- )
-
- try:
- make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
- except Exception as e:
- frappe.db.rollback()
- frappe.log_error(e)
-
-
-def make_reverse_gl_entries(voucher_type, voucher_no):
+def process_cancellation(voucher_type, voucher_no):
from erpnext.accounts.general_ledger import make_reverse_gl_entries
try:
make_reverse_gl_entries(voucher_type=voucher_type, voucher_no=voucher_no)
+ delete_closing_entries(voucher_no)
frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
frappe.log_error(e)
frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Failed")
+
+
+def delete_closing_entries(voucher_no):
+ closing_balance = frappe.qb.DocType("Account Closing Balance")
+ frappe.qb.from_(closing_balance).delete().where(
+ closing_balance.period_closing_voucher == voucher_no
+ ).run()
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 93a91584ecdb..90e606d7861d 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -981,6 +981,12 @@ def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self):
self.assertEqual(so.items[1].item_code, "_Test Item")
self.assertEqual(so.items[1].qty, 3)
+ so = make_sales_order(item_code="_Test Item", qty=5, do_not_submit=1)
+ so.items[0].qty = 1
+ del so.items[-1]
+ so.save()
+ self.assertEqual(len(so.items), 1)
+
def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index b5d4af1ab90b..0032e539e446 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -644,6 +644,9 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
if pricing_rule.round_free_qty:
qty = math.floor(qty)
+ if not qty:
+ return
+
free_item_data_args = {
"item_code": free_item,
"qty": qty,
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
index 857ee0e71879..c9762282a7dc 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
@@ -5,6 +5,8 @@
import frappe
from frappe import _
from frappe.model.document import Document
+from frappe.query_builder import Criterion
+from frappe.query_builder.functions import IfNull
pricing_rule_fields = [
"apply_on",
@@ -91,22 +93,50 @@ def validate_pricing_rules(self):
if self.is_new():
return
- transaction_exists = False
- docnames = []
+ invalid_pricing_rule = self.get_invalid_pricing_rules()
- # If user has changed applicable for
- if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for:
+ if not invalid_pricing_rule:
return
- docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
+ if frappe.db.exists(
+ "Pricing Rule Detail",
+ {
+ "pricing_rule": ["in", invalid_pricing_rule],
+ "docstatus": ["<", 2],
+ },
+ ):
+ raise_for_transaction_exists(self.name)
+
+ for doc in invalid_pricing_rule:
+ frappe.delete_doc("Pricing Rule", doc)
+
+ frappe.msgprint(
+ _("The following invalid Pricing Rules are deleted:")
+ + "
- "
+ + "
- ".join(invalid_pricing_rule)
+ + "
"
+ )
+
+ def get_invalid_pricing_rules(self):
+ pr = frappe.qb.DocType("Pricing Rule")
+ conditions = []
+ conditions.append(pr.promotional_scheme == self.name)
+
+ if self.applicable_for:
+ applicable_for = frappe.scrub(self.applicable_for)
+ applicable_for_list = [d.get(applicable_for) for d in self.get(applicable_for)]
- for docname in docnames:
- if frappe.db.exists("Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}):
- raise_for_transaction_exists(self.name)
+ conditions.append(
+ (IfNull(pr.applicable_for, "") != self.applicable_for)
+ | (
+ (IfNull(pr.applicable_for, "") == self.applicable_for)
+ & IfNull(pr[applicable_for], "").notin(applicable_for_list)
+ )
+ )
+ else:
+ conditions.append(IfNull(pr.applicable_for, "") != "")
- if docnames and not transaction_exists:
- for docname in docnames:
- frappe.delete_doc("Pricing Rule", docname.name)
+ return frappe.qb.from_(pr).select(pr.name).where(Criterion.all(conditions)).run(pluck=True)
def on_update(self):
self.validate()
diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
index 0d08fd98139b..c4a5fcf4b0a3 100644
--- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py
@@ -90,6 +90,31 @@ def test_change_applicable_for_in_promotional_scheme(self):
price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
self.assertEqual(price_rules, [])
+ def test_change_applicable_for_values_in_promotional_scheme(self):
+ ps = make_promotional_scheme(applicable_for="Customer", customer="_Test Customer")
+ ps.append("customer", {"customer": "_Test Customer 2"})
+ ps.save()
+
+ price_rules = frappe.get_all(
+ "Pricing Rule", filters={"promotional_scheme": ps.name, "applicable_for": "Customer"}
+ )
+ self.assertTrue(len(price_rules), 2)
+
+ ps.set("customer", [])
+ ps.append("customer", {"customer": "_Test Customer 2"})
+ ps.save()
+
+ price_rules = frappe.get_all(
+ "Pricing Rule",
+ filters={
+ "promotional_scheme": ps.name,
+ "applicable_for": "Customer",
+ "customer": "_Test Customer",
+ },
+ )
+ self.assertEqual(price_rules, [])
+ frappe.delete_doc("Promotional Scheme", ps.name)
+
def test_min_max_amount_configuration(self):
ps = make_promotional_scheme()
ps.price_discount_slabs[0].min_amount = 10
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 8b0a2557d9b0..d1bb24617956 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -578,8 +578,6 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
conditions.append(ple.voucher_no == ple.against_voucher_no)
conditions.append(ple.company == inv.company)
- (qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1))
-
advance_amt = (
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
)
diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
index f906dc6cec6a..f2c2f380fe90 100644
--- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
+++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json
@@ -1,7 +1,5 @@
{
"actions": [],
- "allow_rename": 1,
- "autoname": "format:UNREC-{#####}",
"creation": "2023-08-22 10:26:34.421423",
"default_view": "List",
"doctype": "DocType",
@@ -58,11 +56,10 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-08-28 17:42:50.261377",
+ "modified": "2024-10-10 12:03:50.022444",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Unreconcile Payment",
- "naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
index 9335a8cd65a8..2684c87a22ab 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js
@@ -47,7 +47,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
},
],
formatter: function (value, row, column, data, default_formatter, filter) {
- if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") {
+ if (column.fieldname == "payment_entry" && value == __("Cheques and Deposits incorrectly cleared")) {
column.link_onclick =
"frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()";
}
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index a809edd33cb9..388b7d8fe86b 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -474,10 +474,13 @@ def update_parent_account_names(accounts):
for d in accounts:
if d.account_number:
- account_name = d.account_number + " - " + d.account_name
+ account_key = d.account_number + " - " + d.account_name
else:
- account_name = d.account_name
- name_to_account_map[d.name] = account_name
+ account_key = d.account_name
+
+ d.account_key = account_key
+
+ name_to_account_map[d.name] = account_key
for account in accounts:
if account.parent_account:
@@ -510,33 +513,26 @@ def get_subsidiary_companies(company):
def get_accounts(root_type, companies):
accounts = []
- added_accounts = []
for company in companies:
- for account in frappe.get_all(
- "Account",
- fields=[
- "name",
- "is_group",
- "company",
- "parent_account",
- "lft",
- "rgt",
- "root_type",
- "report_type",
- "account_name",
- "account_number",
- ],
- filters={"company": company, "root_type": root_type},
- ):
- if account.account_number:
- account_key = account.account_number + "-" + account.account_name
- else:
- account_key = account.account_name
-
- if account_key not in added_accounts:
- accounts.append(account)
- added_accounts.append(account_key)
+ accounts.extend(
+ frappe.get_all(
+ "Account",
+ fields=[
+ "name",
+ "is_group",
+ "company",
+ "parent_account",
+ "lft",
+ "rgt",
+ "root_type",
+ "report_type",
+ "account_name",
+ "account_number",
+ ],
+ filters={"company": company, "root_type": root_type},
+ )
+ )
return accounts
@@ -775,15 +771,17 @@ def add_total_row(out, root_type, balance_must_be, companies, company_currency):
def filter_accounts(accounts, depth=10):
parent_children_map = {}
accounts_by_name = {}
+ added_accounts = []
+
for d in accounts:
- if d.account_number:
- account_name = d.account_number + " - " + d.account_name
- else:
- account_name = d.account_name
+ if d.account_key in added_accounts:
+ continue
+
+ added_accounts.append(d.account_key)
d["company_wise_opening_bal"] = defaultdict(float)
- accounts_by_name[account_name] = d
+ accounts_by_name[d.account_key] = d
- parent_children_map.setdefault(d.parent_account or None, []).append(d)
+ parent_children_map.setdefault(d.parent_account_name or None, []).append(d)
filtered_accounts = []
@@ -795,7 +793,7 @@ def add_to_list(parent, level):
for child in children:
child.indent = level
filtered_accounts.append(child)
- add_to_list(child.name, level + 1)
+ add_to_list(child.account_key, level + 1)
add_to_list(None, 0)
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index 73882d39cec9..a65fe30f050f 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -392,6 +392,7 @@ def create_rfq_items(sq_doc, supplier, data):
"material_request",
"material_request_item",
"stock_qty",
+ "uom",
]:
args[field] = data.get(field)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 827f204e40e9..3b4d32b7305a 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -456,6 +456,16 @@ def set_incoming_rate(self):
raise_error_if_no_rate=False,
)
+ if (
+ not d.incoming_rate
+ and self.get("return_against")
+ and self.get("is_return")
+ and get_valuation_method(d.item_code) == "Moving Average"
+ ):
+ d.incoming_rate = get_rate_for_return(
+ self.doctype, self.name, d.item_code, self.return_against, item_row=d
+ )
+
# For internal transfers use incoming rate as the valuation rate
if self.is_internal_transfer():
if self.doctype == "Delivery Note" or self.get("update_stock"):
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index a4d8e3efee16..b37658b77842 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -50,6 +50,21 @@ def validate(self):
self.set_rate_of_stock_uom()
self.validate_internal_transfer()
self.validate_putaway_capacity()
+ self.reset_conversion_factor()
+
+ def reset_conversion_factor(self):
+ for row in self.get("items"):
+ if row.uom != row.stock_uom:
+ continue
+
+ if row.conversion_factor != 1.0:
+ row.conversion_factor = 1.0
+ frappe.msgprint(
+ _(
+ "Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}."
+ ).format(bold(row.item_code), bold(row.uom), bold(row.stock_uom)),
+ alert=True,
+ )
def make_gl_entries(self, gl_entries=None, from_repost=False, via_landed_cost_voucher=False):
if self.docstatus == 2:
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index f201228c2b1c..293066cde388 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -30,7 +30,7 @@
from erpnext.selling.doctype.customer.customer import check_credit_limit
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
from erpnext.stock.doctype.item.item import get_item_defaults
-from erpnext.stock.get_item_details import get_default_bom, get_price_list_rate
+from erpnext.stock.get_item_details import get_bin_details, get_default_bom, get_price_list_rate
from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
@@ -607,6 +607,9 @@ def update_item(source, target, source_parent):
target.project = source_parent.project
target.qty = get_remaining_qty(source)
target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
+ target.actual_qty = get_bin_details(
+ target.item_code, target.warehouse, source_parent.company, True
+ ).get("actual_qty", 0)
args = target.as_dict().copy()
args.update(
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 74f781d89828..b1207ee97a19 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -30,6 +30,7 @@
)
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.get_item_details import get_bin_details
class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
@@ -99,6 +100,12 @@ def test_make_material_request(self):
self.assertEqual(mr.material_request_type, "Purchase")
self.assertEqual(len(mr.get("items")), len(so.get("items")))
+ for item in mr.get("items"):
+ actual_qty = get_bin_details(item.item_code, item.warehouse, mr.company, True).get(
+ "actual_qty", 0
+ )
+ self.assertEqual(flt(item.actual_qty), actual_qty)
+
def test_make_delivery_note(self):
so = make_sales_order(do_not_submit=True)
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index e9b07e76b2de..85fbda5a28e4 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -1449,6 +1449,46 @@ def test_internal_transfer_for_non_stock_item(self):
self.assertEqual(so.items[0].rate, rate)
self.assertEqual(dn.items[0].rate, so.items[0].rate)
+ def test_batch_return_dn(self):
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+
+ item_code = make_item(
+ "Test Batch Return DN Item 1",
+ properties={
+ "has_batch_no": 1,
+ "valuation_method": "Moving Average",
+ "create_new_batch": 1,
+ "batch_number_series": "TBRDN1-.#####",
+ "is_stock_item": 1,
+ },
+ ).name
+
+ se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=5, basic_rate=100)
+
+ batch_no = se.items[0].batch_no
+ dn = create_delivery_note(
+ item_code=item_code,
+ qty=5,
+ rate=500,
+ batch_no=batch_no,
+ )
+
+ dn_return = make_sales_return(dn.name)
+ dn_return.save().submit()
+
+ self.assertEqual(dn_return.items[0].qty, 5 * -1)
+
+ returned_batch_no = dn_return.items[0].batch_no
+ self.assertEqual(batch_no, returned_batch_no)
+
+ stock_value_difference = frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"voucher_no": dn_return.name, "voucher_type": "Delivery Note"},
+ "stock_value_difference",
+ )
+
+ self.assertEqual(stock_value_difference, 100.0 * 5)
+
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index c87699f5f8c1..1c4feedbcfb0 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2668,6 +2668,21 @@ def test_sles_with_same_posting_datetime_and_creation(self):
self.assertEqual(data[0].get("bal_qty"), 50.0)
+ def test_same_stock_and_transaction_uom_conversion_factor(self):
+ item_code = "Test Item for Same Stock and Transaction UOM Conversion Factor"
+ create_item(item_code)
+
+ pr = make_purchase_receipt(
+ item_code=item_code,
+ qty=10,
+ rate=100,
+ stock_uom="Nos",
+ transaction_uom="Nos",
+ conversion_factor=10,
+ )
+
+ self.assertEqual(pr.items[0].conversion_factor, 1.0)
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 8ebc2aa9c048..a81b57168de0 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -775,6 +775,15 @@ def get_incoming_outgoing_rate_from_transaction(self, sle):
}
)
+ if not rate and sle.voucher_type in ["Delivery Note", "Sales Invoice"]:
+ rate = get_rate_for_return(
+ sle.voucher_type,
+ sle.voucher_no,
+ sle.item_code,
+ voucher_detail_no=sle.voucher_detail_no,
+ sle=sle,
+ )
+
else:
rate = get_rate_for_return(
sle.voucher_type,