From 82d03e2617f17fd3cfbbd3f68d3f7e20eb4c2334 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 30 Jun 2025 17:49:56 +0530 Subject: [PATCH] refactor: function to fetch advance payment doctypes (cherry picked from commit 48e8e85617be17d0a6b812194ab3585140033221) # Conflicts: # erpnext/accounts/doctype/journal_entry/journal_entry.py # erpnext/accounts/doctype/payment_entry/payment_entry.py # erpnext/accounts/doctype/payment_request/payment_request.py # erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py # erpnext/accounts/utils.py # erpnext/controllers/accounts_controller.py --- .../doctype/journal_entry/journal_entry.py | 5 ++ .../doctype/payment_entry/payment_entry.py | 24 ++++++++++ .../payment_request/payment_request.py | 10 +++- .../unreconcile_payment.py | 5 ++ .../accounts_receivable.py | 7 ++- erpnext/accounts/utils.py | 22 +++++++++ erpnext/controllers/accounts_controller.py | 47 +++++++++++++++++++ 7 files changed, 118 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 58c0f23e05e..3e33ad58f34 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -24,6 +24,7 @@ from erpnext.accounts.party import get_party_account from erpnext.accounts.utils import ( cancel_exchange_gain_loss_journal, get_account_currency, + get_advance_payment_doctypes, get_balance_on, get_stock_accounts, get_stock_and_account_balance, @@ -238,6 +239,10 @@ class JournalEntry(AccountsController): def update_advance_paid(self): advance_paid = frappe._dict() +<<<<<<< HEAD +======= + advance_payment_doctypes = get_advance_payment_doctypes() +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) for d in self.get("accounts"): if d.is_advance: if d.reference_type in frappe.get_hooks("advance_payment_doctypes"): diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5c2dc7747bb..f74fafd6c4f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -46,7 +46,11 @@ from erpnext.accounts.party import ( from erpnext.accounts.utils import ( cancel_exchange_gain_loss_journal, get_account_currency, +<<<<<<< HEAD get_balance_on, +======= + get_advance_payment_doctypes, +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) get_outstanding_invoices, ) from erpnext.controllers.accounts_controller import ( @@ -1028,7 +1032,11 @@ class PaymentEntry(AccountsController): def calculate_base_allocated_amount_for_reference(self, d) -> float: base_allocated_amount = 0 +<<<<<<< HEAD if d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"): +======= + if d.reference_doctype in get_advance_payment_doctypes(): +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) # When referencing Sales/Purchase Order, use the source/target exchange rate depending on payment type. # This is so there are no Exchange Gain/Loss generated for such doctypes @@ -1308,8 +1316,12 @@ class PaymentEntry(AccountsController): if not self.party_account: return +<<<<<<< HEAD advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes") +======= + advance_payment_doctypes = get_advance_payment_doctypes() +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) if self.payment_type == "Receive": against_account = self.paid_to else: @@ -1699,12 +1711,24 @@ class PaymentEntry(AccountsController): return flt(gl_dict.get(field, 0) / (conversion_rate or 1)) def update_advance_paid(self): +<<<<<<< HEAD if self.payment_type in ("Receive", "Pay") and self.party: for d in self.get("references"): if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"): frappe.get_doc( d.reference_doctype, d.reference_name, for_update=True ).set_total_advance_paid() +======= + if self.payment_type not in ("Receive", "Pay") or not self.party: + return + + advance_payment_doctypes = get_advance_payment_doctypes() + for d in self.get("references"): + if d.allocated_amount and d.reference_doctype in advance_payment_doctypes: + frappe.get_lazy_doc( + d.reference_doctype, d.reference_name, for_update=True + ).set_total_advance_paid() +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) def on_recurring(self, reference_doc, auto_repeat_doc): self.reference_no = reference_doc.name diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 8bb120f1ab5..b465fd47867 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -16,7 +16,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import ( ) from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate from erpnext.accounts.party import get_party_account, get_party_bank_account -from erpnext.accounts.utils import get_account_currency, get_currency_precision +from erpnext.accounts.utils import get_account_currency, get_advance_payment_doctypes, get_currency_precision from erpnext.utilities import payment_app_import_guard ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST = [ @@ -473,6 +473,14 @@ class PaymentRequest(Document): return create_stripe_subscription(gateway_controller, data) +<<<<<<< HEAD +======= + def update_reference_advance_payment_status(self): + if self.reference_doctype in get_advance_payment_doctypes(): + ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) + ref_doc.set_advance_payment_status() + +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) def _allocate_payment_request_to_pe_references(self, references): """ Allocate the Payment Request to the Payment Entry references based on\n diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py index 7612294a85c..fec45fe5642 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py @@ -12,6 +12,7 @@ from frappe.utils.data import comma_and from erpnext.accounts.utils import ( cancel_exchange_gain_loss_journal, + get_advance_payment_doctypes, unlink_ref_doc_from_payment_entries, update_voucher_outstanding, ) @@ -84,7 +85,11 @@ class UnreconcilePayment(Document): update_voucher_outstanding( alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party ) +<<<<<<< HEAD if doc.doctype in frappe.get_hooks("advance_payment_doctypes"): +======= + if doc.doctype in get_advance_payment_doctypes(): +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) doc.set_total_advance_paid() frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 5bf6163fc6a..9b5b91277ef 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -15,7 +15,11 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) -from erpnext.accounts.utils import get_currency_precision, get_party_types_from_account_type +from erpnext.accounts.utils import ( + get_advance_payment_doctypes, + get_currency_precision, + get_party_types_from_account_type, +) # This report gives a summary of all Outstanding Invoices considering the following @@ -88,6 +92,7 @@ class ReceivablePayableReport: self.party_details = {} self.invoices = set() self.skip_total_row = 0 + self.advance_payment_doctypes = get_advance_payment_doctypes() if self.filters.get("group_by_party"): self.previous_party = "" diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 786bfd6f8b7..9b3ac7b1ed6 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -629,7 +629,12 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): # Update Advance Paid in SO/PO since they might be getting unlinked update_advance_paid = [] +<<<<<<< HEAD if jv_detail.get("reference_type") in ["Sales Order", "Purchase Order"]: +======= + + if jv_detail.get("reference_type") in get_advance_payment_doctypes(): +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) update_advance_paid.append((jv_detail.reference_type, jv_detail.reference_name)) rev_dr_or_cr = ( @@ -736,7 +741,11 @@ def update_reference_in_payment_entry( existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0] # Update Advance Paid in SO/PO since they are getting unlinked +<<<<<<< HEAD if existing_row.get("reference_doctype") in ["Sales Order", "Purchase Order"]: +======= + if existing_row.get("reference_doctype") in get_advance_payment_doctypes(): +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) update_advance_paid.append((existing_row.reference_doctype, existing_row.reference_name)) if d.allocated_amount <= existing_row.allocated_amount: @@ -2256,6 +2265,19 @@ def get_party_types_from_account_type(account_type): return frappe.db.get_all("Party Type", {"account_type": account_type}, pluck="name") +def get_advance_payment_doctypes(payment_type=None): + """ + Get list of advance payment doctypes based on type. + :param type: Optional, can be "receivable" or "payable". If not provided, returns both. + """ + if payment_type: + return frappe.get_hooks(f"advance_payment_{payment_type}_doctypes") or [] + + return (frappe.get_hooks("advance_payment_receivable_doctypes") or []) + ( + frappe.get_hooks("advance_payment_payable_doctypes") or [] + ) + + def run_ledger_health_checks(): health_monitor_settings = frappe.get_doc("Ledger Health Monitor") if health_monitor_settings.enable_health_monitor: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cf0707d9efa..1941de5841b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -51,6 +51,9 @@ from erpnext.accounts.utils import ( get_fiscal_years, validate_fiscal_year, ) +from erpnext.accounts.utils import ( + get_advance_payment_doctypes as _get_advance_payment_doctypes, +) from erpnext.buying.utils import update_last_purchase_rate from erpnext.controllers.print_settings import ( set_print_templates_for_item_table, @@ -386,9 +389,13 @@ class AccountsController(TransactionBase): adv = qb.DocType("Advance Payment Ledger Entry") qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run() +<<<<<<< HEAD advance_payment_doctypes = frappe.get_hooks("advance_payment_doctypes") if self.doctype in advance_payment_doctypes: +======= + if self.doctype in self.get_advance_payment_doctypes(): +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) qb.from_(adv).delete().where( adv.against_voucher_type.eq(self.doctype) & adv.against_voucher_no.eq(self.name) ).run() @@ -2272,6 +2279,41 @@ class AccountsController(TransactionBase): self.db_set("advance_paid", advance_paid) +<<<<<<< HEAD +======= + self.set_advance_payment_status() + + def set_advance_payment_status(self): + new_status = None + + PaymentRequest = frappe.qb.DocType("Payment Request") + paid_amount = frappe.get_value( + doctype="Payment Request", + filters={ + "reference_doctype": self.doctype, + "reference_name": self.name, + "docstatus": 1, + }, + fieldname=Sum(PaymentRequest.grand_total - PaymentRequest.outstanding_amount), + ) + + if not paid_amount: + if self.doctype in self.get_advance_payment_doctypes(payment_type="receivable"): + new_status = "Not Requested" if paid_amount is None else "Requested" + elif self.doctype in self.get_advance_payment_doctypes(payment_type="payable"): + new_status = "Not Initiated" if paid_amount is None else "Initiated" + else: + total_amount = self.get("rounded_total") or self.get("grand_total") + new_status = "Fully Paid" if paid_amount == total_amount else "Partially Paid" + + if new_status == self.advance_payment_status: + return + + self.db_set("advance_payment_status", new_status, update_modified=False) + self.set_status(update=True) + self.notify_update() + +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) @property def company_abbr(self): if not hasattr(self, "_abbr"): @@ -2911,8 +2953,13 @@ class AccountsController(TransactionBase): repost_ledger.insert() repost_ledger.submit() +<<<<<<< HEAD def get_advance_payment_doctypes(self) -> list: return frappe.get_hooks("advance_payment_doctypes") +======= + def get_advance_payment_doctypes(self, payment_type=None) -> list: + return _get_advance_payment_doctypes(payment_type=payment_type) +>>>>>>> 48e8e85617 (refactor: function to fetch advance payment doctypes) def make_advance_payment_ledger_for_journal(self): advance_payment_doctypes = self.get_advance_payment_doctypes()