From ff09b412f4bba0526585d1501a9a41d53ab50fc2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 11 Sep 2019 12:43:27 +0530 Subject: [PATCH] feat: Allowed multiple payment requests against a reference document (#18988) --- .../payment_request/payment_request.py | 51 +++++++++++++++---- .../payment_request/test_payment_request.py | 26 ++++++++-- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 73758bea85..eda59abf04 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -20,7 +20,7 @@ class PaymentRequest(Document): if self.get("__islocal"): self.status = 'Draft' self.validate_reference_document() - self.validate_payment_request() + self.validate_payment_request_amount() self.validate_currency() self.validate_subscription_details() @@ -28,10 +28,19 @@ class PaymentRequest(Document): if not self.reference_doctype or not self.reference_name: frappe.throw(_("To create a Payment Request reference document is required")) - def validate_payment_request(self): - if frappe.db.get_value("Payment Request", {"reference_name": self.reference_name, - "name": ("!=", self.name), "status": ("not in", ["Initiated", "Paid"]), "docstatus": 1}, "name"): - frappe.throw(_("Payment Request already exists {0}".format(self.reference_name))) + def validate_payment_request_amount(self): + existing_payment_request_amount = \ + get_existing_payment_request_amount(self.reference_doctype, self.reference_name) + + if existing_payment_request_amount: + ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) + if (hasattr(ref_doc, "order_type") \ + and getattr(ref_doc, "order_type") != "Shopping Cart"): + ref_amount = get_amount(ref_doc) + + if existing_payment_request_amount + flt(self.grand_total)> ref_amount: + frappe.throw(_("Total Payment Request amount cannot be greater than {0} amount" + .format(self.reference_doctype))) def validate_currency(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) @@ -271,7 +280,7 @@ def make_payment_request(**args): args = frappe._dict(args) ref_doc = frappe.get_doc(args.dt, args.dn) - grand_total = get_amount(ref_doc, args.dt) + grand_total = get_amount(ref_doc) if args.loyalty_points and args.dt == "Sales Order": from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points)) @@ -281,17 +290,25 @@ def make_payment_request(**args): gateway_account = get_gateway_details(args) or frappe._dict() - existing_payment_request = frappe.db.get_value("Payment Request", - {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ["!=", 2]}) - bank_account = (get_party_bank_account(args.get('party_type'), args.get('party')) if args.get('party_type') else '') + existing_payment_request = None + if args.order_type == "Shopping Cart": + existing_payment_request = frappe.db.get_value("Payment Request", + {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)}) + if existing_payment_request: frappe.db.set_value("Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False) pr = frappe.get_doc("Payment Request", existing_payment_request) - else: + if args.order_type != "Shopping Cart": + existing_payment_request_amount = \ + get_existing_payment_request_amount(args.dt, args.dn) + + if existing_payment_request_amount: + grand_total -= existing_payment_request_amount + pr = frappe.new_doc("Payment Request") pr.update({ "payment_gateway_account": gateway_account.get("name"), @@ -327,8 +344,9 @@ def make_payment_request(**args): return pr.as_dict() -def get_amount(ref_doc, dt): +def get_amount(ref_doc): """get amount based on doctype""" + dt = ref_doc.doctype if dt in ["Sales Order", "Purchase Order"]: grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid) @@ -347,6 +365,17 @@ def get_amount(ref_doc, dt): else: frappe.throw(_("Payment Entry is already created")) +def get_existing_payment_request_amount(ref_dt, ref_dn): + existing_payment_request_amount = frappe.db.sql(""" + select sum(grand_total) + from `tabPayment Request` + where + reference_doctype = %s + and reference_name = %s + and docstatus = 1 + """, (ref_dt, ref_dn)) + return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0 + def get_gateway_details(args): """return gateway and payment account of default payment gateway""" if args.get("payment_gateway"): diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index bfd6d54824..188ab0aecf 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -37,12 +37,12 @@ class TestPaymentRequest(unittest.TestCase): def setUp(self): if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"): frappe.get_doc(payment_gateway).insert(ignore_permissions=True) - + for method in payment_method: - if not frappe.db.get_value("Payment Gateway Account", {"payment_gateway": method["payment_gateway"], + if not frappe.db.get_value("Payment Gateway Account", {"payment_gateway": method["payment_gateway"], "currency": method["currency"]}, "name"): frappe.get_doc(method).insert(ignore_permissions=True) - + def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR") pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com") @@ -100,3 +100,23 @@ class TestPaymentRequest(unittest.TestCase): self.assertEqual(expected_gle[gle.account][1], gle.debit) self.assertEqual(expected_gle[gle.account][2], gle.credit) self.assertEqual(expected_gle[gle.account][3], gle.against_voucher) + + def test_multiple_payment_entries_against_sales_order(self): + # Make Sales Order, grand_total = 1000 + so = make_sales_order() + + # Payment Request amount = 200 + pr1 = make_payment_request(dt="Sales Order", dn=so.name, + recipient_id="nabin@erpnext.com", return_doc=1) + pr1.grand_total = 200 + pr1.submit() + + # Make a 2nd Payment Request + pr2 = make_payment_request(dt="Sales Order", dn=so.name, + recipient_id="nabin@erpnext.com", return_doc=1) + + self.assertEqual(pr2.grand_total, 800) + + # Try to make Payment Request more than SO amount, should give validation + pr2.grand_total = 900 + self.assertRaises(frappe.ValidationError, pr2.save) \ No newline at end of file