Merge pull request #56983 from nabinhait/fix-flaky-usd-exchange-rate-tests

test: seed current-dated USD↔INR exchange rate to fix flaky currency tests
This commit is contained in:
Nabin Hait
2026-07-14 18:13:28 +05:30
committed by GitHub
4 changed files with 46 additions and 18 deletions

View File

@@ -348,10 +348,15 @@ class TestExchangeRateRevaluation(ERPNextTestSuite, AccountsTestMixin):
je.reload()
self.assertEqual(je.voucher_type, "Exchange Rate Revaluation")
self.assertEqual(len(je.accounts), 3)
# A gain is credited to the gain/loss account, a loss is debited. The current
# exchange rate (from master data) may sit either side of the booked rate, so
# derive the column from the sign instead of assuming a gain.
gain_loss_debit = abs(total_gain_loss) if total_gain_loss < 0 else 0.0
gain_loss_credit = total_gain_loss if total_gain_loss > 0 else 0.0
expected = [
(usd_account, new_balance, 0.0, 100.0, 0.0),
(usd_account, 0.0, old_balance, 0.0, 100.0),
(gain_loss_account, 0.0, total_gain_loss, 0.0, total_gain_loss),
(gain_loss_account, gain_loss_debit, gain_loss_credit, gain_loss_debit, gain_loss_credit),
]
actual = []
for acc in je.accounts:

View File

@@ -472,7 +472,7 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
pr = frappe.new_doc("Purchase Receipt")
pr.currency = "USD"
pr.company = "_Test Company with perpetual inventory"
pr.conversion_rate = (70,)
pr.conversion_rate = 80
pr.supplier = "_Test Supplier USD"
pr.append(
"items",
@@ -491,7 +491,7 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
# Createing purchase invoice against Purchase Receipt
pi = create_purchase_invoice(pr.name)
pi.conversion_rate = 80
pi.conversion_rate = 70
pi.credit_to = "_Test Payable USD - TCP1"
pi.insert()
pi.submit()

View File

@@ -17,6 +17,7 @@ from erpnext.selling.doctype.customer.mapper import (
make_quotation,
parse_full_name,
)
from erpnext.setup.utils import get_exchange_rate
from erpnext.tests.utils import ERPNextTestSuite
@@ -29,20 +30,9 @@ class TestCustomer(ERPNextTestSuite):
frappe.defaults.set_user_default("company", company)
self.addCleanup(frappe.defaults.clear_user_default, "company")
# Seed a deterministic rate so the test does not depend on the live exchange-rate API.
rate = 83.0
exchange = frappe.get_doc(
{
"doctype": "Currency Exchange",
"date": nowdate(),
"from_currency": foreign_currency,
"to_currency": company_currency,
"exchange_rate": rate,
"for_selling": 1,
"for_buying": 1,
}
).insert(ignore_if_duplicate=True)
self.addCleanup(frappe.delete_doc, "Currency Exchange", exchange.name, force=1)
# Master data seeds a current-dated exchange rate, so make_quotation should
# resolve that rate instead of falling back to the default conversion rate of 1.0.
expected_rate = get_exchange_rate(foreign_currency, company_currency, nowdate())
customer = frappe.get_doc(
{
@@ -59,7 +49,7 @@ class TestCustomer(ERPNextTestSuite):
self.assertEqual(quotation.currency, foreign_currency)
self.assertNotEqual(flt(quotation.conversion_rate), 1.0)
self.assertNotEqual(flt(quotation.conversion_rate), 0.0)
self.assertEqual(flt(quotation.conversion_rate), rate)
self.assertEqual(flt(quotation.conversion_rate), flt(expected_rate))
def test_get_customer_name_dedupes_with_numeric_suffix(self):
# When a customer name already exists, get_customer_name appends "- <max suffix + 1>". The

View File

@@ -181,6 +181,7 @@ class BootStrapTestData:
self.make_location()
self.make_price_list()
self.make_item_price()
self.make_currency_exchange()
self.make_loyalty_program()
self.make_shareholder()
self.make_sales_taxes_template()
@@ -2533,6 +2534,38 @@ class BootStrapTestData:
]
self.make_records(["item_code", "price_list", "price_list_rate"], records)
def make_currency_exchange(self):
"""Seed current-dated USD<->INR rates so foreign-currency documents
transacted on ``today()`` resolve an exchange rate deterministically.
Without this, ``get_exchange_rate`` finds no in-window Currency Exchange
record and falls back to an external API that is unreachable in CI,
returning ``0`` and breaking tests that create USD documents. The rates
mirror the latest values in the Currency Exchange ``test_records`` so
cost calculations stay unchanged regardless of which record is picked.
"""
records = [
{
"doctype": "Currency Exchange",
"date": today(),
"from_currency": "USD",
"to_currency": "INR",
"exchange_rate": 62.9,
"for_buying": 1,
"for_selling": 1,
},
{
"doctype": "Currency Exchange",
"date": today(),
"from_currency": "INR",
"to_currency": "USD",
"exchange_rate": 0.0167,
"for_buying": 1,
"for_selling": 1,
},
]
self.make_records(["from_currency", "to_currency", "date", "for_buying", "for_selling"], records)
def make_operation(self):
records = [
{"doctype": "Operation", "name": "_Test Operation 1", "workstation": "_Test Workstation 1"}