diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 4801bf15b562..a54e4dfa5032 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -4,6 +4,7 @@ import frappe from frappe import _, msgprint +from frappe.model.meta import get_field_precision from frappe.query_builder.custom import ConstantColumn from frappe.utils import flt, getdate from pypika.terms import Bracket, LiteralValue, Order @@ -125,17 +126,32 @@ def _execute(filters=None, additional_table_columns=None): row.update({frappe.scrub(tax_acc): tax_amount}) # total tax, grand total, rounded total & outstanding amount + + outstanding_precision = ( + get_field_precision( + frappe.get_meta("Purchase Invoice").get_field("outstanding_amount"), + currency=company_currency, + ) + or 2 + ) row.update( { "total_tax": total_tax, "grand_total": inv.base_grand_total, "rounded_total": inv.base_rounded_total, - "outstanding_amount": inv.outstanding_amount, } ) if inv.doctype == "Purchase Invoice": - row.update({"debit": inv.base_grand_total, "credit": 0.0}) + row.update( + { + "debit": inv.base_grand_total, + "credit": 0.0, + "outstanding_amount": flt( + (inv.outstanding_amount * (inv.conversion_rate or 1)), outstanding_precision + ), + } + ) else: row.update({"debit": 0.0, "credit": inv.base_grand_total}) data.append(row) @@ -395,6 +411,7 @@ def get_invoices(filters, additional_query_columns): pi.base_rounded_total, pi.outstanding_amount, pi.mode_of_payment, + pi.conversion_rate, ) .where(pi.docstatus == 1) ) diff --git a/erpnext/accounts/report/purchase_register/test_purchase_register.py b/erpnext/accounts/report/purchase_register/test_purchase_register.py index 9e0e2002f607..c80dfae41ae7 100644 --- a/erpnext/accounts/report/purchase_register/test_purchase_register.py +++ b/erpnext/accounts/report/purchase_register/test_purchase_register.py @@ -2,7 +2,7 @@ # MIT License. See license.txt import frappe -from frappe.utils import add_months, today +from frappe.utils import add_months, flt, today from erpnext.accounts.report.purchase_register.purchase_register import execute from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt @@ -67,6 +67,47 @@ def test_purchase_register_ignores_tax_rows_from_other_doctype(self): self.assertEqual(first_row.total_tax, 100) self.assertEqual(first_row.grand_total, 1100) + def test_purchase_currency_conversion(self): + usd_creditors = frappe.get_doc( + { + "doctype": "Account", + "account_name": "USD Creditors", + "parent_account": "Accounts Payable - _TC", + "company": "_Test Company", + "account_type": "Payable", + "root_type": "Liability", + "report_type": "Balance Sheet", + "account_currency": "USD", + } + ).insert() + foreign_invoice = make_purchase_invoice() + foreign_invoice.db_set("currency", "USD") + foreign_invoice.db_set("conversion_rate", 80) + foreign_invoice.db_set("credit_to", usd_creditors.name) + foreign_invoice.db_set("outstanding_amount", 100.236) + local_invoice = make_purchase_invoice() + local_invoice.db_set("currency", "INR") + local_invoice.db_set("conversion_rate", 1) + local_invoice.db_set("outstanding_amount", 200.456) + columns, data, *_ = execute(frappe._dict({"company": foreign_invoice.company})) + outstanding_precision = 2 + for row in data: + if row.get("invoice") == foreign_invoice.name: + expected_value = flt( + foreign_invoice.outstanding_amount * (foreign_invoice.conversion_rate or 1), + outstanding_precision, + ) + self.assertEqual(row.get("outstanding_amount"), expected_value) + self.assertEqual( + row.get("outstanding_amount"), round(row.get("outstanding_amount"), outstanding_precision) + ) + if row.get("invoice") == local_invoice.name: + expected_value = flt( + local_invoice.outstanding_amount * (local_invoice.conversion_rate or 1), + outstanding_precision, + ) + self.assertEqual(row.get("outstanding_amount"), expected_value) + def test_purchase_register_ledger_view(self): filters = frappe._dict( company="_Test Company 6", diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 5aebcc9e2f41..79a2ef1d92d7 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -141,17 +141,31 @@ def _execute(filters, additional_table_columns=None): # total tax, grand total, outstanding amount & rounded total + outstanding_precision = ( + get_field_precision( + frappe.get_meta("Sales Invoice").get_field("outstanding_amount"), + currency=company_currency, + ) + or 2 + ) row.update( { "tax_total": total_tax, "grand_total": inv.base_grand_total, "rounded_total": inv.base_rounded_total, - "outstanding_amount": inv.outstanding_amount, } ) if inv.doctype == "Sales Invoice": - row.update({"debit": inv.base_grand_total, "credit": 0.0}) + row.update( + { + "debit": inv.base_grand_total, + "credit": 0.0, + "outstanding_amount": flt( + (inv.outstanding_amount * (inv.conversion_rate or 1)), outstanding_precision + ), + } + ) else: row.update({"debit": 0.0, "credit": inv.base_grand_total}) data.append(row) @@ -437,6 +451,7 @@ def get_invoices(filters, additional_query_columns): si.is_internal_customer, si.represents_company, si.company, + si.conversion_rate, ) .where(si.docstatus == 1) ) diff --git a/erpnext/accounts/report/sales_register/test_sales_register.py b/erpnext/accounts/report/sales_register/test_sales_register.py index 0875c7c143fb..332b1388895d 100644 --- a/erpnext/accounts/report/sales_register/test_sales_register.py +++ b/erpnext/accounts/report/sales_register/test_sales_register.py @@ -1,9 +1,10 @@ import frappe -from frappe.utils import getdate, today +from frappe.utils import flt, getdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.sales_register.sales_register import execute from erpnext.accounts.test.accounts_mixin import AccountsTestMixin +from erpnext.selling.doctype.customer.test_customer import make_customer from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.tests.utils import ERPNextTestSuite @@ -212,3 +213,49 @@ def test_journal_with_cost_center_filter(self): } result_output = {k: v for k, v in filtered_output[0].items() if k in expected_result} self.assertDictEqual(result_output, expected_result) + + def test_currency_conversion(self): + usd_debtors = frappe.get_doc( + { + "doctype": "Account", + "account_name": "USD Debtors", + "parent_account": "Accounts Receivable - _TC", + "company": "_Test Company", + "account_type": "Receivable", + "root_type": "Asset", + "report_type": "Balance Sheet", + "account_currency": "USD", + } + ).insert() + foreign_invoice = create_sales_invoice( + customer="_Test Customer", + currency="USD", + conversion_rate=80, + qty=1, + rate=100, + debit_to=usd_debtors.name, + ) + foreign_invoice.db_set("outstanding_amount", 100.236) + make_customer("_Test Customer2") + local_invoice = create_sales_invoice( + customer="_Test Customer2", currency="INR", conversion_rate=1, qty=1, rate=200 + ) + local_invoice.db_set("outstanding_amount", 200.456) + columns, data, *_ = execute(frappe._dict({"company": foreign_invoice.company})) + outstanding_precision = 2 + for row in data: + if row.get("invoice") == foreign_invoice.name: + expected_value = flt( + foreign_invoice.outstanding_amount * (foreign_invoice.conversion_rate or 1), + outstanding_precision, + ) + self.assertEqual(row.get("outstanding_amount"), expected_value) + self.assertEqual( + row.get("outstanding_amount"), round(row.get("outstanding_amount"), outstanding_precision) + ) + if row.get("invoice") == local_invoice.name: + expected_value = flt( + local_invoice.outstanding_amount * (local_invoice.conversion_rate or 1), + outstanding_precision, + ) + self.assertEqual(row.get("outstanding_amount"), expected_value)