diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 6f1889b34e..0099e79e5d 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -8,20 +8,17 @@ from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute +from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order -class TestAccountsReceivable(FrappeTestCase): +class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): def setUp(self): - frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'") - frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'") - frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'") - frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'") - frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'") - frappe.db.sql("delete from `tabJournal Entry` where company='_Test Company 2'") - frappe.db.sql("delete from `tabExchange Rate Revaluation` where company='_Test Company 2'") - - self.create_usd_account() + self.create_company() + self.create_customer() + self.create_item() + self.create_usd_receivable_account() + self.clear_old_entries() def tearDown(self): frappe.db.rollback() @@ -49,9 +46,61 @@ class TestAccountsReceivable(FrappeTestCase): debtors_usd.account_type = debtors.account_type self.debtors_usd = debtors_usd.save().name + def create_sales_invoice(self, no_payment_schedule=False, do_not_submit=False): + frappe.set_user("Administrator") + si = create_sales_invoice( + item=self.item, + company=self.company, + customer=self.customer, + debit_to=self.debit_to, + posting_date=today(), + parent_cost_center=self.cost_center, + cost_center=self.cost_center, + rate=100, + price_list_rate=100, + do_not_save=1, + ) + if not no_payment_schedule: + si.append( + "payment_schedule", + dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30), + ) + si.append( + "payment_schedule", + dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50), + ) + si.append( + "payment_schedule", + dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20), + ) + si = si.save() + if not do_not_submit: + si = si.submit() + return si + + def create_payment_entry(self, docname): + pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40) + pe.paid_from = self.debit_to + pe.insert() + pe.submit() + + def create_credit_note(self, docname): + credit_note = create_sales_invoice( + company=self.company, + customer=self.customer, + item=self.item, + qty=-1, + debit_to=self.debit_to, + cost_center=self.cost_center, + is_return=1, + return_against=docname, + ) + + return credit_note + def test_accounts_receivable(self): filters = { - "company": "_Test Company 2", + "company": self.company, "based_on_payment_terms": 1, "report_date": today(), "range1": 30, @@ -61,7 +110,9 @@ class TestAccountsReceivable(FrappeTestCase): } # check invoice grand total and invoiced column's value for 3 payment terms - name = make_sales_invoice().name + si = self.create_sales_invoice() + name = si.name + report = execute(filters) expected_data = [[100, 30], [100, 50], [100, 20]] @@ -71,7 +122,7 @@ class TestAccountsReceivable(FrappeTestCase): self.assertEqual(expected_data[i - 1], [row.invoice_grand_total, row.invoiced]) # check invoice grand total, invoiced, paid and outstanding column's value after payment - make_payment(name) + self.create_payment_entry(si.name) report = execute(filters) expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]] @@ -84,10 +135,10 @@ class TestAccountsReceivable(FrappeTestCase): ) # check invoice grand total, invoiced, paid and outstanding column's value after credit note - make_credit_note(name) + self.create_credit_note(si.name) report = execute(filters) - expected_data_after_credit_note = [100, 0, 0, 40, -40, "Debtors - _TC2"] + expected_data_after_credit_note = [100, 0, 0, 40, -40, self.debit_to] row = report[1][0] self.assertEqual( @@ -108,21 +159,20 @@ class TestAccountsReceivable(FrappeTestCase): """ so = make_sales_order( - company="_Test Company 2", - customer="_Test Customer 2", - warehouse="Finished Goods - _TC2", - currency="EUR", - debit_to="Debtors - _TC2", - income_account="Sales - _TC2", - expense_account="Cost of Goods Sold - _TC2", - cost_center="Main - _TC2", + company=self.company, + customer=self.customer, + warehouse=self.warehouse, + debit_to=self.debit_to, + income_account=self.income_account, + expense_account=self.expense_account, + cost_center=self.cost_center, ) pe = get_payment_entry(so.doctype, so.name) pe = pe.save().submit() filters = { - "company": "_Test Company 2", + "company": self.company, "based_on_payment_terms": 0, "report_date": today(), "range1": 30, @@ -147,34 +197,32 @@ class TestAccountsReceivable(FrappeTestCase): ) @change_settings( - "Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1} + "Accounts Settings", + {"allow_multi_currency_invoices_against_single_party_account": 1, "allow_stale": 0}, ) def test_exchange_revaluation_for_party(self): """ - Exchange Revaluation for party on Receivable/Payable shoule be included + Exchange Revaluation for party on Receivable/Payable should be included """ - company = "_Test Company 2" - customer = "_Test Customer 2" - # Using Exchange Gain/Loss account for unrealized as well. - company_doc = frappe.get_doc("Company", company) + company_doc = frappe.get_doc("Company", self.company) company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account company_doc.save() - si = make_sales_invoice(no_payment_schedule=True, do_not_submit=True) + si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) si.currency = "USD" - si.conversion_rate = 0.90 + si.conversion_rate = 80 si.debit_to = self.debtors_usd si = si.save().submit() # Exchange Revaluation err = frappe.new_doc("Exchange Rate Revaluation") - err.company = company + err.company = self.company err.posting_date = today() accounts = err.get_accounts_data() err.extend("accounts", accounts) - err.accounts[0].new_exchange_rate = 0.95 + err.accounts[0].new_exchange_rate = 85 row = err.accounts[0] row.new_balance_in_base_currency = flt( row.new_exchange_rate * flt(row.balance_in_account_currency) @@ -189,7 +237,7 @@ class TestAccountsReceivable(FrappeTestCase): je = je.submit() filters = { - "company": company, + "company": self.company, "report_date": today(), "range1": 30, "range2": 60, @@ -198,7 +246,7 @@ class TestAccountsReceivable(FrappeTestCase): } report = execute(filters) - expected_data_for_err = [0, -5, 0, 5] + expected_data_for_err = [0, -500, 0, 500] row = [x for x in report[1] if x.voucher_type == je.doctype and x.voucher_no == je.name][0] self.assertEqual( expected_data_for_err, @@ -214,46 +262,43 @@ class TestAccountsReceivable(FrappeTestCase): """ Payment against credit/debit note should be considered against the parent invoice """ - company = "_Test Company 2" - customer = "_Test Customer 2" - si1 = make_sales_invoice() + si1 = self.create_sales_invoice() - pe = get_payment_entry("Sales Invoice", si1.name, bank_account="Cash - _TC2") - pe.paid_from = "Debtors - _TC2" + pe = get_payment_entry(si1.doctype, si1.name, bank_account=self.cash) + pe.paid_from = self.debit_to pe.insert() pe.submit() - cr_note = make_credit_note(si1.name) + cr_note = self.create_credit_note(si1.name) - si2 = make_sales_invoice() + si2 = self.create_sales_invoice() # manually link cr_note with si2 using journal entry je = frappe.new_doc("Journal Entry") - je.company = company + je.company = self.company je.voucher_type = "Credit Note" je.posting_date = today() - debit_account = "Debtors - _TC2" debit_entry = { - "account": debit_account, + "account": self.debit_to, "party_type": "Customer", - "party": customer, + "party": self.customer, "debit": 100, "debit_in_account_currency": 100, "reference_type": cr_note.doctype, "reference_name": cr_note.name, - "cost_center": "Main - _TC2", + "cost_center": self.cost_center, } credit_entry = { - "account": debit_account, + "account": self.debit_to, "party_type": "Customer", - "party": customer, + "party": self.customer, "credit": 100, "credit_in_account_currency": 100, "reference_type": si2.doctype, "reference_name": si2.name, - "cost_center": "Main - _TC2", + "cost_center": self.cost_center, } je.append("accounts", debit_entry) @@ -261,7 +306,7 @@ class TestAccountsReceivable(FrappeTestCase): je = je.save().submit() filters = { - "company": company, + "company": self.company, "report_date": today(), "range1": 30, "range2": 60, @@ -270,65 +315,3 @@ class TestAccountsReceivable(FrappeTestCase): } report = execute(filters) self.assertEqual(report[1], []) - - -def make_sales_invoice(no_payment_schedule=False, do_not_submit=False): - frappe.set_user("Administrator") - - si = create_sales_invoice( - company="_Test Company 2", - customer="_Test Customer 2", - currency="EUR", - warehouse="Finished Goods - _TC2", - debit_to="Debtors - _TC2", - income_account="Sales - _TC2", - expense_account="Cost of Goods Sold - _TC2", - cost_center="Main - _TC2", - do_not_save=1, - ) - - if not no_payment_schedule: - si.append( - "payment_schedule", - dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30), - ) - si.append( - "payment_schedule", - dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50), - ) - si.append( - "payment_schedule", - dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20), - ) - - si = si.save() - - if not do_not_submit: - si = si.submit() - - return si - - -def make_payment(docname): - pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40) - pe.paid_from = "Debtors - _TC2" - pe.insert() - pe.submit() - - -def make_credit_note(docname): - credit_note = create_sales_invoice( - company="_Test Company 2", - customer="_Test Customer 2", - currency="EUR", - qty=-1, - warehouse="Finished Goods - _TC2", - debit_to="Debtors - _TC2", - income_account="Sales - _TC2", - expense_account="Cost of Goods Sold - _TC2", - cost_center="Main - _TC2", - is_return=1, - return_against=docname, - ) - - return credit_note diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py index debfffdcbb..bf01362c97 100644 --- a/erpnext/accounts/test/accounts_mixin.py +++ b/erpnext/accounts/test/accounts_mixin.py @@ -60,7 +60,6 @@ class AccountsTestMixin: self.income_account = "Sales - " + abbr self.expense_account = "Cost of Goods Sold - " + abbr self.debit_to = "Debtors - " + abbr - self.debit_usd = "Debtors USD - " + abbr self.cash = "Cash - " + abbr self.creditors = "Creditors - " + abbr self.retained_earnings = "Retained Earnings - " + abbr @@ -105,6 +104,28 @@ class AccountsTestMixin: new_acc.save() setattr(self, acc.attribute_name, new_acc.name) + def create_usd_receivable_account(self): + account_name = "Debtors USD" + if not frappe.db.get_value( + "Account", filters={"account_name": account_name, "company": self.company} + ): + acc = frappe.new_doc("Account") + acc.account_name = account_name + acc.parent_account = "Accounts Receivable - " + self.company_abbr + acc.company = self.company + acc.account_currency = "USD" + acc.account_type = "Receivable" + acc.insert() + else: + name = frappe.db.get_value( + "Account", + filters={"account_name": account_name, "company": self.company}, + fieldname="name", + pluck=True, + ) + acc = frappe.get_doc("Account", name) + self.debtors_usd = acc.name + def clear_old_entries(self): doctype_list = [ "GL Entry", @@ -113,6 +134,8 @@ class AccountsTestMixin: "Purchase Invoice", "Payment Entry", "Journal Entry", + "Sales Order", + "Exchange Rate Revaluation", ] for doctype in doctype_list: qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()