From 2f4d8e1e94345c013c85040f8d657b63f60f3f7c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 20 Jul 2023 09:08:55 +0530 Subject: [PATCH 1/5] test: overallocation validation in payment entry (cherry picked from commit e7e3853f819d6e50692e779da9ca90a0038c5564) --- .../payment_entry/test_payment_entry.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index ae2625b6539..dfae979ccd9 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1061,6 +1061,26 @@ class TestPaymentEntry(FrappeTestCase): } self.assertDictEqual(ref_details, expected_response) + def test_overallocation_validation_on_payment_terms(self): + si = create_sales_invoice(do_not_save=1, qty=1, rate=200) + create_payment_terms_template() + si.payment_terms_template = "Test Receivable Template" + si.save().submit() + + si.reload() + si.payment_schedule[0].payment_amount + + pe = get_payment_entry(si.doctype, si.name).save() + # Allocated amount should be according to the payment schedule + for idx, schedule in enumerate(si.payment_schedule): + with self.subTest(idx=idx): + self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + pe.paid_amount = 400 + pe.references[0].allocated_amount = 200 + pe.references[1].allocated_amount = 200 + + self.assertRaises(frappe.ValidationError, pe.save) + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") From f3295a9f594515f055181601835d579f5e27932d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 20 Jul 2023 21:19:29 +0530 Subject: [PATCH 2/5] chore: test more scenarios (cherry picked from commit 6b4a81ee482d4a2c6ce32ab69a3b42cfd5496b60) --- .../payment_entry/test_payment_entry.py | 85 +++++++++++++++++-- 1 file changed, 77 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index dfae979ccd9..19a4af62ced 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1061,25 +1061,94 @@ class TestPaymentEntry(FrappeTestCase): } self.assertDictEqual(ref_details, expected_response) + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) def test_overallocation_validation_on_payment_terms(self): - si = create_sales_invoice(do_not_save=1, qty=1, rate=200) + """ + Validate Allocation on Payment Entry based on Payment Schedule. Upon overallocation, validation error must be thrown. + + """ create_payment_terms_template() - si.payment_terms_template = "Test Receivable Template" - si.save().submit() - si.reload() - si.payment_schedule[0].payment_amount + # Validate allocation on base/company currency + si1 = create_sales_invoice(do_not_save=1, qty=1, rate=200) + si1.payment_terms_template = "Test Receivable Template" + si1.save().submit() - pe = get_payment_entry(si.doctype, si.name).save() + si1.reload() + pe = get_payment_entry(si1.doctype, si1.name).save() # Allocated amount should be according to the payment schedule - for idx, schedule in enumerate(si.payment_schedule): + for idx, schedule in enumerate(si1.payment_schedule): with self.subTest(idx=idx): self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + pe.save() + + # Overallocation validation should trigger pe.paid_amount = 400 pe.references[0].allocated_amount = 200 pe.references[1].allocated_amount = 200 - self.assertRaises(frappe.ValidationError, pe.save) + pe.delete() + si1.cancel() + si1.delete() + + # Validate allocation on foreign currency + si2 = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=80, + do_not_save=1, + ) + si2.payment_terms_template = "Test Receivable Template" + si2.save().submit() + + si2.reload() + pe = get_payment_entry(si2.doctype, si2.name).save() + # Allocated amount should be according to the payment schedule + for idx, schedule in enumerate(si2.payment_schedule): + with self.subTest(idx=idx): + self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + pe.save() + + # Overallocation validation should trigger + pe.paid_amount = 200 + pe.references[0].allocated_amount = 100 + pe.references[1].allocated_amount = 100 + self.assertRaises(frappe.ValidationError, pe.save) + pe.delete() + si2.cancel() + si2.delete() + + # Validate allocation in base/company currency on a foreign currency document + # when invoice is made is foreign currency, but posted to base/company currency account + si3 = create_sales_invoice( + customer="_Test Customer USD", + currency="USD", + conversion_rate=80, + do_not_save=1, + ) + si3.payment_terms_template = "Test Receivable Template" + si3.save().submit() + + si3.reload() + pe = get_payment_entry(si3.doctype, si3.name).save() + # Allocated amount should be according to the payment schedule + for idx, schedule in enumerate(si3.payment_schedule): + with self.subTest(idx=idx): + self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + pe.save() + + # Overallocation validation should trigger + pe.paid_amount = 400 + pe.references[0].allocated_amount = 200 + pe.references[1].allocated_amount = 200 + self.assertRaises(frappe.ValidationError, pe.save) + # pe.delete() + # si3.cancel() + # si3.delete() def create_payment_entry(**args): From 602f0769c4a39c56e0fc0d54203be12237e5e329 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 22 Jul 2023 08:15:03 +0530 Subject: [PATCH 3/5] chore: use flt for currency (cherry picked from commit 5b37919574ac6b3454c99faffe1b42ceaca0f74e) --- .../payment_entry/test_payment_entry.py | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 19a4af62ced..38dadbe5eb7 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1070,6 +1070,7 @@ class TestPaymentEntry(FrappeTestCase): Validate Allocation on Payment Entry based on Payment Schedule. Upon overallocation, validation error must be thrown. """ + customer = create_customer() create_payment_terms_template() # Validate allocation on base/company currency @@ -1082,7 +1083,7 @@ class TestPaymentEntry(FrappeTestCase): # Allocated amount should be according to the payment schedule for idx, schedule in enumerate(si1.payment_schedule): with self.subTest(idx=idx): - self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + self.assertEqual(flt(schedule.payment_amount), flt(pe.references[idx].allocated_amount)) pe.save() # Overallocation validation should trigger @@ -1110,7 +1111,7 @@ class TestPaymentEntry(FrappeTestCase): # Allocated amount should be according to the payment schedule for idx, schedule in enumerate(si2.payment_schedule): with self.subTest(idx=idx): - self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + self.assertEqual(flt(schedule.payment_amount), flt(pe.references[idx].allocated_amount)) pe.save() # Overallocation validation should trigger @@ -1123,9 +1124,9 @@ class TestPaymentEntry(FrappeTestCase): si2.delete() # Validate allocation in base/company currency on a foreign currency document - # when invoice is made is foreign currency, but posted to base/company currency account + # when invoice is made is foreign currency, but posted to base/company currency debtors account si3 = create_sales_invoice( - customer="_Test Customer USD", + customer=customer, currency="USD", conversion_rate=80, do_not_save=1, @@ -1138,17 +1139,17 @@ class TestPaymentEntry(FrappeTestCase): # Allocated amount should be according to the payment schedule for idx, schedule in enumerate(si3.payment_schedule): with self.subTest(idx=idx): - self.assertEqual(schedule.payment_amount, pe.references[idx].allocated_amount) + self.assertEqual(flt(schedule.base_payment_amount), flt(pe.references[idx].allocated_amount)) pe.save() # Overallocation validation should trigger - pe.paid_amount = 400 - pe.references[0].allocated_amount = 200 - pe.references[1].allocated_amount = 200 + pe.paid_amount = 16000 + pe.references[0].allocated_amount = 8000 + pe.references[1].allocated_amount = 8000 self.assertRaises(frappe.ValidationError, pe.save) - # pe.delete() - # si3.cancel() - # si3.delete() + pe.delete() + si3.cancel() + si3.delete() def create_payment_entry(**args): @@ -1239,3 +1240,17 @@ def create_payment_terms_template_with_discount( def create_payment_term(name): if not frappe.db.exists("Payment Term", name): frappe.get_doc({"doctype": "Payment Term", "payment_term_name": name}).insert() + + +def create_customer(name="_Test Customer 2 USD", currency="USD"): + customer = None + if frappe.db.exists("Customer", name): + customer = name + else: + customer = frappe.new_doc("Customer") + customer.customer_name = name + customer.default_currency = currency + customer.type = "Individual" + customer.save() + customer = customer.name + return customer From bc7ab2f787d89077e000baf59e6f6b15869ba2b7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 22 Jul 2023 09:16:03 +0530 Subject: [PATCH 4/5] chore: validation on multi-currency tran on company curtency account (cherry picked from commit 8f9ef4ef5b411d4debe460b607ccb78c6490462a) --- .../accounts/doctype/payment_entry/test_payment_entry.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 38dadbe5eb7..bb21e206042 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1136,10 +1136,11 @@ class TestPaymentEntry(FrappeTestCase): si3.reload() pe = get_payment_entry(si3.doctype, si3.name).save() - # Allocated amount should be according to the payment schedule - for idx, schedule in enumerate(si3.payment_schedule): + # Allocated amount should be equal to payment term outstanding + self.assertEqual(len(pe.references), 2) + for idx, ref in enumerate(pe.references): with self.subTest(idx=idx): - self.assertEqual(flt(schedule.base_payment_amount), flt(pe.references[idx].allocated_amount)) + self.assertEqual(ref.payment_term_outstanding, ref.allocated_amount) pe.save() # Overallocation validation should trigger From 25301881c940ddb3bdbe04c776d92604a5c3692d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 22 Jul 2023 10:01:59 +0530 Subject: [PATCH 5/5] chore(test): enable multi-currency party for testing (cherry picked from commit 93246043ec092268d9b011e024bedf8b008993a1) --- .../accounts/doctype/payment_entry/test_payment_entry.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index bb21e206042..785b8a180b1 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1063,7 +1063,11 @@ class TestPaymentEntry(FrappeTestCase): @change_settings( "Accounts Settings", - {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + { + "unlink_payment_on_cancellation_of_invoice": 1, + "delete_linked_ledger_entries": 1, + "allow_multi_currency_invoices_against_single_party_account": 1, + }, ) def test_overallocation_validation_on_payment_terms(self): """