Skip to content

Commit

Permalink
Merge pull request frappe#40643 from blaggacao/fix/adv-pay-status
Browse files Browse the repository at this point in the history
fix(pr): set adv. pay. status base on PR stati
  • Loading branch information
ruthra-kumar authored Apr 3, 2024
2 parents c3b2ed5 + 46e12e0 commit 7d22df2
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 52 deletions.
1 change: 1 addition & 0 deletions erpnext/accounts/doctype/payment_entry/payment_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def on_submit(self):
self.update_outstanding_amounts()
self.update_advance_paid()
self.update_payment_schedule()
self.set_payment_req_status()
self.set_status()

def set_liability_account(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

# test_records = frappe.get_test_records('Payment Gateway Account')

test_ignore = ["Payment Gateway"]


class TestPaymentGatewayAccount(unittest.TestCase):
pass
50 changes: 22 additions & 28 deletions erpnext/accounts/doctype/payment_request/payment_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,35 +149,37 @@ def validate_subscription_details(self):
).format(self.grand_total, amount)
)

def on_change(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
"advance_payment_payable_doctypes"
)
if self.reference_doctype in advance_payment_doctypes:
# set advance payment status
ref_doc.set_advance_payment_status()

def on_submit(self):
if self.payment_request_type == "Outward":
self.db_set("status", "Initiated")
return
elif self.payment_request_type == "Inward":
self.db_set("status", "Requested")

send_mail = self.payment_gateway_validation() if self.payment_gateway else None
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if self.payment_request_type == "Inward":
send_mail = self.payment_gateway_validation() if self.payment_gateway else None
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)

if (
hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart"
) or self.flags.mute_email:
send_mail = False
if (
hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart"
) or self.flags.mute_email:
send_mail = False

if send_mail and self.payment_channel != "Phone":
self.set_payment_request_url()
self.send_email()
self.make_communication_entry()
if send_mail and self.payment_channel != "Phone":
self.set_payment_request_url()
self.send_email()
self.make_communication_entry()

elif self.payment_channel == "Phone":
self.request_phone_payment()

advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
"advance_payment_payable_doctypes"
)
if self.reference_doctype in advance_payment_doctypes:
# set advance payment status
ref_doc.set_total_advance_paid()
elif self.payment_channel == "Phone":
self.request_phone_payment()

def request_phone_payment(self):
controller = _get_payment_gateway_controller(self.payment_gateway)
Expand Down Expand Up @@ -217,14 +219,6 @@ def on_cancel(self):
self.check_if_payment_entry_exists()
self.set_as_cancelled()

ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
"advance_payment_payable_doctypes"
)
if self.reference_doctype in advance_payment_doctypes:
# set advance payment status
ref_doc.set_total_advance_paid()

def make_invoice(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart":
Expand Down
9 changes: 8 additions & 1 deletion erpnext/buying/doctype/purchase_order/test_purchase_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,10 +1127,17 @@ def test_purchase_order_advance_payment_status(self):
po = create_purchase_order()
self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated")

pr = make_payment_request(dt=po.doctype, dn=po.name, submit_doc=True, return_doc=True)
pr = make_payment_request(
dt=po.doctype, dn=po.name, submit_doc=True, return_doc=True, payment_request_type="Outward"
)

po.reload()
self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated")

pe = get_payment_entry(po.doctype, po.name).save().submit()

pr.reload()
self.assertEqual(pr.status, "Paid")
self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Fully Paid")

pe.reload()
Expand Down
49 changes: 30 additions & 19 deletions erpnext/controllers/accounts_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -1924,32 +1924,43 @@ def set_total_advance_paid(self):

self.db_set("advance_paid", advance_paid)

self.set_advance_payment_status(advance_paid, order_total)
self.set_advance_payment_status()

def set_advance_payment_status(self, advance_paid: float | None = None, order_total: float | None = None):
def set_advance_payment_status(self):
new_status = None
# if money is paid set the paid states
if advance_paid:
new_status = "Partially Paid" if advance_paid < order_total else "Fully Paid"

if not new_status:
prs = frappe.db.count(
"Payment Request",
{
"reference_doctype": self.doctype,
"reference_name": self.name,
"docstatus": 1,
},
)
if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"):
new_status = "Requested" if prs else "Not Requested"
if self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"):
new_status = "Initiated" if prs else "Not Initiated"
stati = frappe.get_list(
"Payment Request",
{
"reference_doctype": self.doctype,
"reference_name": self.name,
"docstatus": 1,
},
pluck="status",
)
if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"):
if not stati:
new_status = "Not Requested"
elif "Requested" in stati or "Failed" in stati:
new_status = "Requested"
elif "Partially Paid" in stati:
new_status = "Partially Paid"
elif "Paid" in stati:
new_status = "Fully Paid"
if self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"):
if not stati:
new_status = "Not Initiated"
elif "Initiated" in stati or "Failed" in stati or "Payment Ordered" in stati:
new_status = "Initiated"
elif "Partially Paid" in stati:
new_status = "Partially Paid"
elif "Paid" in stati:
new_status = "Fully Paid"

if new_status == self.advance_payment_status:
return

self.db_set("advance_payment_status", new_status)
self.db_set("advance_payment_status", new_status, update_modified=False)
self.set_status(update=True)
self.notify_update()

Expand Down
1 change: 0 additions & 1 deletion erpnext/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,6 @@
"Payment Entry": {
"on_submit": [
"erpnext.regional.create_transaction_log",
"erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status",
"erpnext.accounts.doctype.dunning.dunning.resolve_dunning",
],
"on_cancel": ["erpnext.accounts.doctype.dunning.dunning.resolve_dunning"],
Expand Down
49 changes: 46 additions & 3 deletions erpnext/selling/doctype/sales_order/test_sales_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt

import json
from unittest.mock import patch

import frappe
import frappe.permissions
Expand Down Expand Up @@ -1956,10 +1957,48 @@ def test_expired_rate_for_packed_item(self):
self.assertEqual(so.items[0].rate, scenario.get("expected_rate"))
self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate"))

def test_sales_order_advance_payment_status(self):
@patch(
# this also shadows one (1) call to _get_payment_gateway_controller
"erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url",
return_value=None,
)
def test_sales_order_advance_payment_status(self, mocked_get_payment_url):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request

# Flow progressing to SI with payment entries "moved" from SO to SI
so = make_sales_order(qty=1, rate=100, do_not_submit=True)
# no-op; for optical consistency with how a webshop SO would look like
so.order_type = "Shopping Cart"
so.submit()
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested")

pr = make_payment_request(
dt=so.doctype, dn=so.name, order_type="Shopping Cart", submit_doc=True, return_doc=True
)
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested")

pe = pr.set_as_paid()
pr.reload() # status updated
pe.reload() # references moved to Sales Invoice
self.assertEqual(pr.status, "Paid")
self.assertEqual(pe.references[0].reference_doctype, "Sales Invoice")
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Fully Paid")

pe.cancel()
pr.reload()
self.assertEqual(pr.status, "Paid") # TODO: this might be a bug
so.reload() # reload
# regardless, since the references have already "handed-over" to SI,
# the SO keeps its historical state at the time of hand over
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Fully Paid")

pr.cancel()
self.assertEqual(
frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
) # TODO: this might be a bug; handover has happened

# Flow NOT progressing to SI with payment entries NOT "moved"
so = make_sales_order(qty=1, rate=100)
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested")

Expand All @@ -1971,11 +2010,15 @@ def test_sales_order_advance_payment_status(self):

pe.reload()
pe.cancel()
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested")
self.assertEqual(
frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested"
) # here: reset

pr.reload()
pr.cancel()
self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested")
self.assertEqual(
frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
) # here: reset

def test_pick_list_without_rejected_materials(self):
serial_and_batch_item = make_item(
Expand Down

0 comments on commit 7d22df2

Please sign in to comment.