Merge pull request #26900 from deepeshgarg007/payment_entry_validations_and_trigger_develop
fix: Multiple fixes in payment entry
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 439b1ed..d96bc27 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -533,8 +533,8 @@
source_exchange_rate: function(frm) {
if (frm.doc.paid_amount) {
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
- if(!frm.set_paid_amount_based_on_received_amount &&
- (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
+ // target exchange rate should always be same as source if both account currencies is same
+ if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
}
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index d2dffde..abacee9 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -55,14 +55,17 @@
self.validate_mandatory()
self.validate_reference_documents()
self.set_tax_withholding()
- self.apply_taxes()
self.set_amounts()
+ self.validate_amounts()
+ self.apply_taxes()
+ self.set_amounts_after_tax()
self.clear_unallocated_reference_document_rows()
self.validate_payment_against_negative_invoice()
self.validate_transaction_reference()
self.set_title()
self.set_remarks()
self.validate_duplicate_entry()
+ self.validate_payment_type_with_outstanding()
self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
@@ -118,6 +121,11 @@
if not self.get(field):
self.set(field, bank_data.account)
+ def validate_payment_type_with_outstanding(self):
+ total_outstanding = sum(d.allocated_amount for d in self.get('references'))
+ if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive':
+ frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type"))
+
def validate_allocated_amount(self):
for d in self.get("references"):
if (flt(d.allocated_amount))> 0:
@@ -236,7 +244,9 @@
self.company_currency, self.posting_date)
def set_target_exchange_rate(self, ref_doc=None):
- if self.paid_to and not self.target_exchange_rate:
+ if self.paid_from_account_currency == self.paid_to_account_currency:
+ self.target_exchange_rate = self.source_exchange_rate
+ elif self.paid_to and not self.target_exchange_rate:
if ref_doc:
if self.paid_to_account_currency == ref_doc.currency:
self.target_exchange_rate = ref_doc.get("exchange_rate")
@@ -468,13 +478,22 @@
def set_amounts(self):
self.set_received_amount()
self.set_amounts_in_company_currency()
- self.set_amounts_after_tax()
self.set_total_allocated_amount()
self.set_unallocated_amount()
self.set_difference_amount()
+ def validate_amounts(self):
+ self.validate_received_amount()
+
+ def validate_received_amount(self):
+ if self.paid_from_account_currency == self.paid_to_account_currency:
+ if self.paid_amount != self.received_amount:
+ frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
+
def set_received_amount(self):
self.base_received_amount = self.base_paid_amount
+ if self.paid_from_account_currency == self.paid_to_account_currency:
+ self.received_amount = self.paid_amount
def set_amounts_after_tax(self):
applicable_tax = 0
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 801dadc..dac927b 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -107,7 +107,7 @@
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
- pe.target_exchange_rate = 50
+ pe.source_exchange_rate = 50
pe.insert()
pe.submit()
@@ -154,7 +154,7 @@
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
- pe.target_exchange_rate = 50
+ pe.source_exchange_rate = 50
pe.insert()
pe.submit()
@@ -491,7 +491,7 @@
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
- pe.target_exchange_rate = 55
+ pe.source_exchange_rate = 55
pe.append("deductions", {
"account": "_Test Exchange Gain/Loss - _TC",
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 01a6b02..c3d83c7 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -26,6 +26,7 @@
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
from erpnext.stock.utils import get_incoming_rate
+from erpnext.accounts.utils import PaymentEntryUnlinkError
class TestSalesInvoice(unittest.TestCase):
def make(self):
@@ -136,7 +137,7 @@
pe.paid_to_account_currency = si.currency
pe.source_exchange_rate = 1
pe.target_exchange_rate = 1
- pe.paid_amount = si.grand_total
+ pe.paid_amount = si.outstanding_amount
pe.insert()
pe.submit()
@@ -145,6 +146,42 @@
self.assertRaises(frappe.LinkExistsError, si.cancel)
unlink_payment_on_cancel_of_invoice()
+ def test_payment_entry_unlink_against_standalone_credit_note(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+ si1 = create_sales_invoice(rate=1000)
+ si2 = create_sales_invoice(rate=300)
+ si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
+
+
+ pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC")
+ pe.append('references', {
+ 'reference_doctype': 'Sales Invoice',
+ 'reference_name': si2.name,
+ 'total_amount': si2.grand_total,
+ 'outstanding_amount': si2.outstanding_amount,
+ 'allocated_amount': si2.outstanding_amount
+ })
+
+ pe.append('references', {
+ 'reference_doctype': 'Sales Invoice',
+ 'reference_name': si3.name,
+ 'total_amount': si3.grand_total,
+ 'outstanding_amount': si3.outstanding_amount,
+ 'allocated_amount': si3.outstanding_amount
+ })
+
+ pe.reference_no = 'Test001'
+ pe.reference_date = nowdate()
+ pe.save()
+ pe.submit()
+
+ si2.load_from_db()
+ si2.cancel()
+
+ si1.load_from_db()
+ self.assertRaises(PaymentEntryUnlinkError, si1.cancel)
+
+
def test_sales_invoice_calculation_export_currency(self):
si = frappe.copy_doc(test_records[2])
si.currency = "USD"
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index eca1e26..118f628 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -19,6 +19,7 @@
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
class FiscalYearError(frappe.ValidationError): pass
+class PaymentEntryUnlinkError(frappe.ValidationError): pass
@frappe.whitelist()
def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
@@ -555,10 +556,16 @@
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
for pe in linked_pe:
- pe_doc = frappe.get_doc("Payment Entry", pe)
- pe_doc.set_total_allocated_amount()
- pe_doc.set_unallocated_amount()
- pe_doc.clear_unallocated_reference_document_rows()
+ try:
+ pe_doc = frappe.get_doc("Payment Entry", pe)
+ pe_doc.set_amounts()
+ pe_doc.clear_unallocated_reference_document_rows()
+ pe_doc.validate_payment_type_with_outstanding()
+ except Exception as e:
+ msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name)
+ msg += '<br>'
+ msg += _("Please cancel payment entry manually first")
+ frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error"))
frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s