diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py index 5a37bccaafb..3e5b08d069d 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py @@ -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: diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 17afc03dde1..e60d3f4614c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -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() diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index a1b15a1e867..c1315fe518b 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -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 "- ". The diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index deeb8310d9c..aebb7a22650 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -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"}